mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 00:12:39 +00:00
fix: return to the list after archiving the open message #1458
This commit is contained in:
@@ -0,0 +1,226 @@
|
||||
import React, { Suspense, lazy } from 'react';
|
||||
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import type { Email, Mailbox } from '@/lib/jmap/types';
|
||||
import { useAuthStore } from '@/stores/auth-store';
|
||||
import { useEmailStore } from '@/stores/email-store';
|
||||
import { useSettingsStore } from '@/stores/settings-store';
|
||||
import { usePluginStore } from '@/stores/plugin-store';
|
||||
import { useThemeStore } from '@/stores/theme-store';
|
||||
import { useToastStore } from '@/stores/toast-store';
|
||||
import { DEFAULT_SEARCH_FILTERS } from '@/lib/jmap/search-utils';
|
||||
import { clearAllHooks } from '@/lib/plugin-hooks';
|
||||
import { forgetEmailPrefetches } from '@/lib/email-prefetch';
|
||||
|
||||
// Settings > "Return to list after delete, archive or mark unread": taking
|
||||
// the open message out of the folder goes back to the list, in Mountain View
|
||||
// as in the default layout, instead of opening the next message. (#1458)
|
||||
|
||||
vi.mock('next/dynamic', () => ({ default: (load: () => Promise<React.ComponentType<Record<string, unknown>>>) => {
|
||||
const Component = lazy(async () => ({ default: await load() }));
|
||||
return function Dynamic(props: Record<string, unknown>) {
|
||||
return <Suspense fallback={null}><Component {...props} /></Suspense>;
|
||||
};
|
||||
} }));
|
||||
vi.mock('@/components/email/email-viewer', () => ({
|
||||
EmailViewer: ({ onArchive, onDelete }: { onArchive?: () => void; onDelete?: () => void }) => (
|
||||
<>
|
||||
<button onClick={() => onArchive?.()}>stub archive</button>
|
||||
<button onClick={() => onDelete?.()}>stub delete</button>
|
||||
</>
|
||||
),
|
||||
}));
|
||||
vi.mock('@/components/email/email-composer', () => ({ EmailComposer: () => null }));
|
||||
vi.mock('@/components/layout/sidebar', () => ({ Sidebar: () => null }));
|
||||
vi.mock('@/components/layout/navigation-rail', () => ({ NavigationRail: () => null }));
|
||||
vi.mock('@/components/layout/calendar-event-notification-toaster', () => ({ CalendarEventNotificationToaster: () => null }));
|
||||
vi.mock('@/components/layout/share-notification-toaster', () => ({ ShareNotificationToaster: () => null }));
|
||||
vi.mock('@/components/layout/mobile-header', () => ({ MobileHeader: () => null }));
|
||||
vi.mock('@/components/layout/mv-top-bar', () => ({ MvTopBar: () => null }));
|
||||
vi.mock('@/components/layout/mv-side-panel', () => ({ MvSidePanel: () => null }));
|
||||
vi.mock('@/components/email/message-list-tabs', () => ({ useMessageListPanelProps: (id: string) => ({ id }), MessageListTabs: () => null }));
|
||||
vi.mock('@/components/search/search-box', () => ({ SearchBox: () => null }));
|
||||
vi.mock('@/components/tour/tour-provider', () => ({ useTour: () => ({ activeTour: null, registerStep: vi.fn() }) }));
|
||||
vi.mock('@/components/plugins/plugin-slot', () => ({ PluginSlot: () => null }));
|
||||
vi.mock('@/hooks/use-plugin-slot-offers', () => ({ usePluginSlotOffers: () => [] }));
|
||||
vi.mock('@/hooks/use-config', () => ({ useConfig: () => ({ appName: 'Mail' }) }));
|
||||
vi.mock('@/hooks/use-identity-sync', () => ({ useIdentitySync: () => {} }));
|
||||
vi.mock('@/hooks/use-pro-multi-account-identities', () => ({ useProMultiAccountIdentities: () => [] }));
|
||||
vi.mock('@/hooks/use-pro-multi-account-mailboxes', () => ({ useProMultiAccountMailboxes: () => {} }));
|
||||
vi.mock('@/hooks/use-wopi-status', () => ({ useWopiStatus: () => null, canWopiOpen: () => false }));
|
||||
vi.mock('@/hooks/use-browser-navigation', () => ({ useBrowserNavigation: () => {} }));
|
||||
vi.mock('@/hooks/use-media-query', () => ({
|
||||
useDeviceDetection: () => ({ isMobile: false, isTablet: false, isDesktop: true }),
|
||||
useMediaQuery: () => false,
|
||||
useIsDesktop: () => true,
|
||||
}));
|
||||
vi.mock('@/lib/push-bindings', () => ({ reconcilePushBindings: () => {}, releasePushBindings: () => {} }));
|
||||
vi.mock('@/components/email/email-list', () => ({ EmailList: () => <div>message list</div> }));
|
||||
|
||||
const { MailApp } = await import('../mail-app');
|
||||
|
||||
function mailbox(id: string, role: string): Mailbox {
|
||||
return {
|
||||
id, name: role, role, sortOrder: 0, totalEmails: 3, unreadEmails: 0, totalThreads: 3, unreadThreads: 0, isSubscribed: true,
|
||||
} as Mailbox;
|
||||
}
|
||||
const folders = [mailbox('inbox', 'inbox'), mailbox('archive', 'archive'), mailbox('trash', 'trash')];
|
||||
const row = (id: string, minute: number) => ({
|
||||
id, threadId: `t-${id}`, subject: id, receivedAt: new Date(Date.UTC(2026, 9, 31, 10, minute)).toISOString(),
|
||||
keywords: { $seen: true }, mailboxIds: { inbox: true },
|
||||
}) as unknown as Email;
|
||||
const inbox = () => [row('m1', 30), row('m2', 20), row('m3', 10)];
|
||||
|
||||
let client: Record<string, ReturnType<typeof vi.fn> | (() => unknown)>;
|
||||
let fetchEmailContent: ReturnType<typeof vi.fn>;
|
||||
// Every message the store had open, in order.
|
||||
let opened: (string | null)[];
|
||||
let unsubscribe: () => void;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error('Unexpected network request')));
|
||||
vi.stubGlobal('ResizeObserver', class { observe() {} unobserve() {} disconnect() {} });
|
||||
usePluginStore.setState({ initializePlugins: vi.fn().mockResolvedValue(undefined) });
|
||||
useThemeStore.setState({ syncServerThemes: vi.fn().mockResolvedValue(undefined) });
|
||||
useToastStore.setState({ toasts: [] });
|
||||
clearAllHooks();
|
||||
forgetEmailPrefetches();
|
||||
client = {
|
||||
getAccountId: () => 'acc', getUsername: () => 'a@example.org',
|
||||
getSieveAccountId: () => null, hasDelayedSend: () => false, getSession: () => null,
|
||||
getMailboxes: vi.fn().mockResolvedValue(folders),
|
||||
getThread: vi.fn(async (threadId: string) => ({ id: threadId, emailIds: [threadId.slice(2)] })),
|
||||
getEmail: vi.fn(async (id: string) => ({ ...inbox().find((e) => e.id === id)!, bodyValues: {} })),
|
||||
moveEmail: vi.fn().mockResolvedValue(undefined),
|
||||
moveToTrash: vi.fn().mockResolvedValue(undefined),
|
||||
markAsRead: vi.fn().mockResolvedValue(undefined),
|
||||
};
|
||||
useAuthStore.setState({
|
||||
isAuthenticated: true, isLoading: false, client: client as never, activeAccountId: 'login-a', identities: [],
|
||||
getClientForAccount: (id: string) => (id === 'login-a' ? client : undefined) as never,
|
||||
getAllConnectedClients: () => new Map([['login-a', client]]) as never,
|
||||
});
|
||||
useSettingsStore.setState({
|
||||
enableUnifiedMailbox: false, interfaceLayout: 'default', mailLayout: 'focus', archiveMode: 'single',
|
||||
deleteAction: 'trash', returnToListAfterAction: true, confirmArchive: false, confirmDelete: false,
|
||||
singleKeyShortcuts: true,
|
||||
} as never);
|
||||
fetchEmailContent = vi.fn().mockResolvedValue(undefined);
|
||||
const emails = inbox();
|
||||
useEmailStore.setState({
|
||||
emails, selectedEmail: emails[0], selectedMailbox: 'inbox', selectedKeyword: null, mailboxes: folders,
|
||||
viewingAccountId: null, accountMailboxes: {}, isUnifiedView: false, unifiedRole: null,
|
||||
isScheduledView: false, isLoadingEmail: false, isLoading: false, searchQuery: '',
|
||||
searchFilters: { ...DEFAULT_SEARCH_FILTERS }, searchMailboxId: '',
|
||||
selectedEmailKeys: new Set(), unreadSection: null,
|
||||
fetchMailboxes: vi.fn().mockResolvedValue(undefined), fetchEmails: vi.fn().mockResolvedValue(undefined),
|
||||
fetchQuota: vi.fn().mockResolvedValue(undefined), fetchTagCounts: vi.fn().mockResolvedValue(undefined),
|
||||
refreshScheduledMetadata: vi.fn().mockResolvedValue(undefined),
|
||||
fetchEmailContent,
|
||||
} as never);
|
||||
opened = [];
|
||||
unsubscribe = useEmailStore.subscribe((state, prev) => {
|
||||
if (state.selectedEmail !== prev.selectedEmail) opened.push(state.selectedEmail?.id ?? null);
|
||||
});
|
||||
});
|
||||
afterEach(() => {
|
||||
unsubscribe();
|
||||
vi.restoreAllMocks();
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
const selectedId = () => useEmailStore.getState().selectedEmail?.id ?? null;
|
||||
|
||||
describe.each([
|
||||
['Mountain View', { interfaceLayout: 'mountain-view' }],
|
||||
['the default layout', { interfaceLayout: 'default' }],
|
||||
])('Archive on the open message in %s', (_name, layout) => {
|
||||
beforeEach(() => useSettingsStore.setState(layout as never));
|
||||
|
||||
it('goes back to the list without opening the next message', async () => {
|
||||
render(<MailApp />);
|
||||
fireEvent.click(await screen.findByText('stub archive', {}, { timeout: 5000 }));
|
||||
await waitFor(() => expect(client.moveEmail).toHaveBeenCalledWith('m1', 'archive', undefined));
|
||||
await waitFor(() => expect(useEmailStore.getState().emails.map((e) => e.id)).toEqual(['m2', 'm3']));
|
||||
expect(selectedId()).toBeNull();
|
||||
expect(opened).not.toContain('m2');
|
||||
expect(fetchEmailContent).not.toHaveBeenCalledWith(expect.anything(), 'm2');
|
||||
});
|
||||
|
||||
it('opens the next message with the setting off', async () => {
|
||||
useSettingsStore.setState({ returnToListAfterAction: false } as never);
|
||||
render(<MailApp />);
|
||||
fireEvent.click(await screen.findByText('stub archive', {}, { timeout: 5000 }));
|
||||
await waitFor(() => expect(selectedId()).toBe('m2'));
|
||||
});
|
||||
|
||||
it('stays on the message when the archive confirm is cancelled', async () => {
|
||||
useSettingsStore.setState({ confirmArchive: true } as never);
|
||||
render(<MailApp />);
|
||||
fireEvent.click(await screen.findByText('stub archive', {}, { timeout: 5000 }));
|
||||
const dialog = await screen.findByRole('alertdialog');
|
||||
fireEvent.keyDown(dialog, { key: 'Escape' });
|
||||
await waitFor(() => expect(screen.queryByRole('alertdialog')).toBeNull());
|
||||
expect(selectedId()).toBe('m1');
|
||||
expect(client.moveEmail).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe.each([
|
||||
['Mountain View', { interfaceLayout: 'mountain-view' }],
|
||||
['the default layout', { interfaceLayout: 'default' }],
|
||||
])('Delete on the open message in %s', (_name, layout) => {
|
||||
beforeEach(() => useSettingsStore.setState(layout as never));
|
||||
|
||||
it('goes back to the list without opening the next message', async () => {
|
||||
render(<MailApp />);
|
||||
fireEvent.click(await screen.findByText('stub delete', {}, { timeout: 5000 }));
|
||||
await waitFor(() => expect(client.moveToTrash).toHaveBeenCalled());
|
||||
await waitFor(() => expect(useEmailStore.getState().emails.map((e) => e.id)).toEqual(['m2', 'm3']));
|
||||
expect(selectedId()).toBeNull();
|
||||
expect(opened).not.toContain('m2');
|
||||
});
|
||||
|
||||
it('stays on the message when the delete confirm is cancelled', async () => {
|
||||
useSettingsStore.setState({ confirmDelete: true } as never);
|
||||
render(<MailApp />);
|
||||
fireEvent.click(await screen.findByText('stub delete', {}, { timeout: 5000 }));
|
||||
const dialog = await screen.findByRole('alertdialog');
|
||||
fireEvent.keyDown(dialog, { key: 'Escape' });
|
||||
await waitFor(() => expect(screen.queryByRole('alertdialog')).toBeNull());
|
||||
expect(selectedId()).toBe('m1');
|
||||
expect(client.moveToTrash).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('The e shortcut in Mountain View', () => {
|
||||
beforeEach(() => {
|
||||
useSettingsStore.setState({ interfaceLayout: 'mountain-view' } as never);
|
||||
useEmailStore.setState({ selectedEmail: null } as never);
|
||||
});
|
||||
|
||||
const press = (key: string) => act(() => { fireEvent.keyDown(window, { key }); });
|
||||
|
||||
it('moves the list cursor on to the next row', async () => {
|
||||
render(<MailApp />);
|
||||
await screen.findByText('message list', {}, { timeout: 5000 });
|
||||
await press('j');
|
||||
await waitFor(() => expect(selectedId()).toBe('m1'));
|
||||
await press('e');
|
||||
await waitFor(() => expect(client.moveEmail).toHaveBeenCalledWith('m1', 'archive', undefined));
|
||||
await waitFor(() => expect(selectedId()).toBe('m2'));
|
||||
});
|
||||
|
||||
it('goes back to the list from a message opened with Enter', async () => {
|
||||
render(<MailApp />);
|
||||
await screen.findByText('message list', {}, { timeout: 5000 });
|
||||
await press('j');
|
||||
await waitFor(() => expect(selectedId()).toBe('m1'));
|
||||
await press('Enter');
|
||||
await press('e');
|
||||
await waitFor(() => expect(client.moveEmail).toHaveBeenCalledWith('m1', 'archive', undefined));
|
||||
await waitFor(() => expect(useEmailStore.getState().emails.map((e) => e.id)).toEqual(['m2', 'm3']));
|
||||
expect(selectedId()).toBeNull();
|
||||
expect(opened).not.toContain('m2');
|
||||
});
|
||||
});
|
||||
@@ -1168,7 +1168,7 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
|
||||
}
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}), [activeEmails, selectedEmail, client, selectedMailbox, isMobile, isTablet, selectedEmailKeys, mailboxes, isScheduledView, mvShell, showCrossStarred, isUnifiedView, unifiedRole, archiveActionEnabled]);
|
||||
}), [activeEmails, selectedEmail, client, selectedMailbox, isMobile, isTablet, selectedEmailKeys, mailboxes, isScheduledView, mvShell, cursorOnly, showCrossStarred, isUnifiedView, unifiedRole, archiveActionEnabled]);
|
||||
|
||||
// Initialize keyboard shortcuts
|
||||
// Mountain View keeps the ticked rows while a message is read (#1231). The
|
||||
@@ -2918,6 +2918,18 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
|
||||
clearSelection(); // only on success - a vetoed/no-op attempt keeps the selection
|
||||
};
|
||||
|
||||
// Deleting or archiving the open message returns to the list (Gmail-style),
|
||||
// not the next email, unless the user turned the setting off. Called once
|
||||
// the action is confirmed and right before the store removes the row: with
|
||||
// nothing selected its remove-and-advance opens nothing, so the next message
|
||||
// is not loaded and marked read on the way. A Mountain View cursor is no
|
||||
// open message and moves on to the next row. (#1458)
|
||||
const leaveIfOpen = (email: Email) => {
|
||||
const open = useEmailStore.getState().selectedEmail;
|
||||
if (!open || emailKeyFor(open) !== emailKeyFor(email) || (mvShell && cursorOnly)) return;
|
||||
if (useSettingsStore.getState().returnToListAfterAction) handleMobileBack();
|
||||
};
|
||||
|
||||
const handleDelete = async (emailToDelete: Email | null = selectedEmail) => {
|
||||
if (!client || !emailToDelete) return;
|
||||
|
||||
@@ -2959,6 +2971,8 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
|
||||
return;
|
||||
}
|
||||
|
||||
leaveIfOpen(emailToDelete);
|
||||
|
||||
// The store's delete reads the "Delete action" setting: move to the
|
||||
// email's own account's trash (marking it read when asked), or destroy
|
||||
// it. In Trash it always destroys. (#281 for the account routing)
|
||||
@@ -3023,11 +3037,9 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
|
||||
});
|
||||
if (!archiveMailbox) return; // a plugin refused the Archive folder
|
||||
|
||||
let archived: boolean;
|
||||
let restore: EmailRestoreBatch[] | null = null;
|
||||
const keepRestore = (batches: EmailRestoreBatch[]) => { restore = batches; };
|
||||
let destination: Mailbox;
|
||||
if (archiveMode === 'single') {
|
||||
archived = await moveThreadToMailbox(client, emailToArchive.id, archiveMailbox.id, emailKeyFor(emailToArchive), keepRestore);
|
||||
destination = archiveMailbox;
|
||||
} else {
|
||||
const emailDate = new Date(emailToArchive.receivedAt);
|
||||
const year = emailDate.getFullYear().toString();
|
||||
@@ -3052,7 +3064,7 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
|
||||
}
|
||||
|
||||
if (archiveMode === 'year') {
|
||||
archived = await moveThreadToMailbox(client, emailToArchive.id, yearMailbox.id, emailKeyFor(emailToArchive), keepRestore);
|
||||
destination = yearMailbox;
|
||||
} else {
|
||||
const yearId = yearMailbox.originalId || yearMailbox.id;
|
||||
const afterYear = readMailboxes();
|
||||
@@ -3062,9 +3074,13 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
|
||||
if (!monthMailbox) return; // a plugin refused the folder
|
||||
await refreshMailboxes();
|
||||
}
|
||||
archived = await moveThreadToMailbox(client, emailToArchive.id, monthMailbox.id, emailKeyFor(emailToArchive), keepRestore);
|
||||
destination = monthMailbox;
|
||||
}
|
||||
}
|
||||
|
||||
let restore: EmailRestoreBatch[] | null = null;
|
||||
leaveIfOpen(emailToArchive);
|
||||
const archived = await moveThreadToMailbox(client, emailToArchive.id, destination.id, emailKeyFor(emailToArchive), (batches) => { restore = batches; });
|
||||
if (!archived) return;
|
||||
|
||||
if (conversationThread && conversationThread.threadKey === threadKeyFor(emailToArchive)) {
|
||||
@@ -5499,17 +5515,7 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
|
||||
onReplyAll={handleReplyAll}
|
||||
onForward={handleForward}
|
||||
onForwardAsAttachment={handleForwardAsAttachment}
|
||||
onDelete={() => {
|
||||
// Deleting the open message returns to the list (Gmail-style),
|
||||
// not the next email — unless the user turned the setting off.
|
||||
// Deselect first so the store's remove-and-advance sees no
|
||||
// selection and doesn't auto-open the next message.
|
||||
const target = selectedEmail;
|
||||
if (useSettingsStore.getState().returnToListAfterAction) {
|
||||
handleMobileBack();
|
||||
}
|
||||
handleDelete(target);
|
||||
}}
|
||||
onDelete={() => handleDelete()}
|
||||
onArchive={archiveActionEnabled ? () => handleArchive() : undefined}
|
||||
onToggleStar={handleToggleStar}
|
||||
onTogglePinned={() => { if (selectedEmail) void handleTogglePinned(selectedEmail); }}
|
||||
|
||||
@@ -2016,8 +2016,8 @@
|
||||
"unsupported": "الحساب الحالي لا يعلن عن دعم الإرسال المؤجل. لا يزال الإعداد محفوظًا للحسابات الأخرى."
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "العودة إلى القائمة بعد الحذف أو التمييز كغير مقروء",
|
||||
"description": "بعد حذف الرسالة المفتوحة أو تمييزها كغير مقروءة، العودة إلى قائمة الرسائل بدلًا من فتح الرسالة التالية."
|
||||
"label": "العودة إلى القائمة بعد الحذف أو الأرشفة أو التمييز كغير مقروء",
|
||||
"description": "بعد حذف الرسالة المفتوحة أو أرشفتها أو تمييزها كغير مقروءة، العودة إلى قائمة الرسائل بدلًا من فتح الرسالة التالية."
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "إجراء السحب لليمين (الجوال)",
|
||||
|
||||
@@ -2016,8 +2016,8 @@
|
||||
"unsupported": "El compte actual no indica compatibilitat amb l'enviament retardat. La configuració es continua desant per a altres comptes."
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "Torna a la llista després de suprimir o marcar com a no llegit",
|
||||
"description": "Després de suprimir o marcar com a no llegit el missatge obert, torna a la llista de missatges en lloc d'obrir el següent."
|
||||
"label": "Torna a la llista després de suprimir, arxivar o marcar com a no llegit",
|
||||
"description": "Després de suprimir, arxivar o marcar com a no llegit el missatge obert, torna a la llista de missatges en lloc d'obrir el següent."
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "Acció en lliscar a la dreta (mòbil)",
|
||||
|
||||
@@ -2013,8 +2013,8 @@
|
||||
"unsupported": "Aktuální účet neoznamuje podporu odloženého odeslání. Nastavení zůstane uloženo pro jiné účty."
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "Návrat do seznamu po smazání nebo označení jako nepřečtené",
|
||||
"description": "Po smazání nebo označení otevřené zprávy jako nepřečtené se vrátit do seznamu zpráv místo otevření následující zprávy."
|
||||
"label": "Návrat do seznamu po smazání, archivaci nebo označení jako nepřečtené",
|
||||
"description": "Po smazání, archivaci nebo označení otevřené zprávy jako nepřečtené se vrátit do seznamu zpráv místo otevření následující zprávy."
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "Akce při přejetí doprava (mobil)",
|
||||
|
||||
@@ -2016,8 +2016,8 @@
|
||||
"unsupported": "Den aktuelle konto annoncerer ikke understøttelse af forsinket afsendelse. Indstillingen gemmes stadig for andre konti."
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "Vend tilbage til listen efter sletning eller markering som ulæst",
|
||||
"description": "Efter sletning eller markering af den åbne besked som ulæst vendes der tilbage til beskedlisten i stedet for at åbne den næste besked."
|
||||
"label": "Vend tilbage til listen efter sletning, arkivering eller markering som ulæst",
|
||||
"description": "Efter sletning, arkivering eller markering af den åbne besked som ulæst vendes der tilbage til beskedlisten i stedet for at åbne den næste besked."
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "Handling ved strygning til højre (mobil)",
|
||||
|
||||
@@ -2013,8 +2013,8 @@
|
||||
"unsupported": "Das aktuelle Konto meldet keine Unterstützung für verzögertes Senden. Die Einstellung bleibt für andere Konten gespeichert."
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "Nach Löschen oder Ungelesen-Markieren zur Liste zurückkehren",
|
||||
"description": "Nach dem Löschen oder Als-ungelesen-Markieren der geöffneten E-Mail zurück zur Liste, statt die nächste E-Mail zu öffnen."
|
||||
"label": "Nach Löschen, Archivieren oder Ungelesen-Markieren zur Liste zurückkehren",
|
||||
"description": "Nach dem Löschen, Archivieren oder Als-ungelesen-Markieren der geöffneten E-Mail zurück zur Liste, statt die nächste E-Mail zu öffnen."
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "Aktion beim Wischen nach rechts (mobil)",
|
||||
|
||||
@@ -2016,8 +2016,8 @@
|
||||
"unsupported": "The current account does not advertise delayed-send support. The setting is still saved for other accounts."
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "Return to list after delete or mark unread",
|
||||
"description": "After deleting or marking the open message unread, go back to the message list instead of opening the next message."
|
||||
"label": "Return to list after delete, archive or mark unread",
|
||||
"description": "After deleting, archiving or marking the open message unread, go back to the message list instead of opening the next message."
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "Swipe right action (mobile)",
|
||||
|
||||
@@ -2013,8 +2013,8 @@
|
||||
"unsupported": "La cuenta actual no anuncia compatibilidad con envío demorado. La opción seguirá guardada para otras cuentas."
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "Volver a la lista tras eliminar o marcar como no leído",
|
||||
"description": "Después de eliminar o marcar como no leído el mensaje abierto, volver a la lista de mensajes en lugar de abrir el mensaje siguiente."
|
||||
"label": "Volver a la lista tras eliminar, archivar o marcar como no leído",
|
||||
"description": "Después de eliminar, archivar o marcar como no leído el mensaje abierto, volver a la lista de mensajes en lugar de abrir el mensaje siguiente."
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "Acción al deslizar a la derecha (móvil)",
|
||||
|
||||
@@ -2016,8 +2016,8 @@
|
||||
"unsupported": "Praegune konto ei toeta viivitatud saatmist. Seade salvestatakse siiski teiste kontode jaoks."
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "Naase loendisse pärast kustutamist või lugemata märkimist",
|
||||
"description": "Pärast avatud kirja kustutamist või lugemata märkimist naase kirjade loendisse, selle asemel et avada järgmine kiri."
|
||||
"label": "Naase loendisse pärast kustutamist, arhiveerimist või lugemata märkimist",
|
||||
"description": "Pärast avatud kirja kustutamist, arhiveerimist või lugemata märkimist naase kirjade loendisse, selle asemel et avada järgmine kiri."
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "Paremale pühkimise toiming (mobiil)",
|
||||
|
||||
@@ -2016,8 +2016,8 @@
|
||||
"unsupported": "پشتیبانی نمیشود"
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "بازگشت به فهرست پس از حذف یا علامتگذاری بهعنوان خواندهنشده",
|
||||
"description": "پس از حذف یا علامتگذاری پیام باز بهعنوان خواندهنشده، بهجای باز کردن پیام بعدی به فهرست پیامها بازگردید."
|
||||
"label": "بازگشت به فهرست پس از حذف، بایگانی یا علامتگذاری بهعنوان خواندهنشده",
|
||||
"description": "پس از حذف، بایگانی یا علامتگذاری پیام باز بهعنوان خواندهنشده، بهجای باز کردن پیام بعدی به فهرست پیامها بازگردید."
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "عملکرد کشیدن به راست (موبایل)",
|
||||
|
||||
@@ -2013,8 +2013,8 @@
|
||||
"unsupported": "Le compte actuel n’annonce pas la prise en charge de l’envoi différé. Le réglage reste enregistré pour d’autres comptes."
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "Revenir à la liste après suppression ou marquage comme non lu",
|
||||
"description": "Après avoir supprimé ou marqué comme non lu le message ouvert, revenir à la liste des messages au lieu d'ouvrir le message suivant."
|
||||
"label": "Revenir à la liste après suppression, archivage ou marquage comme non lu",
|
||||
"description": "Après avoir supprimé, archivé ou marqué comme non lu le message ouvert, revenir à la liste des messages au lieu d'ouvrir le message suivant."
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "Action du balayage vers la droite (mobile)",
|
||||
|
||||
@@ -1978,8 +1978,8 @@
|
||||
"unsupported": "החשבון הנוכחי אינו פרסום תמיכה בשליחה מעוכבת. ההגדרה עדיין שמורה לחשבונות אחרים."
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "חזור לרשימה לאחר מחיקה או סימון שלא נקראו",
|
||||
"description": "לאחר מחיקה או סימון ההודעה הפתוחה כלא קרויה, חזור לרשימת ההודעות במקום לפתוח את ההודעה הבאה."
|
||||
"label": "חזור לרשימה לאחר מחיקה, העברה לארכיון או סימון שלא נקראו",
|
||||
"description": "לאחר מחיקה, העברה לארכיון או סימון ההודעה הפתוחה כלא קרויה, חזור לרשימת ההודעות במקום לפתוח את ההודעה הבאה."
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "פעולת החלקה ימינה (נייד)",
|
||||
|
||||
@@ -2016,8 +2016,8 @@
|
||||
"unsupported": "A jelenlegi fiók nem támogatja a késleltetett küldést. A beállítás más fiókokhoz elmentésre kerül."
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "Visszatérés a listához törlés vagy olvasatlanként jelölés után",
|
||||
"description": "A megnyitott üzenet törlése vagy olvasatlanként való megjelölése után visszatérés az üzenetlistához a következő üzenet megnyitása helyett."
|
||||
"label": "Visszatérés a listához törlés, archiválás vagy olvasatlanként jelölés után",
|
||||
"description": "A megnyitott üzenet törlése, archiválása vagy olvasatlanként való megjelölése után visszatérés az üzenetlistához a következő üzenet megnyitása helyett."
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "Jobbra húzás művelete (mobil)",
|
||||
|
||||
@@ -2013,8 +2013,8 @@
|
||||
"unsupported": "L’account corrente non dichiara il supporto all’invio ritardato. L’impostazione resta salvata per altri account."
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "Torna all'elenco dopo l'eliminazione o il contrassegno come non letto",
|
||||
"description": "Dopo aver eliminato o segnato come non letto il messaggio aperto, torna all'elenco dei messaggi invece di aprire il messaggio successivo."
|
||||
"label": "Torna all'elenco dopo l'eliminazione, l'archiviazione o il contrassegno come non letto",
|
||||
"description": "Dopo aver eliminato, archiviato o segnato come non letto il messaggio aperto, torna all'elenco dei messaggi invece di aprire il messaggio successivo."
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "Azione per lo scorrimento a destra (mobile)",
|
||||
|
||||
@@ -2013,8 +2013,8 @@
|
||||
"unsupported": "現在のアカウントは遅延送信のサポートを通知していません。この設定は他のアカウント用に保存されます。"
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "削除または未読にした後に一覧へ戻る",
|
||||
"description": "開いているメッセージを削除または未読にした後、次のメッセージを開かずにメッセージ一覧へ戻ります。"
|
||||
"label": "削除、アーカイブ、または未読にした後に一覧へ戻る",
|
||||
"description": "開いているメッセージを削除、アーカイブ、または未読にした後、次のメッセージを開かずにメッセージ一覧へ戻ります。"
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "右スワイプの操作(モバイル)",
|
||||
|
||||
@@ -2013,8 +2013,8 @@
|
||||
"unsupported": "현재 계정은 지연 보내기 지원을 알리지 않습니다. 설정은 다른 계정을 위해 계속 저장됩니다."
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "삭제 또는 읽지 않음으로 표시 후 목록으로 돌아가기",
|
||||
"description": "열려 있는 메시지를 삭제하거나 읽지 않음으로 표시한 후 다음 메시지를 열지 않고 메시지 목록으로 돌아갑니다."
|
||||
"label": "삭제, 보관 또는 읽지 않음으로 표시 후 목록으로 돌아가기",
|
||||
"description": "열려 있는 메시지를 삭제하거나 보관하거나 읽지 않음으로 표시한 후 다음 메시지를 열지 않고 메시지 목록으로 돌아갑니다."
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "오른쪽으로 스와이프 동작 (모바일)",
|
||||
|
||||
@@ -2013,8 +2013,8 @@
|
||||
"unsupported": "Pašreizējais konts neziņo par aizturētas sūtīšanas atbalstu. Iestatījums joprojām tiks saglabāts citiem kontiem."
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "Atgriezties sarakstā pēc dzēšanas vai atzīmēšanas kā nelasītu",
|
||||
"description": "Pēc atvērtā ziņojuma dzēšanas vai atzīmēšanas kā nelasīta atgriezieties ziņojumu sarakstā, nevis atveriet nākamo ziņojumu."
|
||||
"label": "Atgriezties sarakstā pēc dzēšanas, arhivēšanas vai atzīmēšanas kā nelasītu",
|
||||
"description": "Pēc atvērtā ziņojuma dzēšanas, arhivēšanas vai atzīmēšanas kā nelasīta atgriezieties ziņojumu sarakstā, nevis atveriet nākamo ziņojumu."
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "Darbība, velkot pa labi (mobilajā ierīcē)",
|
||||
|
||||
@@ -2016,8 +2016,8 @@
|
||||
"unsupported": "Харин данс нь хойшлуулсан-илгээлтийн дэмжлэгийг сурталчилдаггүй. Тохиргоог бусад бүртгэлд хадгалсан хэвээр байна."
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "Устгасны дараа жагсаалт руу буцах эсвэл уншаагүй гэж тэмдэглэнэ",
|
||||
"description": "Нээлттэй мессежийг устгасан эсвэл уншаагүй гэж тэмдэглэсний дараа дараагийн мессежийг нээхийн оронд мессежийн жагсаалт руу буцна уу."
|
||||
"label": "Устгах, архивлах эсвэл уншаагүй гэж тэмдэглэсний дараа жагсаалт руу буцах",
|
||||
"description": "Нээлттэй мессежийг устгасан, архивласан эсвэл уншаагүй гэж тэмдэглэсний дараа дараагийн мессежийг нээхийн оронд мессежийн жагсаалт руу буцна уу."
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "Баруун тийш шудрах үйлдэл (гар утас)",
|
||||
|
||||
@@ -2016,8 +2016,8 @@
|
||||
"unsupported": "Denne kontoen støtter ikke forsinket sending. Innstillingen lagres likevel for andre kontoer."
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "Gå tilbake til listen etter sletting eller merking som ulest",
|
||||
"description": "Gå tilbake til meldingslisten i stedet for å åpne neste melding etter at du har slettet den åpne meldingen eller merket den som ulest."
|
||||
"label": "Gå tilbake til listen etter sletting, arkivering eller merking som ulest",
|
||||
"description": "Gå tilbake til meldingslisten i stedet for å åpne neste melding etter at du har slettet eller arkivert den åpne meldingen eller merket den som ulest."
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "Handling ved sveip mot høyre (mobil)",
|
||||
|
||||
@@ -2013,8 +2013,8 @@
|
||||
"unsupported": "Het huidige account meldt geen ondersteuning voor vertraagd verzenden. De instelling blijft opgeslagen voor andere accounts."
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "Terug naar lijst na verwijderen of markeren als ongelezen",
|
||||
"description": "Na het verwijderen of als ongelezen markeren van het geopende bericht terugkeren naar de berichtenlijst in plaats van het volgende bericht te openen."
|
||||
"label": "Terug naar lijst na verwijderen, archiveren of markeren als ongelezen",
|
||||
"description": "Na het verwijderen, archiveren of als ongelezen markeren van het geopende bericht terugkeren naar de berichtenlijst in plaats van het volgende bericht te openen."
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "Actie bij naar rechts vegen (mobiel)",
|
||||
|
||||
@@ -2013,8 +2013,8 @@
|
||||
"unsupported": "Bieżące konto nie zgłasza obsługi opóźnionej wysyłki. Ustawienie pozostanie zapisane dla innych kont."
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "Powrót do listy po usunięciu lub oznaczeniu jako nieprzeczytane",
|
||||
"description": "Po usunięciu lub oznaczeniu otwartej wiadomości jako nieprzeczytanej wróć do listy wiadomości zamiast otwierać następną."
|
||||
"label": "Powrót do listy po usunięciu, zarchiwizowaniu lub oznaczeniu jako nieprzeczytane",
|
||||
"description": "Po usunięciu, zarchiwizowaniu lub oznaczeniu otwartej wiadomości jako nieprzeczytanej wróć do listy wiadomości zamiast otwierać następną."
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "Akcja przy przesunięciu w prawo (mobilne)",
|
||||
|
||||
@@ -2013,8 +2013,8 @@
|
||||
"unsupported": "A conta atual não anuncia suporte a envio atrasado. A configuração continuará salva para outras contas."
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "Voltar à lista após eliminar ou marcar como não lido",
|
||||
"description": "Depois de eliminar ou marcar como não lida a mensagem aberta, voltar à lista de mensagens em vez de abrir a mensagem seguinte."
|
||||
"label": "Voltar à lista após eliminar, arquivar ou marcar como não lido",
|
||||
"description": "Depois de eliminar, arquivar ou marcar como não lida a mensagem aberta, voltar à lista de mensagens em vez de abrir a mensagem seguinte."
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "Ação ao deslizar para a direita (celular)",
|
||||
|
||||
@@ -2016,8 +2016,8 @@
|
||||
"unsupported": "Contul curent nu indică faptul că acceptă trimiterea amânată. Setarea este totuși salvată pentru alte conturi."
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "Revenire la listă după ștergere sau marcare ca necitit",
|
||||
"description": "După ștergerea sau marcarea mesajului deschis ca necitit, reveniți la lista de mesaje în loc să deschideți mesajul următor."
|
||||
"label": "Revenire la listă după ștergere, arhivare sau marcare ca necitit",
|
||||
"description": "După ștergerea, arhivarea sau marcarea mesajului deschis ca necitit, reveniți la lista de mesaje în loc să deschideți mesajul următor."
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "Acțiune la glisare spre dreapta (mobil)",
|
||||
|
||||
@@ -2013,8 +2013,8 @@
|
||||
"unsupported": "Текущая учетная запись не заявляет поддержку отложенной отправки. Настройка сохранится для других учетных записей."
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "Возврат к списку после удаления или отметки как непрочитанного",
|
||||
"description": "После удаления или отметки открытого письма как непрочитанного возвращаться к списку писем вместо открытия следующего."
|
||||
"label": "Возврат к списку после удаления, архивации или отметки как непрочитанного",
|
||||
"description": "После удаления, архивации или отметки открытого письма как непрочитанного возвращаться к списку писем вместо открытия следующего."
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "Действие при свайпе вправо (мобильные)",
|
||||
|
||||
@@ -2016,8 +2016,8 @@
|
||||
"unsupported": "Aktuálny účet neoznamuje podporu oneskoreného odoslania."
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "Vrátiť sa na zoznam po odstránení alebo označení ako neprečítané",
|
||||
"description": "Po odstránení alebo označení otvorenej správy ako neprečítanej sa vrátiť na zoznam správ."
|
||||
"label": "Vrátiť sa na zoznam po odstránení, archivovaní alebo označení ako neprečítané",
|
||||
"description": "Po odstránení, archivovaní alebo označení otvorenej správy ako neprečítanej sa vrátiť na zoznam správ."
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "Akcia pri potiahnutí doprava (mobil)",
|
||||
|
||||
@@ -4453,8 +4453,8 @@
|
||||
"unsupported": "บัญชีปัจจุบันไม่รองรับการส่งแบบหน่วงเวลา การตั้งค่านี้จะยังคงถูกบันทึกไว้สำหรับบัญชีอื่น"
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "กลับไปยังรายการหลังจากลบหรือทำเครื่องหมายว่ายังไม่ได้อ่าน",
|
||||
"description": "หลังจากลบหรือทำเครื่องหมายข้อความที่เปิดอยู่ว่ายังไม่ได้อ่าน ให้กลับไปยังรายการข้อความแทนการเปิดข้อความถัดไป"
|
||||
"label": "กลับไปยังรายการหลังจากลบ เก็บถาวร หรือทำเครื่องหมายว่ายังไม่ได้อ่าน",
|
||||
"description": "หลังจากลบ เก็บถาวร หรือทำเครื่องหมายข้อความที่เปิดอยู่ว่ายังไม่ได้อ่าน ให้กลับไปยังรายการข้อความแทนการเปิดข้อความถัดไป"
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "การดำเนินการเมื่อปัดไปทางขวา (มือถือ)",
|
||||
|
||||
@@ -2013,8 +2013,8 @@
|
||||
"unsupported": "Geçerli hesap gecikmeli gönderim desteği bildirmiyor. Ayar diğer hesaplar için kaydedilmeye devam eder."
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "Silme veya okunmadı olarak işaretlemeden sonra listeye dön",
|
||||
"description": "Açık iletiyi sildikten veya okunmadı olarak işaretledikten sonra, sonraki iletiyi açmak yerine ileti listesine dön."
|
||||
"label": "Silme, arşivleme veya okunmadı olarak işaretlemeden sonra listeye dön",
|
||||
"description": "Açık iletiyi sildikten, arşivledikten veya okunmadı olarak işaretledikten sonra, sonraki iletiyi açmak yerine ileti listesine dön."
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "Sağa kaydırma eylemi (mobil)",
|
||||
|
||||
@@ -2013,8 +2013,8 @@
|
||||
"unsupported": "Поточний обліковий запис не повідомляє про підтримку відкладеного надсилання. Налаштування залишиться збереженим для інших облікових записів."
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "Повернення до списку після видалення або позначення як непрочитане",
|
||||
"description": "Після видалення або позначення відкритого листа як непрочитаного повертатися до списку листів замість відкриття наступного."
|
||||
"label": "Повернення до списку після видалення, архівування або позначення як непрочитане",
|
||||
"description": "Після видалення, архівування або позначення відкритого листа як непрочитаного повертатися до списку листів замість відкриття наступного."
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "Дія при свайпі вправо (мобільні)",
|
||||
|
||||
@@ -2016,8 +2016,8 @@
|
||||
"unsupported": "目前帳號未宣告支援延遲傳送。該設定仍會為其他帳號儲存。"
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "刪除或標示為未讀後回到清單",
|
||||
"description": "刪除開啟的郵件或標示為未讀後,回到郵件清單,而不是開啟下一封郵件。"
|
||||
"label": "刪除、封存或標示為未讀後回到清單",
|
||||
"description": "刪除、封存開啟的郵件或標示為未讀後,回到郵件清單,而不是開啟下一封郵件。"
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "向右滑動動作(行動裝置)",
|
||||
|
||||
@@ -2013,8 +2013,8 @@
|
||||
"unsupported": "当前账户未声明支持延迟发送。该设置仍会为其他账户保存。"
|
||||
},
|
||||
"return_to_list_after_action": {
|
||||
"label": "删除或标记为未读后返回列表",
|
||||
"description": "删除打开的邮件或将其标记为未读后,返回邮件列表而不是打开下一封邮件。"
|
||||
"label": "删除、归档或标记为未读后返回列表",
|
||||
"description": "删除、归档打开的邮件或将其标记为未读后,返回邮件列表而不是打开下一封邮件。"
|
||||
},
|
||||
"swipe_right_action": {
|
||||
"label": "右滑操作(移动端)",
|
||||
|
||||
@@ -545,7 +545,7 @@ interface SettingsState extends AccessibilitySettings {
|
||||
deleteAction: DeleteAction;
|
||||
permanentlyDeleteJunk: boolean; // Permanently delete emails from junk/spam instead of moving to trash
|
||||
confirmBulkDelete: boolean; // Ask before moving several selected emails to Trash (permanent deletes always ask)
|
||||
returnToListAfterAction: boolean; // After delete / mark-unread in an open message, return to the list instead of opening the next message
|
||||
returnToListAfterAction: boolean; // After delete / archive / mark-unread in an open message, return to the list instead of opening the next message
|
||||
clearSearchOnFolderChange: boolean; // Reset the search query + advanced filters when switching folders, instead of re-running the search in the newly selected folder (#553 keeps it applied when this is off)
|
||||
showPreview: boolean;
|
||||
showVerificationCodes: boolean; // Offer the one-time code of a sign-in mail as a copy chip in the list and the reader
|
||||
|
||||
Reference in New Issue
Block a user