Преглед изворни кода

fix(frontend): refresh subscription settings after save (#6287)

The derived defaults query is cached indefinitely, so subscription links kept using the old path after settings saves. Invalidate it only after successful saves so inbounds and clients refetch generated subscription URLs.
dawn пре 8 часа
родитељ
комит
b73ceae081

+ 1 - 0
frontend/src/api/queries/useAllSettings.ts

@@ -77,6 +77,7 @@ export function useAllSettings() {
       if (!msg?.success) return;
       if (saved) markSaved(saved);
       queryClient.invalidateQueries({ queryKey: keys.settings.all() });
+      queryClient.invalidateQueries({ queryKey: keys.settings.defaults() });
     },
   });
 

+ 61 - 0
frontend/src/test/use-all-settings.test.tsx

@@ -13,6 +13,67 @@ afterEach(() => {
 });
 
 describe('useAllSettings', () => {
+  it('refreshes cached default settings after a successful save', async () => {
+    vi.spyOn(HttpUtil, 'post').mockResolvedValue(new Msg(true, '', {}));
+    const queryClient = makeTestQueryClient();
+    const fetchDefaults = vi
+      .fn()
+      .mockResolvedValueOnce({ subURI: 'https://example.com/sub/' })
+      .mockResolvedValueOnce({ subURI: 'https://example.com/my_custom_path/' });
+    const defaultsQuery = {
+      queryKey: keys.settings.defaults(),
+      queryFn: fetchDefaults,
+      staleTime: Infinity,
+    };
+    const wrapper = ({ children }: { children: ReactNode }) => (
+      <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
+    );
+
+    await queryClient.fetchQuery(defaultsQuery);
+    const { result } = renderHook(() => useAllSettings(), { wrapper });
+
+    await waitFor(() => expect(result.current.fetched).toBe(true));
+    await act(async () => {
+      await result.current.saveAll();
+    });
+
+    const defaults = await queryClient.fetchQuery(defaultsQuery);
+    expect(fetchDefaults).toHaveBeenCalledTimes(2);
+    expect(defaults.subURI).toBe('https://example.com/my_custom_path/');
+  });
+
+  it('keeps cached default settings when a save fails', async () => {
+    vi.spyOn(HttpUtil, 'post').mockImplementation(async (url) => {
+      if (url === '/panel/api/setting/update') return new Msg(false, 'Save failed');
+      return new Msg(true, '', {});
+    });
+    const queryClient = makeTestQueryClient();
+    const fetchDefaults = vi
+      .fn()
+      .mockResolvedValueOnce({ subURI: 'https://example.com/sub/' })
+      .mockResolvedValueOnce({ subURI: 'https://example.com/my_custom_path/' });
+    const defaultsQuery = {
+      queryKey: keys.settings.defaults(),
+      queryFn: fetchDefaults,
+      staleTime: Infinity,
+    };
+    const wrapper = ({ children }: { children: ReactNode }) => (
+      <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
+    );
+
+    await queryClient.fetchQuery(defaultsQuery);
+    const { result } = renderHook(() => useAllSettings(), { wrapper });
+
+    await waitFor(() => expect(result.current.fetched).toBe(true));
+    await act(async () => {
+      await result.current.saveAll();
+    });
+
+    const defaults = await queryClient.fetchQuery(defaultsQuery);
+    expect(fetchDefaults).toHaveBeenCalledOnce();
+    expect(defaults.subURI).toBe('https://example.com/sub/');
+  });
+
   it('accepts legacy overlength regex settings without logging a response validation warning', async () => {
     const subJsonUserAgentRegex = 'x'.repeat(2_049);
     vi.spyOn(HttpUtil, 'post').mockResolvedValue(new Msg(true, '', { subJsonUserAgentRegex }));