mirror of
https://github.com/bulwarkmail/native.git
synced 2026-10-31 08:06:36 +00:00
fix: show the relay push still goes through after the mail server refused a new one
This commit is contained in:
@@ -31,7 +31,7 @@ import {
|
||||
revokePushDevice,
|
||||
serverSupportsEmailPush,
|
||||
setStoredRelayBaseUrl,
|
||||
setupPushNotifications,
|
||||
enablePushFromSettings,
|
||||
getStoredPushTransport,
|
||||
setStoredPushTransport,
|
||||
type PushDevice,
|
||||
@@ -137,11 +137,13 @@ export function NotificationSettings() {
|
||||
const relayValid = isValidRelayUrl(trimmed);
|
||||
const busy = pushStatus.kind === 'busy';
|
||||
|
||||
const refreshDevices = useCallback(async () => {
|
||||
// `relay` is the relay an enable run just settled on: the field's state
|
||||
// has not caught up with it yet.
|
||||
const refreshDevices = useCallback(async (relay?: string) => {
|
||||
if (!activeAccountId || !client) return;
|
||||
setDevices({ kind: 'loading' });
|
||||
try {
|
||||
const list = await listPushDevices({ accountId: activeAccountId, relayBaseUrl: trimmed });
|
||||
const list = await listPushDevices({ accountId: activeAccountId, relayBaseUrl: relay ?? trimmed });
|
||||
setDevices({ kind: 'loaded', devices: list });
|
||||
} catch {
|
||||
setDevices({ kind: 'error' });
|
||||
@@ -196,21 +198,19 @@ export function NotificationSettings() {
|
||||
kind: 'busy',
|
||||
message: t('settings.notifications.push.status_busy', 'Working…'),
|
||||
});
|
||||
try {
|
||||
// The setup stores the relay itself, and keeps the one in use when the
|
||||
// mail server refuses the new one's address.
|
||||
await setupPushNotifications({
|
||||
relayBaseUrl: trimmed,
|
||||
accountLabel: username ?? undefined,
|
||||
forceRecreate,
|
||||
});
|
||||
setPushEnabled(true);
|
||||
setPushStatus({ kind: 'enabled' });
|
||||
} catch (error) {
|
||||
setPushStatus({ kind: 'error', message: describeError(error) });
|
||||
} finally {
|
||||
void refreshDevices();
|
||||
}
|
||||
// The setup stores the relay itself, and keeps the one in use when the
|
||||
// mail server refuses the new one's address: the field follows it.
|
||||
const outcome = await enablePushFromSettings({
|
||||
relayBaseUrl: trimmed,
|
||||
accountLabel: username ?? undefined,
|
||||
forceRecreate,
|
||||
});
|
||||
setRelayUrl(outcome.relayBaseUrl);
|
||||
setPushEnabled(outcome.enabled);
|
||||
setPushStatus(outcome.error
|
||||
? { kind: 'error', message: describeError(outcome.error) }
|
||||
: { kind: 'enabled' });
|
||||
void refreshDevices(outcome.relayBaseUrl);
|
||||
};
|
||||
|
||||
// Switching the transport while push is on re-registers immediately so the
|
||||
|
||||
@@ -110,6 +110,7 @@ import {
|
||||
setupPushNotifications,
|
||||
deviceClientIdKey,
|
||||
disablePushForAccount,
|
||||
enablePushFromSettings,
|
||||
forgetPushServerState,
|
||||
isValidRelayUrl,
|
||||
mailAccountsWithoutPush,
|
||||
@@ -420,6 +421,31 @@ describe('setupPushNotifications subscription shape', () => {
|
||||
expect(fetchMock.mock.calls.some((c) => (c[1] as RequestInit | undefined)?.method === 'DELETE')).toBe(false);
|
||||
});
|
||||
|
||||
it('hands the settings the relay in use back after the server refused a new one', async () => {
|
||||
await AsyncStorage.setItem('push:relayBaseUrl:v1', RELAY);
|
||||
await AsyncStorage.setItem(SUB_KEY, 'existing');
|
||||
listMock.mockResolvedValue([{ id: 'existing', deviceClientId: OUR_DCID, expires: new Date(Date.now() + 5 * 86400000).toISOString(), types: ['EmailDelivery'] }]);
|
||||
createMock.mockRejectedValueOnce(new JMAPSetError('invalidProperties', 'Host is not a public domain name', ['url']));
|
||||
|
||||
const outcome = await enablePushFromSettings({ relayBaseUrl: 'https://relay.home.lan/', forceRecreate: true });
|
||||
|
||||
expect(outcome.error).toBeInstanceOf(PushSetupError);
|
||||
expect(outcome.relayBaseUrl).toBe(RELAY);
|
||||
expect(outcome.enabled).toBe(true);
|
||||
|
||||
// Nothing in use yet: the typed relay stays, so it can be corrected.
|
||||
await AsyncStorage.clear();
|
||||
await AsyncStorage.setItem(deviceClientIdKey(ACCOUNT_ID), OUR_DCID);
|
||||
listMock.mockResolvedValue([]);
|
||||
createMock.mockRejectedValueOnce(new JMAPSetError('invalidProperties', 'Host is not a public domain name', ['url']));
|
||||
expect(await enablePushFromSettings({ relayBaseUrl: 'https://relay.home.lan/' }))
|
||||
.toMatchObject({ relayBaseUrl: 'https://relay.home.lan', enabled: false });
|
||||
|
||||
// A run that works shows the relay it registered with.
|
||||
expect(await enablePushFromSettings({ relayBaseUrl: 'https://relay.example.com/' }))
|
||||
.toEqual({ error: null, relayBaseUrl: RELAY, enabled: true });
|
||||
});
|
||||
|
||||
it('rejects plain-http relay URLs', async () => {
|
||||
await expect(setupPushNotifications({ relayBaseUrl: 'http://relay.example.com' })).rejects.toMatchObject({ phase: 'relay' });
|
||||
expect(createMock).not.toHaveBeenCalled();
|
||||
|
||||
@@ -1706,6 +1706,40 @@ export async function resyncPushNotifications(
|
||||
return setupPushNotifications(params);
|
||||
}
|
||||
|
||||
export interface PushEnableOutcome {
|
||||
error: unknown;
|
||||
// The relay the settings should show afterwards.
|
||||
relayBaseUrl: string;
|
||||
// Whether the account has a subscription recorded once the run is over.
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* The enable and re-register buttons of the notification settings: run the
|
||||
* setup and say what it left behind. When the mail server refuses a new
|
||||
* relay, the setup goes back to the one in use; the relay field must show
|
||||
* that one again rather than the refused address, since the device list asks
|
||||
* the relay in the field about each device and a revoke goes to it. With no
|
||||
* relay in use the typed one stays, so the user can correct it.
|
||||
*/
|
||||
export async function enablePushFromSettings(params: PushSetupParams): Promise<PushEnableOutcome> {
|
||||
const accountId = jmapClient.username && jmapClient.serverUrl
|
||||
? generateAccountId(jmapClient.username, jmapClient.serverUrl)
|
||||
: null;
|
||||
let error: unknown = null;
|
||||
try {
|
||||
await setupPushNotifications(params);
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
||||
const typed = (params.relayBaseUrl ?? DEFAULT_RELAY_BASE_URL).replace(/\/+$/, '');
|
||||
return {
|
||||
error,
|
||||
relayBaseUrl: (await getStoredRelayBaseUrl()) ?? typed,
|
||||
enabled: accountId ? await isPushEnabledForAccount(accountId) : false,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-apply an account's push types to its subscription after device sync was
|
||||
* turned on or off for it (#34). The account the client serves goes through
|
||||
|
||||
Reference in New Issue
Block a user