fix(push): show each login's real push state in the all-accounts panel

Push state is stored under the JMAP account id the server assigned
(enableWebPush keys it on client.getAccountId()), but the all-accounts
panel looked it up under the login's own id, so every account read
"notifications off" even right after enabling them. Read it back
under the JMAP account id.
This commit is contained in:
lucamzanon
2026-10-01 07:36:44 +02:00
parent eacfaf2c37
commit 405c0e656e
2 changed files with 53 additions and 1 deletions
@@ -0,0 +1,47 @@
import { render, waitFor } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import { MultiAccountPushSettings } from '../multi-account-push';
import { useAccountStore } from '@/stores/account-store';
import { useAuthStore } from '@/stores/auth-store';
import * as webPush from '@/lib/web-push';
vi.mock('next-intl', () => ({
useTranslations: () => (key: string) => key,
}));
vi.mock('../settings-section', async (importOriginal) => ({
...(await importOriginal<typeof import('../settings-section')>()),
SettingsSection: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
}));
// Push state is stored under the JMAP account id the server assigned: only
// "jmap-a" has a registration here.
vi.mock('@/lib/web-push', () => ({
isWebPushSupported: () => true,
isWebPushEnabled: vi.fn(async (id: string) => id === 'jmap-a'),
enableWebPushForAccounts: vi.fn(),
disableWebPush: vi.fn(),
}));
describe('MultiAccountPushSettings', () => {
it('reads each login\'s push state under its JMAP account id', async () => {
const clients: Record<string, { getAccountId: () => string }> = {
'a@mail.example.com': { getAccountId: () => 'jmap-a' },
'b@mail.example.com': { getAccountId: () => 'jmap-b' },
};
useAccountStore.setState({
accounts: [
{ id: 'a@mail.example.com', username: 'a', email: 'a@example.com', isConnected: true },
{ id: 'b@mail.example.com', username: 'b', email: 'b@example.com', isConnected: true },
] as never,
});
useAuthStore.setState({ getClientForAccount: ((id: string) => clients[id]) as never });
const { container } = render(<MultiAccountPushSettings />);
await waitFor(() => expect(container.textContent).toContain('a@example.com · state_on'));
expect(container.textContent).toContain('b@example.com · state_off');
expect(webPush.isWebPushEnabled).toHaveBeenCalledWith('jmap-a');
expect(webPush.isWebPushEnabled).not.toHaveBeenCalledWith('a@mail.example.com');
});
});
+6 -1
View File
@@ -31,8 +31,13 @@ export function MultiAccountPushSettings({ relayBaseUrl }: { relayBaseUrl?: stri
const connected = accounts.filter(a => a.isConnected && useAuthStore.getState().getClientForAccount(a.id));
const refresh = useCallback(async () => {
// Push state is stored under the JMAP account id the server assigned (see
// enableWebPush), not under the login's own id: read it back the same way.
const entries = await Promise.all(
useAccountStore.getState().accounts.map(async a => [a.id, await isWebPushEnabled(a.id)] as const),
useAccountStore.getState().accounts.map(async (a) => {
const jmapAccountId = useAuthStore.getState().getClientForAccount(a.id)?.getAccountId();
return [a.id, jmapAccountId ? await isWebPushEnabled(jmapAccountId) : false] as const;
}),
);
setEnabled(Object.fromEntries(entries));
}, []);