Fix plugin update bugs from Bugbot review
- Fix SSE error handler not calling onEvent, leaving UI stuck in updating state - Fix plugin name prefix matching to avoid corrupting wrong config entries - Fix EventSource cleanup on component unmount using useRef
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState, useMemo, useEffect } from 'react';
|
import { useState, useMemo, useEffect, useRef } from 'react';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { toast } from '@/components/toast';
|
import { toast } from '@/components/toast';
|
||||||
import { type Plugin, getInstalledPlugins, updatePlugin, type InstalledPluginInfo } from '@/lib/api';
|
import { type Plugin, getInstalledPlugins, updatePlugin, type InstalledPluginInfo } from '@/lib/api';
|
||||||
@@ -585,6 +585,16 @@ function InstalledPluginsSection() {
|
|||||||
|
|
||||||
const [updating, setUpdating] = useState<string | null>(null);
|
const [updating, setUpdating] = useState<string | null>(null);
|
||||||
const [updateProgress, setUpdateProgress] = useState<string | null>(null);
|
const [updateProgress, setUpdateProgress] = useState<string | null>(null);
|
||||||
|
const cleanupRef = useRef<(() => void) | null>(null);
|
||||||
|
|
||||||
|
// Cleanup EventSource on unmount
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (cleanupRef.current) {
|
||||||
|
cleanupRef.current();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleUpdate = (packageName: string) => {
|
const handleUpdate = (packageName: string) => {
|
||||||
setUpdating(packageName);
|
setUpdating(packageName);
|
||||||
@@ -597,16 +607,17 @@ function InstalledPluginsSection() {
|
|||||||
toast.success(event.message);
|
toast.success(event.message);
|
||||||
setUpdating(null);
|
setUpdating(null);
|
||||||
setUpdateProgress(null);
|
setUpdateProgress(null);
|
||||||
|
cleanupRef.current = null;
|
||||||
mutate(); // Refresh the list
|
mutate(); // Refresh the list
|
||||||
} else if (event.event_type === 'error') {
|
} else if (event.event_type === 'error') {
|
||||||
toast.error(event.message);
|
toast.error(event.message);
|
||||||
setUpdating(null);
|
setUpdating(null);
|
||||||
setUpdateProgress(null);
|
setUpdateProgress(null);
|
||||||
|
cleanupRef.current = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Cleanup on unmount
|
cleanupRef.current = cleanup;
|
||||||
return cleanup;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
|
|||||||
@@ -1669,6 +1669,11 @@ export function updatePlugin(
|
|||||||
|
|
||||||
eventSource.onerror = () => {
|
eventSource.onerror = () => {
|
||||||
eventSource.close();
|
eventSource.close();
|
||||||
|
onEvent({
|
||||||
|
event_type: "error",
|
||||||
|
message: "Connection error: failed to connect to server",
|
||||||
|
progress: null,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return () => eventSource.close();
|
return () => eventSource.close();
|
||||||
|
|||||||
@@ -1069,7 +1069,8 @@ fn stream_plugin_update(package: String) -> impl Stream<Item = Result<Event, std
|
|||||||
// Update the plugin spec to @latest
|
// Update the plugin spec to @latest
|
||||||
for p in plugins.iter_mut() {
|
for p in plugins.iter_mut() {
|
||||||
if let Some(s) = p.as_str() {
|
if let Some(s) = p.as_str() {
|
||||||
if s.starts_with(&package) {
|
// Match exact package name or package@version to avoid prefix collisions
|
||||||
|
if s == package || s.starts_with(&format!("{}@", package)) {
|
||||||
*p = serde_json::json!(format!("{}@latest", package));
|
*p = serde_json::json!(format!("{}@latest", package));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user