mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-03 08:15:38 +00:00
fix: take the browser's templates into the account as soon as its synced settings have loaded
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
// The driver syncs the active account's templates when its synced settings
|
||||
// have loaded, also when the load changed nothing in the browser's list:
|
||||
// only then is it known whose that list is.
|
||||
|
||||
const sync = vi.hoisted(() => ({ schedule: vi.fn(), refresh: vi.fn() }));
|
||||
vi.mock('@/lib/templates/personal-templates', () => ({
|
||||
scheduleAccountSync: sync.schedule,
|
||||
refreshAccountTemplates: sync.refresh,
|
||||
}));
|
||||
vi.mock('@/lib/templates/team-templates', () => ({ refreshTeamTemplates: vi.fn() }));
|
||||
vi.mock('@/lib/browser-navigation', async (importOriginal) => ({
|
||||
...(await importOriginal<Record<string, unknown>>()),
|
||||
apiFetch: vi.fn(async () => new Response(JSON.stringify({ settings: { theme: 'dark' } }), { status: 200 })),
|
||||
}));
|
||||
|
||||
import { AccountTemplateSync } from '../account-template-sync';
|
||||
import { useAuthStore } from '@/stores/auth-store';
|
||||
import { useSettingsStore } from '@/stores/settings-store';
|
||||
|
||||
const ACCOUNT = 'carol@example.org@mail.example.com';
|
||||
const original = useAuthStore.getState();
|
||||
|
||||
beforeEach(() => {
|
||||
sync.schedule.mockClear();
|
||||
localStorage.removeItem('settings-sync-owner');
|
||||
useAuthStore.setState({ isAuthenticated: true, activeAccountId: ACCOUNT, client: {} as never });
|
||||
});
|
||||
afterEach(() => {
|
||||
useAuthStore.setState({ isAuthenticated: original.isAuthenticated, activeAccountId: original.activeAccountId, client: original.client });
|
||||
});
|
||||
|
||||
describe('AccountTemplateSync', () => {
|
||||
it('syncs the active account once its settings have loaded', async () => {
|
||||
render(<AccountTemplateSync />);
|
||||
expect(sync.schedule).not.toHaveBeenCalled();
|
||||
await useSettingsStore.getState().loadFromServer('carol@example.org', 'https://mail.example.com');
|
||||
expect(sync.schedule).toHaveBeenCalledWith(ACCOUNT);
|
||||
});
|
||||
|
||||
it('leaves another account\'s settings load alone', async () => {
|
||||
render(<AccountTemplateSync />);
|
||||
await useSettingsStore.getState().loadFromServer('dave@example.org', 'https://mail.example.com');
|
||||
expect(sync.schedule).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -5,6 +5,7 @@ import { useAuthStore } from '@/stores/auth-store';
|
||||
import { useAccountTemplatesStore } from '@/stores/account-templates-store';
|
||||
import { refreshAccountTemplates, scheduleAccountSync } from '@/lib/templates/personal-templates';
|
||||
import { useTemplateStore } from '@/stores/template-store';
|
||||
import { onSettingsLoadSettled } from '@/stores/settings-store';
|
||||
import { refreshTeamTemplates } from '@/lib/templates/team-templates';
|
||||
|
||||
/**
|
||||
@@ -26,7 +27,16 @@ export function AccountTemplateSync() {
|
||||
// loaded, or a template changed there) is merged in at once.
|
||||
useEffect(() => {
|
||||
if (!isAuthenticated || !activeAccountId) return;
|
||||
return useTemplateStore.subscribe(() => scheduleAccountSync(activeAccountId));
|
||||
const unsubscribe = useTemplateStore.subscribe(() => scheduleAccountSync(activeAccountId));
|
||||
// Once the account's synced settings have loaded, its browser list is
|
||||
// known, even when the load changed nothing in it.
|
||||
const unlisten = onSettingsLoadSettled((accountId) => {
|
||||
if (accountId === activeAccountId) scheduleAccountSync(activeAccountId);
|
||||
});
|
||||
return () => {
|
||||
unsubscribe();
|
||||
unlisten();
|
||||
};
|
||||
}, [activeAccountId, isAuthenticated]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -65,6 +65,14 @@ export function isSettingsLoadSettled(accountId: string): boolean {
|
||||
return settledLoads.has(accountId);
|
||||
}
|
||||
|
||||
const settledListeners = new Set<(accountId: string) => void>();
|
||||
|
||||
/** Called whenever an account's settings load finishes (with or without a copy). */
|
||||
export function onSettingsLoadSettled(listener: (accountId: string) => void): () => void {
|
||||
settledListeners.add(listener);
|
||||
return () => settledListeners.delete(listener);
|
||||
}
|
||||
|
||||
/** The account whose server copy the local settings (and template list) mirror; null if none. */
|
||||
export function readSettingsOwner(): string | null {
|
||||
try {
|
||||
@@ -1819,6 +1827,7 @@ export const useSettingsStore = create<SettingsState>()(
|
||||
return failWithoutForeignState();
|
||||
} finally {
|
||||
settledLoads.add(accountId);
|
||||
for (const listener of settledListeners) listener(accountId);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user