mirror of
https://github.com/DrizzleTime/Foxel.git
synced 2026-09-06 16:17:06 +08:00
fix: update error handling to avoid unused catch variables and improve code clarity
This commit is contained in:
@@ -15,6 +15,16 @@ export default tseslint.config([
|
|||||||
reactHooks.configs['recommended-latest'],
|
reactHooks.configs['recommended-latest'],
|
||||||
reactRefresh.configs.vite,
|
reactRefresh.configs.vite,
|
||||||
],
|
],
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
'react-refresh/only-export-components': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
allowConstantExport: true,
|
||||||
|
allowExportNames: ['routes', 'useAuth', 'useTheme', 'useAppWindows', 'useI18n'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
ecmaVersion: 2020,
|
ecmaVersion: 2020,
|
||||||
globals: globals.browser,
|
globals: globals.browser,
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ async function request<T = any>(url: string, options: RequestOptions = {}): Prom
|
|||||||
} else {
|
} else {
|
||||||
errMsg = (typeof data?.detail === 'string') ? data.detail : (data.detail ? JSON.stringify(data.detail) : JSON.stringify(data));
|
errMsg = (typeof data?.detail === 'string') ? data.detail : (data.detail ? JSON.stringify(data.detail) : JSON.stringify(data));
|
||||||
}
|
}
|
||||||
} catch (_) { }
|
} catch { void 0; }
|
||||||
throw new Error(errMsg || `Request failed: ${resp.status}`);
|
throw new Error(errMsg || `Request failed: ${resp.status}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -107,7 +107,7 @@ export const vfsApi = {
|
|||||||
const json = JSON.parse(xhr.responseText);
|
const json = JSON.parse(xhr.responseText);
|
||||||
if (json.code === 0) return resolve(json.data);
|
if (json.code === 0) return resolve(json.data);
|
||||||
return reject(new Error(json.msg || json.message || 'Upload failed'));
|
return reject(new Error(json.msg || json.message || 'Upload failed'));
|
||||||
} catch (e) {
|
} catch {
|
||||||
return reject(new Error('Invalid response'));
|
return reject(new Error('Invalid response'));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -115,7 +115,7 @@ export const vfsApi = {
|
|||||||
try {
|
try {
|
||||||
const json = JSON.parse(xhr.responseText);
|
const json = JSON.parse(xhr.responseText);
|
||||||
err = json.detail || json.msg || json.message || err;
|
err = json.detail || json.msg || json.message || err;
|
||||||
} catch (_) {}
|
} catch { void 0; }
|
||||||
reject(new Error(err));
|
reject(new Error(err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,8 +57,8 @@ export const AppWindowsLayer: React.FC<AppWindowsLayerProps> = ({ windows, onClo
|
|||||||
const { id, startX, startY, originX, originY } = dragRef.current;
|
const { id, startX, startY, originX, originY } = dragRef.current;
|
||||||
const dx = e.clientX - startX;
|
const dx = e.clientX - startX;
|
||||||
const dy = e.clientY - startY;
|
const dy = e.clientY - startY;
|
||||||
let newX = Math.max(0, originX + dx);
|
const newX = Math.max(0, originX + dx);
|
||||||
let newY = Math.max(0, originY + dy);
|
const newY = Math.max(0, originY + dy);
|
||||||
dragRef.current.newX = newX;
|
dragRef.current.newX = newX;
|
||||||
dragRef.current.newY = newY;
|
dragRef.current.newY = newY;
|
||||||
const el = windowEls.current[id];
|
const el = windowEls.current[id];
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { useSystemStatus } from '../../contexts/SystemContext';
|
|||||||
|
|
||||||
export const OfficeViewerApp: React.FC<AppComponentProps> = ({ filePath, onRequestClose }) => {
|
export const OfficeViewerApp: React.FC<AppComponentProps> = ({ filePath, onRequestClose }) => {
|
||||||
const systemStatus = useSystemStatus();
|
const systemStatus = useSystemStatus();
|
||||||
|
const fileDomain = systemStatus?.file_domain;
|
||||||
const [url, setUrl] = useState<string>();
|
const [url, setUrl] = useState<string>();
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [err, setErr] = useState<string>();
|
const [err, setErr] = useState<string>();
|
||||||
@@ -19,7 +20,7 @@ export const OfficeViewerApp: React.FC<AppComponentProps> = ({ filePath, onReque
|
|||||||
vfsApi.getTempLinkToken(filePath.replace(/^\/+/, ''))
|
vfsApi.getTempLinkToken(filePath.replace(/^\/+/, ''))
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (cancelled) return;
|
if (cancelled) return;
|
||||||
const baseUrl = systemStatus?.file_domain || window.location.origin;
|
const baseUrl = fileDomain || window.location.origin;
|
||||||
const fullUrl = new URL(res.url, baseUrl).href;
|
const fullUrl = new URL(res.url, baseUrl).href;
|
||||||
const officeUrl = `https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(fullUrl)}`;
|
const officeUrl = `https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(fullUrl)}`;
|
||||||
setUrl(officeUrl);
|
setUrl(officeUrl);
|
||||||
@@ -38,7 +39,7 @@ export const OfficeViewerApp: React.FC<AppComponentProps> = ({ filePath, onReque
|
|||||||
return () => {
|
return () => {
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
};
|
};
|
||||||
}, [filePath]);
|
}, [filePath, fileDomain]);
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export const PluginAppHost: React.FC<PluginAppHostProps> = ({ plugin, filePath,
|
|||||||
if (pluginRef.current?.unmount && containerRef.current) {
|
if (pluginRef.current?.unmount && containerRef.current) {
|
||||||
pluginRef.current.unmount(containerRef.current);
|
pluginRef.current.unmount(containerRef.current);
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch { void 0; }
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ export const PluginAppOpenHost: React.FC<PluginAppOpenHostProps> = ({ plugin, on
|
|||||||
const p = pluginRef.current;
|
const p = pluginRef.current;
|
||||||
if (p?.unmountApp) return p.unmountApp(containerRef.current);
|
if (p?.unmountApp) return p.unmountApp(containerRef.current);
|
||||||
if (p?.unmount) return p.unmount(containerRef.current);
|
if (p?.unmount) return p.unmount(containerRef.current);
|
||||||
} catch { }
|
} catch { void 0; }
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -9,13 +9,14 @@ const MarkdownEditor = React.lazy(() => import('@uiw/react-md-editor'));
|
|||||||
|
|
||||||
const { Header, Content } = Layout;
|
const { Header, Content } = Layout;
|
||||||
|
|
||||||
|
const MAX_PREVIEW_BYTES = 1024 * 1024; // 1MB
|
||||||
|
|
||||||
export const TextEditorApp: React.FC<AppComponentProps> = ({ filePath, entry, onRequestClose }) => {
|
export const TextEditorApp: React.FC<AppComponentProps> = ({ filePath, entry, onRequestClose }) => {
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
const [content, setContent] = useState('');
|
const [content, setContent] = useState('');
|
||||||
const [initialContent, setInitialContent] = useState('');
|
const [initialContent, setInitialContent] = useState('');
|
||||||
const [truncated, setTruncated] = useState(false);
|
const [truncated, setTruncated] = useState(false);
|
||||||
const MAX_PREVIEW_BYTES = 1024 * 1024; // 1MB
|
|
||||||
const isDirty = content !== initialContent;
|
const isDirty = content !== initialContent;
|
||||||
const onRequestCloseRef = useRef(onRequestClose);
|
const onRequestCloseRef = useRef(onRequestClose);
|
||||||
onRequestCloseRef.current = onRequestClose;
|
onRequestCloseRef.current = onRequestClose;
|
||||||
|
|||||||
@@ -21,8 +21,7 @@ async function loadApps() {
|
|||||||
try {
|
try {
|
||||||
const items = await pluginsApi.list();
|
const items = await pluginsApi.list();
|
||||||
items.filter(p => p.enabled !== false).forEach((p) => registerPluginAsApp(p));
|
items.filter(p => p.enabled !== false).forEach((p) => registerPluginAsApp(p));
|
||||||
} catch (e) {
|
} catch { void 0; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function registerPluginAsApp(p: PluginItem) {
|
function registerPluginAsApp(p: PluginItem) {
|
||||||
@@ -104,5 +103,5 @@ export async function reloadPluginApps() {
|
|||||||
: undefined;
|
: undefined;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch { }
|
} catch { void 0; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|||||||
const res = await authApi.login({ username, password });
|
const res = await authApi.login({ username, password });
|
||||||
if (res) {
|
if (res) {
|
||||||
setToken(res.access_token);
|
setToken(res.access_token);
|
||||||
try { await refreshUser(); } catch (_) {}
|
try { await refreshUser(); } catch { void 0; }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -52,7 +52,6 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|||||||
} else {
|
} else {
|
||||||
setUser(null);
|
setUser(null);
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [token]);
|
}, [token]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { ConfigProvider, theme as antdTheme } from 'antd';
|
import { ConfigProvider, theme as antdTheme } from 'antd';
|
||||||
import zhCN from 'antd/locale/zh_CN';
|
import zhCN from 'antd/locale/zh_CN';
|
||||||
import enUS from 'antd/locale/en_US';
|
import enUS from 'antd/locale/en_US';
|
||||||
@@ -85,15 +85,22 @@ function buildThemeConfig(state: ThemeState, systemDark: boolean): ThemeConfig {
|
|||||||
const baseComponents = { ...(baseTheme.components as any) };
|
const baseComponents = { ...(baseTheme.components as any) };
|
||||||
if (resolvedMode === 'dark' && baseComponents) {
|
if (resolvedMode === 'dark' && baseComponents) {
|
||||||
if (baseComponents.Menu) {
|
if (baseComponents.Menu) {
|
||||||
const { itemHoverColor, itemHoverBg, itemSelectedBg, itemSelectedColor, ...rest } = baseComponents.Menu;
|
const rest = { ...baseComponents.Menu };
|
||||||
|
delete rest.itemHoverColor;
|
||||||
|
delete rest.itemHoverBg;
|
||||||
|
delete rest.itemSelectedBg;
|
||||||
|
delete rest.itemSelectedColor;
|
||||||
baseComponents.Menu = rest;
|
baseComponents.Menu = rest;
|
||||||
}
|
}
|
||||||
if (baseComponents.Dropdown) {
|
if (baseComponents.Dropdown) {
|
||||||
const { controlItemBgHover, ...rest } = baseComponents.Dropdown;
|
const rest = { ...baseComponents.Dropdown };
|
||||||
|
delete rest.controlItemBgHover;
|
||||||
baseComponents.Dropdown = rest;
|
baseComponents.Dropdown = rest;
|
||||||
}
|
}
|
||||||
if (baseComponents.Table) {
|
if (baseComponents.Table) {
|
||||||
const { headerBg, rowHoverBg, ...rest } = baseComponents.Table;
|
const rest = { ...baseComponents.Table };
|
||||||
|
delete rest.headerBg;
|
||||||
|
delete rest.rowHoverBg;
|
||||||
baseComponents.Table = rest;
|
baseComponents.Table = rest;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,9 +113,14 @@ export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
|||||||
const { lang } = useI18n();
|
const { lang } = useI18n();
|
||||||
const systemDark = useSystemDarkPreferred();
|
const systemDark = useSystemDarkPreferred();
|
||||||
const [state, setState] = useState<ThemeState>({ mode: 'light' });
|
const [state, setState] = useState<ThemeState>({ mode: 'light' });
|
||||||
|
const stateRef = useRef(state);
|
||||||
const styleTagRef = useRef<HTMLStyleElement | null>(null);
|
const styleTagRef = useRef<HTMLStyleElement | null>(null);
|
||||||
|
|
||||||
const ensureStyleTag = () => {
|
useEffect(() => {
|
||||||
|
stateRef.current = state;
|
||||||
|
}, [state]);
|
||||||
|
|
||||||
|
const ensureStyleTag = useCallback(() => {
|
||||||
if (styleTagRef.current) return styleTagRef.current;
|
if (styleTagRef.current) return styleTagRef.current;
|
||||||
let styleEl = document.getElementById('foxel-custom-css') as HTMLStyleElement | null;
|
let styleEl = document.getElementById('foxel-custom-css') as HTMLStyleElement | null;
|
||||||
if (!styleEl) {
|
if (!styleEl) {
|
||||||
@@ -118,22 +130,22 @@ export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
|||||||
}
|
}
|
||||||
styleTagRef.current = styleEl;
|
styleTagRef.current = styleEl;
|
||||||
return styleEl;
|
return styleEl;
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
const applyCustomCSS = (cssText: string | null | undefined) => {
|
const applyCustomCSS = useCallback((cssText: string | null | undefined) => {
|
||||||
const el = ensureStyleTag();
|
const el = ensureStyleTag();
|
||||||
el.textContent = cssText || '';
|
el.textContent = cssText || '';
|
||||||
};
|
}, [ensureStyleTag]);
|
||||||
|
|
||||||
const applyHtmlDataTheme = (mode: ThemeMode) => {
|
const applyHtmlDataTheme = useCallback((mode: ThemeMode) => {
|
||||||
const finalMode = mode === 'system' ? (systemDark ? 'dark' : 'light') : mode;
|
const finalMode = mode === 'system' ? (systemDark ? 'dark' : 'light') : mode;
|
||||||
document.documentElement.setAttribute('data-theme', finalMode);
|
document.documentElement.setAttribute('data-theme', finalMode);
|
||||||
};
|
}, [systemDark]);
|
||||||
|
|
||||||
const refreshTheme = async () => {
|
const refreshTheme = useCallback(async () => {
|
||||||
if (!isAuthenticated) {
|
if (!isAuthenticated) {
|
||||||
applyHtmlDataTheme(state.mode || 'light');
|
applyHtmlDataTheme(stateRef.current.mode || 'light');
|
||||||
applyCustomCSS(state.customCSS || '');
|
applyCustomCSS(stateRef.current.customCSS || '');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -147,22 +159,23 @@ export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
|||||||
setState({ mode, primaryColor: primary, borderRadius: radius, customTokens, customCSS });
|
setState({ mode, primaryColor: primary, borderRadius: radius, customTokens, customCSS });
|
||||||
applyHtmlDataTheme(mode);
|
applyHtmlDataTheme(mode);
|
||||||
applyCustomCSS(customCSS);
|
applyCustomCSS(customCSS);
|
||||||
} catch (e) {
|
} catch {
|
||||||
applyHtmlDataTheme('light');
|
applyHtmlDataTheme('light');
|
||||||
applyCustomCSS('');
|
applyCustomCSS('');
|
||||||
}
|
}
|
||||||
};
|
}, [applyCustomCSS, applyHtmlDataTheme, isAuthenticated]);
|
||||||
|
|
||||||
const previewTheme = (patch: Partial<ThemeState>) => {
|
const previewTheme = useCallback((patch: Partial<ThemeState>) => {
|
||||||
const next: ThemeState = { ...state, ...patch };
|
const next: ThemeState = { ...stateRef.current, ...patch };
|
||||||
|
stateRef.current = next;
|
||||||
setState(next);
|
setState(next);
|
||||||
applyHtmlDataTheme(next.mode || 'light');
|
applyHtmlDataTheme(next.mode || 'light');
|
||||||
applyCustomCSS(next.customCSS || '');
|
applyCustomCSS(next.customCSS || '');
|
||||||
};
|
}, [applyCustomCSS, applyHtmlDataTheme]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
refreshTheme();
|
void refreshTheme();
|
||||||
}, [isAuthenticated, systemDark]);
|
}, [refreshTheme]);
|
||||||
|
|
||||||
const themeConfig = useMemo(() => buildThemeConfig(state, systemDark), [state, systemDark]);
|
const themeConfig = useMemo(() => buildThemeConfig(state, systemDark), [state, systemDark]);
|
||||||
const resolvedMode: ThemeMode = useMemo(() => (state.mode === 'system' ? (systemDark ? 'dark' : 'light') : state.mode), [state.mode, systemDark]);
|
const resolvedMode: ThemeMode = useMemo(() => (state.mode === 'system' ? (systemDark ? 'dark' : 'light') : state.mode), [state.mode, systemDark]);
|
||||||
@@ -173,7 +186,7 @@ export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
|||||||
previewTheme,
|
previewTheme,
|
||||||
mode: state.mode,
|
mode: state.mode,
|
||||||
resolvedMode,
|
resolvedMode,
|
||||||
}), [state.mode, resolvedMode]);
|
}), [previewTheme, refreshTheme, resolvedMode, state.mode]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Ctx.Provider value={ctxValue}>
|
<Ctx.Provider value={ctxValue}>
|
||||||
|
|||||||
@@ -30,6 +30,5 @@ export function useAsyncSafeEffect(
|
|||||||
ac.abort();
|
ac.abort();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, deps);
|
}, deps); // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { createContext, useContext, useMemo, useState, useEffect } from 'react';
|
import { createContext, useCallback, useContext, useMemo, useState, useEffect } from 'react';
|
||||||
import type { PropsWithChildren } from 'react';
|
import type { PropsWithChildren } from 'react';
|
||||||
import en from './locales/en.json';
|
import en from './locales/en.json';
|
||||||
import zhOverrides from './locales/zh.json';
|
import zhOverrides from './locales/zh.json';
|
||||||
@@ -27,22 +27,22 @@ function interpolate(template: string, params?: Record<string, string | number>)
|
|||||||
export function I18nProvider({ children }: PropsWithChildren) {
|
export function I18nProvider({ children }: PropsWithChildren) {
|
||||||
const [lang, setLangState] = useState<Lang>(() => (localStorage.getItem('lang') as Lang) || 'zh');
|
const [lang, setLangState] = useState<Lang>(() => (localStorage.getItem('lang') as Lang) || 'zh');
|
||||||
|
|
||||||
const setLang = (l: Lang) => {
|
const setLang = useCallback((l: Lang) => {
|
||||||
setLangState(l);
|
setLangState(l);
|
||||||
localStorage.setItem('lang', l);
|
localStorage.setItem('lang', l);
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.documentElement.lang = lang;
|
document.documentElement.lang = lang;
|
||||||
}, [lang]);
|
}, [lang]);
|
||||||
|
|
||||||
const t = (key: string, params?: Record<string, string | number>) => {
|
const t = useCallback((key: string, params?: Record<string, string | number>) => {
|
||||||
const dict = dicts[lang] || {};
|
const dict = dicts[lang] || {};
|
||||||
const raw = dict[key] ?? key; // fallback to key (English)
|
const raw = dict[key] ?? key; // fallback to key (English)
|
||||||
return interpolate(raw, params);
|
return interpolate(raw, params);
|
||||||
};
|
}, [lang]);
|
||||||
|
|
||||||
const value = useMemo<I18nContextValue>(() => ({ lang, setLang, t }), [lang]);
|
const value = useMemo<I18nContextValue>(() => ({ lang, setLang, t }), [lang, setLang, t]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<I18nContext.Provider value={value}>
|
<I18nContext.Provider value={value}>
|
||||||
|
|||||||
@@ -302,7 +302,7 @@ const SearchDialog: React.FC<SearchDialogProps> = ({ open, onClose }) => {
|
|||||||
} else {
|
} else {
|
||||||
setHasMore(false);
|
setHasMore(false);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch {
|
||||||
if (requestId !== requestIdRef.current) {
|
if (requestId !== requestIdRef.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ const AdaptersPage = memo(function AdaptersPage() {
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, []);
|
}, [t]);
|
||||||
|
|
||||||
useEffect(() => { fetchList(); }, [fetchList]);
|
useEffect(() => { fetchList(); }, [fetchList]);
|
||||||
|
|
||||||
|
|||||||
@@ -47,28 +47,24 @@ const AuditLogsPage = memo(function AuditLogsPage() {
|
|||||||
const [selectedLog, setSelectedLog] = useState<AuditLogItem | null>(null);
|
const [selectedLog, setSelectedLog] = useState<AuditLogItem | null>(null);
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const buildParams = () => {
|
|
||||||
const params: any = { ...filters };
|
|
||||||
if (!params.action) delete params.action;
|
|
||||||
if (params.success === '') delete params.success;
|
|
||||||
if (!params.username) delete params.username;
|
|
||||||
if (!params.path) delete params.path;
|
|
||||||
if (!params.start_time) delete params.start_time;
|
|
||||||
if (!params.end_time) delete params.end_time;
|
|
||||||
return params;
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchList = useCallback(async () => {
|
const fetchList = useCallback(async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const res = await auditApi.list(buildParams());
|
const params: any = { ...filters };
|
||||||
|
if (!params.action) delete params.action;
|
||||||
|
if (params.success === '') delete params.success;
|
||||||
|
if (!params.username) delete params.username;
|
||||||
|
if (!params.path) delete params.path;
|
||||||
|
if (!params.start_time) delete params.start_time;
|
||||||
|
if (!params.end_time) delete params.end_time;
|
||||||
|
const res = await auditApi.list(params);
|
||||||
setData(res);
|
setData(res);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
message.error(e.message || t('Load failed'));
|
message.error(e.message || t('Load failed'));
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [filters]);
|
}, [filters, t]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchList();
|
fetchList();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { memo, useState, useEffect } from 'react';
|
import { memo, useState, useEffect, useCallback } from 'react';
|
||||||
import { Modal, Radio, message, Button, Typography, Input, Space } from 'antd';
|
import { Modal, Radio, message, Button, Typography, Input, Space } from 'antd';
|
||||||
import { CopyOutlined, FileMarkdownOutlined } from '@ant-design/icons';
|
import { CopyOutlined, FileMarkdownOutlined } from '@ant-design/icons';
|
||||||
import type { VfsEntry } from '../../../../api/client';
|
import type { VfsEntry } from '../../../../api/client';
|
||||||
@@ -33,14 +33,7 @@ export const DirectLinkModal = memo(function DirectLinkModal({ entry, path, open
|
|||||||
const [link, setLink] = useState('');
|
const [link, setLink] = useState('');
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
useEffect(() => {
|
const generateLink = useCallback(async () => {
|
||||||
if (open && entry) {
|
|
||||||
setLink('');
|
|
||||||
generateLink();
|
|
||||||
}
|
|
||||||
}, [open, entry, expiresIn]);
|
|
||||||
|
|
||||||
const generateLink = async () => {
|
|
||||||
if (!entry) return;
|
if (!entry) return;
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
@@ -57,7 +50,14 @@ export const DirectLinkModal = memo(function DirectLinkModal({ entry, path, open
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
}, [entry, expiresIn, path, t]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open && entry) {
|
||||||
|
setLink('');
|
||||||
|
void generateLink();
|
||||||
|
}
|
||||||
|
}, [open, entry, generateLink]);
|
||||||
|
|
||||||
const handleCopy = (text: string) => {
|
const handleCopy = (text: string) => {
|
||||||
navigator.clipboard.writeText(text);
|
navigator.clipboard.writeText(text);
|
||||||
|
|||||||
@@ -41,9 +41,7 @@ export function MoveCopyModal({ mode, entries, open, defaultPath, onOk, onCancel
|
|||||||
try {
|
try {
|
||||||
await onOk(trimmed);
|
await onOk(trimmed);
|
||||||
onCancel();
|
onCancel();
|
||||||
} catch (e) {
|
} catch { void 0; } finally {
|
||||||
// 上层已处理提示,这里只需保持对话框
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export function useAppWindows(path: string) {
|
|||||||
}
|
}
|
||||||
const defaultApp = getDefaultAppForEntry(entry) || apps[0];
|
const defaultApp = getDefaultAppForEntry(entry) || apps[0];
|
||||||
openWithApp(entry, defaultApp);
|
openWithApp(entry, defaultApp);
|
||||||
}, [openWithApp]);
|
}, [openWithApp, t]);
|
||||||
|
|
||||||
const confirmOpenWithApp = useCallback((entry: VfsEntry, appKey: string) => {
|
const confirmOpenWithApp = useCallback((entry: VfsEntry, appKey: string) => {
|
||||||
const app = getAppByKey(appKey);
|
const app = getAppByKey(appKey);
|
||||||
@@ -72,7 +72,7 @@ export function useAppWindows(path: string) {
|
|||||||
openWithApp(entry, app);
|
openWithApp(entry, app);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [openWithApp]);
|
}, [openWithApp, t]);
|
||||||
|
|
||||||
const closeWindow = (id: string) => setAppWindows(ws => ws.filter(w => w.id !== id));
|
const closeWindow = (id: string) => setAppWindows(ws => ws.filter(w => w.id !== id));
|
||||||
const toggleMax = (id: string) => setAppWindows(ws => ws.map(w => w.id === id ? { ...w, maximized: !w.maximized } : w));
|
const toggleMax = (id: string) => setAppWindows(ws => ws.map(w => w.id === id ? { ...w, maximized: !w.maximized } : w));
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export function useFileActions({ path, refresh, clearSelection, onShare, onGetDi
|
|||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
message.error(e.message);
|
message.error(e.message);
|
||||||
}
|
}
|
||||||
}, [path, refresh]);
|
}, [path, refresh, t]);
|
||||||
|
|
||||||
const doDelete = useCallback(async (entries: VfsEntry[]) => {
|
const doDelete = useCallback(async (entries: VfsEntry[]) => {
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
@@ -51,7 +51,7 @@ export function useFileActions({ path, refresh, clearSelection, onShare, onGetDi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [path, refresh, clearSelection]);
|
}, [path, refresh, clearSelection, t]);
|
||||||
|
|
||||||
const doRename = useCallback(async (entry: VfsEntry, newName: string) => {
|
const doRename = useCallback(async (entry: VfsEntry, newName: string) => {
|
||||||
if (!newName.trim() || newName.trim() === entry.name) {
|
if (!newName.trim() || newName.trim() === entry.name) {
|
||||||
@@ -173,7 +173,7 @@ export function useFileActions({ path, refresh, clearSelection, onShare, onGetDi
|
|||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
message.error(e.message || t('Download failed'));
|
message.error(e.message || t('Download failed'));
|
||||||
}
|
}
|
||||||
}, [path]);
|
}, [path, t]);
|
||||||
|
|
||||||
const doShare = useCallback((entries: VfsEntry[]) => {
|
const doShare = useCallback((entries: VfsEntry[]) => {
|
||||||
if (entries.length === 0) {
|
if (entries.length === 0) {
|
||||||
@@ -181,7 +181,7 @@ export function useFileActions({ path, refresh, clearSelection, onShare, onGetDi
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onShare(entries);
|
onShare(entries);
|
||||||
}, [onShare]);
|
}, [onShare, t]);
|
||||||
|
|
||||||
const doGetDirectLink = useCallback((entry: VfsEntry) => {
|
const doGetDirectLink = useCallback((entry: VfsEntry) => {
|
||||||
if (entry.is_dir) {
|
if (entry.is_dir) {
|
||||||
@@ -189,7 +189,7 @@ export function useFileActions({ path, refresh, clearSelection, onShare, onGetDi
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onGetDirectLink(entry);
|
onGetDirectLink(entry);
|
||||||
}, [onGetDirectLink]);
|
}, [onGetDirectLink, t]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
doCreateDir,
|
doCreateDir,
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export function useProcessor({ path, processorTypes, refresh }: ProcessorParams)
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [modal.entry, selectedProcessor, processorTypes, config, path, overwrite, savingPath, refresh]);
|
}, [modal.entry, selectedProcessor, processorTypes, config, path, overwrite, savingPath, refresh, t]);
|
||||||
|
|
||||||
const handleCancel = useCallback(() => {
|
const handleCancel = useCallback(() => {
|
||||||
setModal({ entry: null, visible: false });
|
setModal({ entry: null, visible: false });
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { memo, useEffect, useMemo, useState } from 'react';
|
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { Button, Modal, Form, Input, Tag, message, Card, Typography, Popconfirm, Empty, Skeleton, theme, Divider, Tabs, Select, Pagination } from 'antd';
|
import { Button, Modal, Form, Input, Tag, message, Card, Typography, Popconfirm, Empty, Skeleton, theme, Divider, Tabs, Select, Pagination } from 'antd';
|
||||||
import { GithubOutlined, LinkOutlined } from '@ant-design/icons';
|
import { GithubOutlined, LinkOutlined } from '@ant-design/icons';
|
||||||
import { pluginsApi, type PluginItem } from '../api/plugins';
|
import { pluginsApi, type PluginItem } from '../api/plugins';
|
||||||
@@ -38,7 +38,7 @@ const PluginsPage = memo(function PluginsPage() {
|
|||||||
try {
|
try {
|
||||||
await ensureAppsLoaded();
|
await ensureAppsLoaded();
|
||||||
setSystemApps(listSystemApps());
|
setSystemApps(listSystemApps());
|
||||||
} catch {}
|
} catch { void 0; }
|
||||||
})();
|
})();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -48,23 +48,23 @@ const PluginsPage = memo(function PluginsPage() {
|
|||||||
return set;
|
return set;
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
const reloadRepo = async () => {
|
const reloadRepo = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
setRepoLoading(true);
|
setRepoLoading(true);
|
||||||
const res = await fetchRepoList({ query: repoQ || undefined, sort: repoSort, page: repoPage, pageSize: repoPageSize });
|
const res = await fetchRepoList({ query: repoQ || undefined, sort: repoSort, page: repoPage, pageSize: repoPageSize });
|
||||||
setRepoItems(res.items || []);
|
setRepoItems(res.items || []);
|
||||||
setRepoTotal(res.total || 0);
|
setRepoTotal(res.total || 0);
|
||||||
} catch (e) {
|
} catch {
|
||||||
setRepoItems([]);
|
setRepoItems([]);
|
||||||
setRepoTotal(0);
|
setRepoTotal(0);
|
||||||
} finally {
|
} finally {
|
||||||
setRepoLoading(false);
|
setRepoLoading(false);
|
||||||
}
|
}
|
||||||
};
|
}, [repoPage, repoPageSize, repoQ, repoSort]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (tab === 'discover') reloadRepo();
|
if (tab === 'discover') reloadRepo();
|
||||||
}, [tab, repoQ, repoSort, repoPage, repoPageSize]);
|
}, [reloadRepo, tab]);
|
||||||
|
|
||||||
const handleAdd = async () => {
|
const handleAdd = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -73,13 +73,13 @@ const PluginsPage = memo(function PluginsPage() {
|
|||||||
try {
|
try {
|
||||||
const p = await loadPlugin(created);
|
const p = await loadPlugin(created);
|
||||||
await ensureManifest(created.id, p);
|
await ensureManifest(created.id, p);
|
||||||
} catch {}
|
} catch { void 0; }
|
||||||
setAdding(false);
|
setAdding(false);
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
await reload();
|
await reload();
|
||||||
await reloadPluginApps();
|
await reloadPluginApps();
|
||||||
message.success(t('Installed successfully'));
|
message.success(t('Installed successfully'));
|
||||||
} catch {}
|
} catch { void 0; }
|
||||||
};
|
};
|
||||||
|
|
||||||
const filtered = useMemo(() => {
|
const filtered = useMemo(() => {
|
||||||
@@ -290,7 +290,7 @@ const PluginsPage = memo(function PluginsPage() {
|
|||||||
try {
|
try {
|
||||||
const p = await loadPlugin(created);
|
const p = await loadPlugin(created);
|
||||||
await ensureManifest(created.id, p);
|
await ensureManifest(created.id, p);
|
||||||
} catch {}
|
} catch { void 0; }
|
||||||
await reload();
|
await reload();
|
||||||
await reloadPluginApps();
|
await reloadPluginApps();
|
||||||
message.success(t('Installed successfully'));
|
message.success(t('Installed successfully'));
|
||||||
|
|||||||
@@ -22,19 +22,19 @@ export const DirectoryViewer = memo(function DirectoryViewer({ token, shareInfo,
|
|||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const loadData = useCallback(async (p: string) => {
|
const loadData = useCallback(async (p: string) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError('');
|
setError('');
|
||||||
try {
|
try {
|
||||||
const listing = await shareApi.listDir(token, p, password);
|
const listing = await shareApi.listDir(token, p, password);
|
||||||
setEntries(listing.entries || []);
|
setEntries(listing.entries || []);
|
||||||
setCurrentPath(p);
|
setCurrentPath(p);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
setError(e.message || t('Share load failed'));
|
setError(e.message || t('Share load failed'));
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [token, password]);
|
}, [password, t, token]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadData(currentPath);
|
loadData(currentPath);
|
||||||
|
|||||||
@@ -17,10 +17,10 @@ const PublicSharePage = memo(function PublicSharePage() {
|
|||||||
const [verified, setVerified] = useState(false);
|
const [verified, setVerified] = useState(false);
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const loadData = useCallback(async (pwd?: string) => {
|
const loadData = useCallback(async (pwd?: string) => {
|
||||||
if (!token) return;
|
if (!token) return;
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError('');
|
setError('');
|
||||||
try {
|
try {
|
||||||
let info = shareInfo;
|
let info = shareInfo;
|
||||||
if (!info) {
|
if (!info) {
|
||||||
@@ -50,10 +50,10 @@ const PublicSharePage = memo(function PublicSharePage() {
|
|||||||
if (e.message === '需要密码') {
|
if (e.message === '需要密码') {
|
||||||
setVerified(false);
|
setVerified(false);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [token, shareInfo, password, verified]);
|
}, [password, shareInfo, t, token, verified]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadData();
|
loadData();
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const SharePage = memo(function SharePage() {
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, []);
|
}, [t]);
|
||||||
|
|
||||||
useEffect(() => { fetchList(); }, [fetchList]);
|
useEffect(() => { fetchList(); }, [fetchList]);
|
||||||
|
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ function parseEmailConfig(raw?: string | null): EmailFormValues {
|
|||||||
timeout: data?.timeout !== undefined ? Number(data.timeout) : DEFAULT_FORM.timeout,
|
timeout: data?.timeout !== undefined ? Number(data.timeout) : DEFAULT_FORM.timeout,
|
||||||
security: (data?.security ?? DEFAULT_FORM.security) as EmailFormValues['security'],
|
security: (data?.security ?? DEFAULT_FORM.security) as EmailFormValues['security'],
|
||||||
};
|
};
|
||||||
} catch (_err) {
|
} catch {
|
||||||
return { ...DEFAULT_FORM };
|
return { ...DEFAULT_FORM };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,12 +133,13 @@ export async function ensureManifest(pluginId: number, plugin: RegisteredPlugin)
|
|||||||
website: plugin.website,
|
website: plugin.website,
|
||||||
github: plugin.github,
|
github: plugin.github,
|
||||||
};
|
};
|
||||||
try { console.debug('[foxel] report manifest', pluginId, manifest); } catch { }
|
try { console.debug('[foxel] report manifest', pluginId, manifest); } catch { void 0; }
|
||||||
const key = `foxel:manifestReported:${pluginId}`;
|
const key = `foxel:manifestReported:${pluginId}`;
|
||||||
if (sessionStorage.getItem(key) === '1') return;
|
if (sessionStorage.getItem(key) === '1') return;
|
||||||
try {
|
try {
|
||||||
await pluginsApi.updateManifest(pluginId, manifest);
|
await pluginsApi.updateManifest(pluginId, manifest);
|
||||||
sessionStorage.setItem(key, '1');
|
sessionStorage.setItem(key, '1');
|
||||||
} catch {
|
} catch {
|
||||||
|
void 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user