mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-04 16:14:07 +00:00
fix: don't wipe a store just because this window can't open it, and don't send a stand-in when a file's upload is held again
This commit is contained in:
@@ -460,6 +460,19 @@ describe('a send the server couldn\'t take right now', () => {
|
||||
expect(row).toMatchObject({ status: 'waiting', copyId: 'copy-1' });
|
||||
});
|
||||
|
||||
it('doesn\'t hand an online send a stand-in for a file the upload held again', async () => {
|
||||
writeEnabled(['acc']);
|
||||
stop = startOfflineMode();
|
||||
const { blobId } = await holdFile('acc', new File(['x'], 'x.txt', { type: 'text/plain' }), 'x.txt');
|
||||
const client = fakeClient('https://mail.example.org', { uploadBlob: vi.fn(async () => ({ blobId: `${LOCAL_BLOB_PREFIX}stand-in`, size: 1, type: 'text/plain' })) });
|
||||
useAuthStore.setState({ client: client as never, activeAccountId: 'acc', getClientForAccount: () => client } as never);
|
||||
try {
|
||||
await expect(getOutboxHooks()!.resolveHeldAttachments(client as never, [{ blobId }])).rejects.toMatchObject({ name: 'SendNotStartedError' });
|
||||
} finally {
|
||||
useAuthStore.setState({ getClientForAccount: useRealClient, client: null });
|
||||
}
|
||||
});
|
||||
|
||||
it('doesn\'t send a stand-in for a file the upload held again', async () => {
|
||||
writeEnabled(['acc']);
|
||||
const { blobId } = await holdFile('acc', new File(['x'], 'x.txt', { type: 'text/plain' }), 'x.txt');
|
||||
@@ -583,13 +596,16 @@ describe('a browser that can\'t run the store', () => {
|
||||
expect(await strandedLegacyMessages()).toBe(0);
|
||||
});
|
||||
|
||||
it('turns the switch off in a window that refuses the private file system', async () => {
|
||||
writeEnabled([]);
|
||||
it('turns the switch off in a window that refuses the private file system, and wipes nothing', async () => {
|
||||
writeEnabled(['acc']);
|
||||
Object.defineProperty(window.navigator, 'storage', { configurable: true, value: { getDirectory: async () => { throw new DOMException('The operation is insecure.', 'SecurityError'); } } });
|
||||
try {
|
||||
stop = startOfflineMode();
|
||||
await vi.waitFor(() => expect(useOfflineStore.getState().storageBlocked).toBe(true));
|
||||
expect(offlineSupported()).toBe(false);
|
||||
enforcePolicy();
|
||||
await new Promise((resolve) => setTimeout(resolve, 20));
|
||||
expect(useOfflineStore.getState().enabled).toEqual(['acc']);
|
||||
} finally {
|
||||
delete (window.navigator as { storage?: unknown }).storage;
|
||||
useOfflineStore.setState({ storageBlocked: false });
|
||||
|
||||
@@ -39,7 +39,7 @@ import { useEmailStore } from '@/stores/email-store';
|
||||
import { usePolicyStore, whenPolicySettled } from '@/stores/policy-store';
|
||||
import { onBeforeSignOut, onFullSignOut } from '@/lib/sign-out-cleanup';
|
||||
import { generateMessageId } from '@/lib/jmap/client';
|
||||
import { setOfflineHooks, type OfflineClientRef, type OfflineHooks, type OfflineMethodCall, type OfflineResponse } from '@/lib/jmap/offline-hooks';
|
||||
import { SendNotStartedError, setOfflineHooks, type OfflineClientRef, type OfflineHooks, type OfflineMethodCall, type OfflineResponse } from '@/lib/jmap/offline-hooks';
|
||||
import type { SendEmailResult } from '@/lib/jmap/types';
|
||||
import { setOutboxHooks, type OutboxHooks } from './outbox-hooks';
|
||||
import {
|
||||
@@ -99,7 +99,8 @@ function hasIndexedDb(): boolean {
|
||||
|
||||
/** Offline mode is possible here at all: not in Lite, with what the store needs. */
|
||||
export function offlineSupported(): boolean {
|
||||
if (IS_LITE || !hasIndexedDb() || useOfflineStore.getState().storageBlocked) return false;
|
||||
if (IS_LITE || !hasIndexedDb()) return false;
|
||||
if (useOfflineStore.getState().storageBlocked) return false;
|
||||
try {
|
||||
return typeof crypto !== 'undefined' && !!crypto.subtle && getStore().supported();
|
||||
} catch {
|
||||
@@ -457,6 +458,8 @@ const outbox: OutboxHooks = {
|
||||
const file = await heldFile(account, attachment.blobId);
|
||||
if (!file) throw Object.assign(new Error('A file attached offline is no longer on this device'), { name: 'OfflineFileMissingError' });
|
||||
const { blobId } = await real.uploadBlob(file);
|
||||
// The server went away just now, and offline mode held the file again.
|
||||
if (isLocalBlobId(blobId)) throw new SendNotStartedError();
|
||||
out.push({ ...attachment, blobId });
|
||||
}
|
||||
return out;
|
||||
@@ -814,7 +817,9 @@ export function enforcePolicy(): void {
|
||||
if (!loaded) return;
|
||||
for (const account of useOfflineStore.getState().enabled) {
|
||||
if (!policyDecided(account)) continue;
|
||||
if (!offlineAllowed(account)) {
|
||||
// What the organisation says, not whether this window can open the store:
|
||||
// a blocked store says nothing against the data in it.
|
||||
if (devicesForAccount(account).noOffline) {
|
||||
// Whoever is here now wasn't signed in to a dropped account.
|
||||
const someoneElses = isEvicted(account);
|
||||
clearHandoffs();
|
||||
|
||||
@@ -462,8 +462,10 @@ describe('sends', () => {
|
||||
|
||||
it('doesn\'t call a try that never started an attempt', async () => {
|
||||
const id = await t.engine.areaCall('outbox.queueSend', { summary, item }) as number;
|
||||
await t.engine.areaCall('outbox.claim', { id });
|
||||
await t.engine.areaCall('outbox.update', { id, status: 'waiting' });
|
||||
for (let i = 0; i < 2; i += 1) {
|
||||
await t.engine.areaCall('outbox.claim', { id });
|
||||
await t.engine.areaCall('outbox.update', { id, status: 'waiting' });
|
||||
}
|
||||
expect((await read()).entries[0].firstAttemptAt).toBeUndefined();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user