feat: replace folder prompt() calls with proper modal dialog

This commit is contained in:
Linus Rath
2026-04-25 16:30:18 +02:00
parent 4788e8a91a
commit 29197ea355
17 changed files with 338 additions and 63 deletions
+29 -9
View File
@@ -24,6 +24,7 @@ import { useDeviceDetection } from "@/hooks/use-media-query";
import { useKeyboardShortcuts } from "@/hooks/use-keyboard-shortcuts"; import { useKeyboardShortcuts } from "@/hooks/use-keyboard-shortcuts";
import { useRefreshGesture } from "@/hooks/use-refresh-gesture"; import { useRefreshGesture } from "@/hooks/use-refresh-gesture";
import { useConfirmDialog } from "@/hooks/use-confirm-dialog"; import { useConfirmDialog } from "@/hooks/use-confirm-dialog";
import { usePromptDialog } from "@/hooks/use-prompt-dialog";
import { useBrowserNavigation, type NavSnapshot } from "@/hooks/use-browser-navigation"; import { useBrowserNavigation, type NavSnapshot } from "@/hooks/use-browser-navigation";
import { debug } from "@/lib/debug"; import { debug } from "@/lib/debug";
import { playNotificationSound } from "@/lib/notification-sound"; import { playNotificationSound } from "@/lib/notification-sound";
@@ -36,6 +37,7 @@ import {
ComposerErrorFallback, ComposerErrorFallback,
} from "@/components/error"; } from "@/components/error";
import { ConfirmDialog } from "@/components/ui/confirm-dialog"; import { ConfirmDialog } from "@/components/ui/confirm-dialog";
import { PromptDialog } from "@/components/ui/prompt-dialog";
import { TotpReauthDialog } from "@/components/totp-reauth-dialog"; import { TotpReauthDialog } from "@/components/totp-reauth-dialog";
import { DragDropProvider } from "@/contexts/drag-drop-context"; import { DragDropProvider } from "@/contexts/drag-drop-context";
import { isFilterEmpty, activeFilterCount } from "@/lib/jmap/search-utils"; import { isFilterEmpty, activeFilterCount } from "@/lib/jmap/search-utils";
@@ -68,6 +70,7 @@ export default function Home() {
const [pendingDraft, setPendingDraft] = useState<ComposerDraftData | null>(null); const [pendingDraft, setPendingDraft] = useState<ComposerDraftData | null>(null);
const [composerSessionId, setComposerSessionId] = useState(0); const [composerSessionId, setComposerSessionId] = useState(0);
const { dialogProps: confirmDialogProps, confirm: confirmDialog } = useConfirmDialog(); const { dialogProps: confirmDialogProps, confirm: confirmDialog } = useConfirmDialog();
const { dialogProps: promptDialogProps, prompt: promptDialog } = usePromptDialog();
const { showAppsModal, inlineApp, loadedApps, handleManageApps, handleInlineApp, closeInlineApp, closeAppsModal } = useSidebarApps(); const { showAppsModal, inlineApp, loadedApps, handleManageApps, handleInlineApp, closeInlineApp, closeAppsModal } = useSidebarApps();
const [initialCheckDone, setInitialCheckDone] = useState(() => useAuthStore.getState().isAuthenticated && !!useAuthStore.getState().client); const [initialCheckDone, setInitialCheckDone] = useState(() => useAuthStore.getState().isAuthenticated && !!useAuthStore.getState().client);
const [showShortcutsModal, setShowShortcutsModal] = useState(false); const [showShortcutsModal, setShowShortcutsModal] = useState(false);
@@ -1204,10 +1207,15 @@ export default function Home() {
const handleCreateSubfolderFromContextMenu = async (parentId: string) => { const handleCreateSubfolderFromContextMenu = async (parentId: string) => {
if (!client) return; if (!client) return;
const name = window.prompt(tCtxMenu('mailbox_context_menu.prompt_new_subfolder')); const name = await promptDialog({
if (!name || !name.trim()) return; title: tCtxMenu('mailbox_context_menu.new_subfolder'),
message: tCtxMenu('mailbox_context_menu.prompt_new_subfolder'),
placeholder: tCtxMenu('mailbox_context_menu.placeholder_folder_name'),
confirmText: tCtxMenu('mailbox_context_menu.create'),
});
if (!name) return;
try { try {
await createMailbox(client, name.trim(), parentId); await createMailbox(client, name, parentId);
toast.success(tCtxMenu('mailbox_context_menu.toast_folder_created')); toast.success(tCtxMenu('mailbox_context_menu.toast_folder_created'));
} catch { } catch {
toast.error(tCtxMenu('mailbox_context_menu.toast_error_create')); toast.error(tCtxMenu('mailbox_context_menu.toast_error_create'));
@@ -1216,10 +1224,15 @@ export default function Home() {
const handleCreateFolderFromContextMenu = async () => { const handleCreateFolderFromContextMenu = async () => {
if (!client) return; if (!client) return;
const name = window.prompt(tCtxMenu('mailbox_context_menu.prompt_new_folder')); const name = await promptDialog({
if (!name || !name.trim()) return; title: tCtxMenu('mailbox_context_menu.new_folder'),
message: tCtxMenu('mailbox_context_menu.prompt_new_folder'),
placeholder: tCtxMenu('mailbox_context_menu.placeholder_folder_name'),
confirmText: tCtxMenu('mailbox_context_menu.create'),
});
if (!name) return;
try { try {
await createMailbox(client, name.trim()); await createMailbox(client, name);
toast.success(tCtxMenu('mailbox_context_menu.toast_folder_created')); toast.success(tCtxMenu('mailbox_context_menu.toast_folder_created'));
} catch { } catch {
toast.error(tCtxMenu('mailbox_context_menu.toast_error_create')); toast.error(tCtxMenu('mailbox_context_menu.toast_error_create'));
@@ -1230,10 +1243,16 @@ export default function Home() {
if (!client) return; if (!client) return;
const mailbox = mailboxes.find(mb => mb.id === mailboxId); const mailbox = mailboxes.find(mb => mb.id === mailboxId);
if (!mailbox) return; if (!mailbox) return;
const name = window.prompt(tCtxMenu('mailbox_context_menu.prompt_rename'), mailbox.name); const name = await promptDialog({
if (!name || !name.trim() || name.trim() === mailbox.name) return; title: tCtxMenu('mailbox_context_menu.rename'),
message: tCtxMenu('mailbox_context_menu.prompt_rename'),
placeholder: tCtxMenu('mailbox_context_menu.placeholder_folder_name'),
defaultValue: mailbox.name,
confirmText: tCtxMenu('mailbox_context_menu.rename_confirm'),
});
if (!name || name === mailbox.name) return;
try { try {
await renameMailbox(client, mailboxId, name.trim()); await renameMailbox(client, mailboxId, name);
toast.success(tCtxMenu('mailbox_context_menu.toast_folder_renamed')); toast.success(tCtxMenu('mailbox_context_menu.toast_folder_renamed'));
} catch { } catch {
toast.error(tCtxMenu('mailbox_context_menu.toast_error_rename')); toast.error(tCtxMenu('mailbox_context_menu.toast_error_rename'));
@@ -2195,6 +2214,7 @@ export default function Home() {
<SidebarAppsModal isOpen={showAppsModal} onClose={closeAppsModal} /> <SidebarAppsModal isOpen={showAppsModal} onClose={closeAppsModal} />
<ConfirmDialog {...confirmDialogProps} /> <ConfirmDialog {...confirmDialogProps} />
<PromptDialog {...promptDialogProps} />
<TotpReauthDialog /> <TotpReauthDialog />
</div> </div>
</DragDropProvider> </DragDropProvider>
+126
View File
@@ -0,0 +1,126 @@
"use client";
import { useEffect, useId, useRef, useState } from "react";
import { useFocusTrap } from "@/hooks/use-focus-trap";
import { useTranslations } from "next-intl";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
interface PromptDialogProps {
isOpen: boolean;
onClose: () => void;
onSubmit: (value: string) => void;
title: string;
message?: string;
placeholder?: string;
defaultValue?: string;
confirmText?: string;
cancelText?: string;
}
export function PromptDialog({
isOpen,
onClose,
onSubmit,
title,
message,
placeholder,
defaultValue = "",
confirmText,
cancelText,
}: PromptDialogProps) {
const t = useTranslations("confirm_dialog");
const id = useId();
const [value, setValue] = useState(defaultValue);
const inputRef = useRef<HTMLInputElement>(null);
const dialogRef = useFocusTrap({
isActive: isOpen,
onEscape: onClose,
restoreFocus: true,
});
useEffect(() => {
if (isOpen) {
setValue(defaultValue);
const t = setTimeout(() => {
inputRef.current?.focus();
inputRef.current?.select();
}, 50);
return () => clearTimeout(t);
}
}, [isOpen, defaultValue]);
useEffect(() => {
if (!isOpen) return;
const handleBackdropClick = (e: MouseEvent) => {
if (dialogRef.current && !dialogRef.current.contains(e.target as Node)) {
onClose();
}
};
document.addEventListener("mousedown", handleBackdropClick);
return () => document.removeEventListener("mousedown", handleBackdropClick);
}, [isOpen, onClose, dialogRef]);
if (!isOpen) return null;
const resolvedConfirmText = confirmText || t("confirm");
const resolvedCancelText = cancelText || t("cancel");
const trimmed = value.trim();
const canSubmit = trimmed.length > 0;
const handleSubmit = (e?: React.FormEvent) => {
e?.preventDefault();
if (!canSubmit) return;
try {
onSubmit(trimmed);
} finally {
onClose();
}
};
return (
<div className="fixed inset-0 bg-black/50 backdrop-blur-[1px] flex items-center justify-center z-[60] p-4 animate-in fade-in duration-150">
<div
ref={dialogRef}
role="dialog"
aria-modal="true"
aria-labelledby={`${id}-title`}
className="bg-background border border-border rounded-lg shadow-xl w-full max-w-md animate-in zoom-in-95 duration-200"
>
<form onSubmit={handleSubmit}>
<div className="p-6">
<h2
id={`${id}-title`}
className="text-lg font-semibold text-foreground"
>
{title}
</h2>
{message && (
<p className="mt-2 text-sm text-muted-foreground">{message}</p>
)}
<Input
ref={inputRef}
type="text"
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder={placeholder}
className="mt-4"
/>
</div>
<div className="flex items-center justify-end gap-3 px-6 pb-6">
<Button type="button" variant="outline" onClick={onClose}>
{resolvedCancelText}
</Button>
<Button type="submit" variant="default" disabled={!canSubmit}>
{resolvedConfirmText}
</Button>
</div>
</form>
</div>
</div>
);
}
+87
View File
@@ -0,0 +1,87 @@
import { useState, useCallback, useRef, useEffect } from "react";
interface PromptDialogState {
isOpen: boolean;
title: string;
message?: string;
placeholder?: string;
defaultValue: string;
confirmText?: string;
cancelText?: string;
onSubmit: (value: string) => void;
}
const INITIAL_STATE: PromptDialogState = {
isOpen: false,
title: "",
defaultValue: "",
onSubmit: () => {},
};
interface PromptOptions {
title: string;
message?: string;
placeholder?: string;
defaultValue?: string;
confirmText?: string;
cancelText?: string;
}
export function usePromptDialog() {
const [state, setState] = useState<PromptDialogState>(INITIAL_STATE);
const resolveRef = useRef<((value: string | null) => void) | null>(null);
useEffect(() => {
return () => {
if (resolveRef.current) {
resolveRef.current(null);
resolveRef.current = null;
}
};
}, []);
const prompt = useCallback(
(options: PromptOptions): Promise<string | null> => {
return new Promise((resolve) => {
resolveRef.current = resolve;
setState({
isOpen: true,
title: options.title,
message: options.message,
placeholder: options.placeholder,
defaultValue: options.defaultValue ?? "",
confirmText: options.confirmText,
cancelText: options.cancelText,
onSubmit: (value) => {
resolveRef.current = null;
resolve(value);
},
});
});
},
[]
);
const close = useCallback(() => {
if (resolveRef.current) {
resolveRef.current(null);
resolveRef.current = null;
}
setState(INITIAL_STATE);
}, []);
return {
dialogProps: {
isOpen: state.isOpen,
onClose: close,
onSubmit: state.onSubmit,
title: state.title,
message: state.message,
placeholder: state.placeholder,
defaultValue: state.defaultValue,
confirmText: state.confirmText,
cancelText: state.cancelText,
},
prompt,
};
}
+6 -3
View File
@@ -1605,9 +1605,12 @@
"mark_all_confirm_message": "Jede ungelesene Nachricht in deinem persönlichen Konto als gelesen markieren?", "mark_all_confirm_message": "Jede ungelesene Nachricht in deinem persönlichen Konto als gelesen markieren?",
"delete_confirm_title": "Ordner löschen", "delete_confirm_title": "Ordner löschen",
"delete_confirm_message": "Den Ordner \"{name}\" dauerhaft löschen? Dies kann nicht rückgängig gemacht werden.", "delete_confirm_message": "Den Ordner \"{name}\" dauerhaft löschen? Dies kann nicht rückgängig gemacht werden.",
"prompt_new_subfolder": "Name des neuen Unterordners:", "prompt_new_subfolder": "Gib einen Namen für den neuen Unterordner ein.",
"prompt_new_folder": "Name des neuen Ordners:", "prompt_new_folder": "Gib einen Namen für den neuen Ordner ein.",
"prompt_rename": "Ordner umbenennen in:", "prompt_rename": "Gib einen neuen Namen für diesen Ordner ein.",
"placeholder_folder_name": "Ordnername",
"create": "Erstellen",
"rename_confirm": "Umbenennen",
"toast_marked_read": "Ordner als gelesen markiert", "toast_marked_read": "Ordner als gelesen markiert",
"toast_marked_read_count": "{count, plural, one {1 Nachricht} other {# Nachrichten}} als gelesen markiert", "toast_marked_read_count": "{count, plural, one {1 Nachricht} other {# Nachrichten}} als gelesen markiert",
"toast_already_read": "Keine ungelesenen Nachrichten", "toast_already_read": "Keine ungelesenen Nachrichten",
+6 -3
View File
@@ -1609,9 +1609,12 @@
"mark_all_confirm_message": "Mark every unread message in your personal account as read?", "mark_all_confirm_message": "Mark every unread message in your personal account as read?",
"delete_confirm_title": "Delete folder", "delete_confirm_title": "Delete folder",
"delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.", "delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.",
"prompt_new_subfolder": "New subfolder name:", "prompt_new_subfolder": "Enter a name for the new subfolder.",
"prompt_new_folder": "New folder name:", "prompt_new_folder": "Enter a name for the new folder.",
"prompt_rename": "Rename folder to:", "prompt_rename": "Enter a new name for this folder.",
"placeholder_folder_name": "Folder name",
"create": "Create",
"rename_confirm": "Rename",
"toast_marked_read": "Folder marked as read", "toast_marked_read": "Folder marked as read",
"toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read", "toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read",
"toast_already_read": "No unread messages", "toast_already_read": "No unread messages",
+7 -4
View File
@@ -1605,9 +1605,9 @@
"mark_all_confirm_message": "Mark every unread message in your personal account as read?", "mark_all_confirm_message": "Mark every unread message in your personal account as read?",
"delete_confirm_title": "Delete folder", "delete_confirm_title": "Delete folder",
"delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.", "delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.",
"prompt_new_subfolder": "New subfolder name:", "prompt_new_subfolder": "Enter a name for the new subfolder.",
"prompt_new_folder": "New folder name:", "prompt_new_folder": "Enter a name for the new folder.",
"prompt_rename": "Rename folder to:", "prompt_rename": "Enter a new name for this folder.",
"toast_marked_read": "Folder marked as read", "toast_marked_read": "Folder marked as read",
"toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read", "toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read",
"toast_already_read": "No unread messages", "toast_already_read": "No unread messages",
@@ -1622,7 +1622,10 @@
"toast_error_rename": "Failed to rename folder", "toast_error_rename": "Failed to rename folder",
"toast_error_delete": "Failed to delete folder", "toast_error_delete": "Failed to delete folder",
"toast_error_delete_has_children": "Folder has subfolders. Remove them first.", "toast_error_delete_has_children": "Folder has subfolders. Remove them first.",
"toast_error_delete_has_email": "Folder is not empty. Empty it first." "toast_error_delete_has_email": "Folder is not empty. Empty it first.",
"placeholder_folder_name": "Folder name",
"create": "Create",
"rename_confirm": "Rename"
}, },
"shortcuts": { "shortcuts": {
"title": "Atajos de Teclado", "title": "Atajos de Teclado",
+7 -4
View File
@@ -1605,9 +1605,9 @@
"mark_all_confirm_message": "Mark every unread message in your personal account as read?", "mark_all_confirm_message": "Mark every unread message in your personal account as read?",
"delete_confirm_title": "Delete folder", "delete_confirm_title": "Delete folder",
"delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.", "delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.",
"prompt_new_subfolder": "New subfolder name:", "prompt_new_subfolder": "Enter a name for the new subfolder.",
"prompt_new_folder": "New folder name:", "prompt_new_folder": "Enter a name for the new folder.",
"prompt_rename": "Rename folder to:", "prompt_rename": "Enter a new name for this folder.",
"toast_marked_read": "Folder marked as read", "toast_marked_read": "Folder marked as read",
"toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read", "toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read",
"toast_already_read": "No unread messages", "toast_already_read": "No unread messages",
@@ -1622,7 +1622,10 @@
"toast_error_rename": "Failed to rename folder", "toast_error_rename": "Failed to rename folder",
"toast_error_delete": "Failed to delete folder", "toast_error_delete": "Failed to delete folder",
"toast_error_delete_has_children": "Folder has subfolders. Remove them first.", "toast_error_delete_has_children": "Folder has subfolders. Remove them first.",
"toast_error_delete_has_email": "Folder is not empty. Empty it first." "toast_error_delete_has_email": "Folder is not empty. Empty it first.",
"placeholder_folder_name": "Folder name",
"create": "Create",
"rename_confirm": "Rename"
}, },
"shortcuts": { "shortcuts": {
"title": "Raccourcis clavier", "title": "Raccourcis clavier",
+7 -4
View File
@@ -1605,9 +1605,9 @@
"mark_all_confirm_message": "Mark every unread message in your personal account as read?", "mark_all_confirm_message": "Mark every unread message in your personal account as read?",
"delete_confirm_title": "Delete folder", "delete_confirm_title": "Delete folder",
"delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.", "delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.",
"prompt_new_subfolder": "New subfolder name:", "prompt_new_subfolder": "Enter a name for the new subfolder.",
"prompt_new_folder": "New folder name:", "prompt_new_folder": "Enter a name for the new folder.",
"prompt_rename": "Rename folder to:", "prompt_rename": "Enter a new name for this folder.",
"toast_marked_read": "Folder marked as read", "toast_marked_read": "Folder marked as read",
"toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read", "toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read",
"toast_already_read": "No unread messages", "toast_already_read": "No unread messages",
@@ -1622,7 +1622,10 @@
"toast_error_rename": "Failed to rename folder", "toast_error_rename": "Failed to rename folder",
"toast_error_delete": "Failed to delete folder", "toast_error_delete": "Failed to delete folder",
"toast_error_delete_has_children": "Folder has subfolders. Remove them first.", "toast_error_delete_has_children": "Folder has subfolders. Remove them first.",
"toast_error_delete_has_email": "Folder is not empty. Empty it first." "toast_error_delete_has_email": "Folder is not empty. Empty it first.",
"placeholder_folder_name": "Folder name",
"create": "Create",
"rename_confirm": "Rename"
}, },
"shortcuts": { "shortcuts": {
"title": "Scorciatoie da tastiera", "title": "Scorciatoie da tastiera",
+7 -4
View File
@@ -1605,9 +1605,9 @@
"mark_all_confirm_message": "Mark every unread message in your personal account as read?", "mark_all_confirm_message": "Mark every unread message in your personal account as read?",
"delete_confirm_title": "Delete folder", "delete_confirm_title": "Delete folder",
"delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.", "delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.",
"prompt_new_subfolder": "New subfolder name:", "prompt_new_subfolder": "Enter a name for the new subfolder.",
"prompt_new_folder": "New folder name:", "prompt_new_folder": "Enter a name for the new folder.",
"prompt_rename": "Rename folder to:", "prompt_rename": "Enter a new name for this folder.",
"toast_marked_read": "Folder marked as read", "toast_marked_read": "Folder marked as read",
"toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read", "toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read",
"toast_already_read": "No unread messages", "toast_already_read": "No unread messages",
@@ -1622,7 +1622,10 @@
"toast_error_rename": "Failed to rename folder", "toast_error_rename": "Failed to rename folder",
"toast_error_delete": "Failed to delete folder", "toast_error_delete": "Failed to delete folder",
"toast_error_delete_has_children": "Folder has subfolders. Remove them first.", "toast_error_delete_has_children": "Folder has subfolders. Remove them first.",
"toast_error_delete_has_email": "Folder is not empty. Empty it first." "toast_error_delete_has_email": "Folder is not empty. Empty it first.",
"placeholder_folder_name": "Folder name",
"create": "Create",
"rename_confirm": "Rename"
}, },
"shortcuts": { "shortcuts": {
"title": "キーボードショートカット", "title": "キーボードショートカット",
+7 -4
View File
@@ -1605,9 +1605,9 @@
"mark_all_confirm_message": "Mark every unread message in your personal account as read?", "mark_all_confirm_message": "Mark every unread message in your personal account as read?",
"delete_confirm_title": "Delete folder", "delete_confirm_title": "Delete folder",
"delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.", "delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.",
"prompt_new_subfolder": "New subfolder name:", "prompt_new_subfolder": "Enter a name for the new subfolder.",
"prompt_new_folder": "New folder name:", "prompt_new_folder": "Enter a name for the new folder.",
"prompt_rename": "Rename folder to:", "prompt_rename": "Enter a new name for this folder.",
"toast_marked_read": "Folder marked as read", "toast_marked_read": "Folder marked as read",
"toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read", "toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read",
"toast_already_read": "No unread messages", "toast_already_read": "No unread messages",
@@ -1622,7 +1622,10 @@
"toast_error_rename": "Failed to rename folder", "toast_error_rename": "Failed to rename folder",
"toast_error_delete": "Failed to delete folder", "toast_error_delete": "Failed to delete folder",
"toast_error_delete_has_children": "Folder has subfolders. Remove them first.", "toast_error_delete_has_children": "Folder has subfolders. Remove them first.",
"toast_error_delete_has_email": "Folder is not empty. Empty it first." "toast_error_delete_has_email": "Folder is not empty. Empty it first.",
"placeholder_folder_name": "Folder name",
"create": "Create",
"rename_confirm": "Rename"
}, },
"shortcuts": { "shortcuts": {
"title": "단축키", "title": "단축키",
+7 -4
View File
@@ -1605,9 +1605,9 @@
"mark_all_confirm_message": "Mark every unread message in your personal account as read?", "mark_all_confirm_message": "Mark every unread message in your personal account as read?",
"delete_confirm_title": "Delete folder", "delete_confirm_title": "Delete folder",
"delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.", "delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.",
"prompt_new_subfolder": "New subfolder name:", "prompt_new_subfolder": "Enter a name for the new subfolder.",
"prompt_new_folder": "New folder name:", "prompt_new_folder": "Enter a name for the new folder.",
"prompt_rename": "Rename folder to:", "prompt_rename": "Enter a new name for this folder.",
"toast_marked_read": "Folder marked as read", "toast_marked_read": "Folder marked as read",
"toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read", "toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read",
"toast_already_read": "No unread messages", "toast_already_read": "No unread messages",
@@ -1622,7 +1622,10 @@
"toast_error_rename": "Failed to rename folder", "toast_error_rename": "Failed to rename folder",
"toast_error_delete": "Failed to delete folder", "toast_error_delete": "Failed to delete folder",
"toast_error_delete_has_children": "Folder has subfolders. Remove them first.", "toast_error_delete_has_children": "Folder has subfolders. Remove them first.",
"toast_error_delete_has_email": "Folder is not empty. Empty it first." "toast_error_delete_has_email": "Folder is not empty. Empty it first.",
"placeholder_folder_name": "Folder name",
"create": "Create",
"rename_confirm": "Rename"
}, },
"shortcuts": { "shortcuts": {
"title": "Īsinājumtaustiņi", "title": "Īsinājumtaustiņi",
+7 -4
View File
@@ -1605,9 +1605,9 @@
"mark_all_confirm_message": "Mark every unread message in your personal account as read?", "mark_all_confirm_message": "Mark every unread message in your personal account as read?",
"delete_confirm_title": "Delete folder", "delete_confirm_title": "Delete folder",
"delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.", "delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.",
"prompt_new_subfolder": "New subfolder name:", "prompt_new_subfolder": "Enter a name for the new subfolder.",
"prompt_new_folder": "New folder name:", "prompt_new_folder": "Enter a name for the new folder.",
"prompt_rename": "Rename folder to:", "prompt_rename": "Enter a new name for this folder.",
"toast_marked_read": "Folder marked as read", "toast_marked_read": "Folder marked as read",
"toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read", "toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read",
"toast_already_read": "No unread messages", "toast_already_read": "No unread messages",
@@ -1622,7 +1622,10 @@
"toast_error_rename": "Failed to rename folder", "toast_error_rename": "Failed to rename folder",
"toast_error_delete": "Failed to delete folder", "toast_error_delete": "Failed to delete folder",
"toast_error_delete_has_children": "Folder has subfolders. Remove them first.", "toast_error_delete_has_children": "Folder has subfolders. Remove them first.",
"toast_error_delete_has_email": "Folder is not empty. Empty it first." "toast_error_delete_has_email": "Folder is not empty. Empty it first.",
"placeholder_folder_name": "Folder name",
"create": "Create",
"rename_confirm": "Rename"
}, },
"shortcuts": { "shortcuts": {
"title": "Sneltoetsen", "title": "Sneltoetsen",
+7 -4
View File
@@ -1605,9 +1605,9 @@
"mark_all_confirm_message": "Mark every unread message in your personal account as read?", "mark_all_confirm_message": "Mark every unread message in your personal account as read?",
"delete_confirm_title": "Delete folder", "delete_confirm_title": "Delete folder",
"delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.", "delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.",
"prompt_new_subfolder": "New subfolder name:", "prompt_new_subfolder": "Enter a name for the new subfolder.",
"prompt_new_folder": "New folder name:", "prompt_new_folder": "Enter a name for the new folder.",
"prompt_rename": "Rename folder to:", "prompt_rename": "Enter a new name for this folder.",
"toast_marked_read": "Folder marked as read", "toast_marked_read": "Folder marked as read",
"toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read", "toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read",
"toast_already_read": "No unread messages", "toast_already_read": "No unread messages",
@@ -1622,7 +1622,10 @@
"toast_error_rename": "Failed to rename folder", "toast_error_rename": "Failed to rename folder",
"toast_error_delete": "Failed to delete folder", "toast_error_delete": "Failed to delete folder",
"toast_error_delete_has_children": "Folder has subfolders. Remove them first.", "toast_error_delete_has_children": "Folder has subfolders. Remove them first.",
"toast_error_delete_has_email": "Folder is not empty. Empty it first." "toast_error_delete_has_email": "Folder is not empty. Empty it first.",
"placeholder_folder_name": "Folder name",
"create": "Create",
"rename_confirm": "Rename"
}, },
"shortcuts": { "shortcuts": {
"title": "Skróty klawiszowe", "title": "Skróty klawiszowe",
+7 -4
View File
@@ -1605,9 +1605,9 @@
"mark_all_confirm_message": "Mark every unread message in your personal account as read?", "mark_all_confirm_message": "Mark every unread message in your personal account as read?",
"delete_confirm_title": "Delete folder", "delete_confirm_title": "Delete folder",
"delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.", "delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.",
"prompt_new_subfolder": "New subfolder name:", "prompt_new_subfolder": "Enter a name for the new subfolder.",
"prompt_new_folder": "New folder name:", "prompt_new_folder": "Enter a name for the new folder.",
"prompt_rename": "Rename folder to:", "prompt_rename": "Enter a new name for this folder.",
"toast_marked_read": "Folder marked as read", "toast_marked_read": "Folder marked as read",
"toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read", "toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read",
"toast_already_read": "No unread messages", "toast_already_read": "No unread messages",
@@ -1622,7 +1622,10 @@
"toast_error_rename": "Failed to rename folder", "toast_error_rename": "Failed to rename folder",
"toast_error_delete": "Failed to delete folder", "toast_error_delete": "Failed to delete folder",
"toast_error_delete_has_children": "Folder has subfolders. Remove them first.", "toast_error_delete_has_children": "Folder has subfolders. Remove them first.",
"toast_error_delete_has_email": "Folder is not empty. Empty it first." "toast_error_delete_has_email": "Folder is not empty. Empty it first.",
"placeholder_folder_name": "Folder name",
"create": "Create",
"rename_confirm": "Rename"
}, },
"shortcuts": { "shortcuts": {
"title": "Atalhos de Teclado", "title": "Atalhos de Teclado",
+7 -4
View File
@@ -1605,9 +1605,9 @@
"mark_all_confirm_message": "Mark every unread message in your personal account as read?", "mark_all_confirm_message": "Mark every unread message in your personal account as read?",
"delete_confirm_title": "Delete folder", "delete_confirm_title": "Delete folder",
"delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.", "delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.",
"prompt_new_subfolder": "New subfolder name:", "prompt_new_subfolder": "Enter a name for the new subfolder.",
"prompt_new_folder": "New folder name:", "prompt_new_folder": "Enter a name for the new folder.",
"prompt_rename": "Rename folder to:", "prompt_rename": "Enter a new name for this folder.",
"toast_marked_read": "Folder marked as read", "toast_marked_read": "Folder marked as read",
"toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read", "toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read",
"toast_already_read": "No unread messages", "toast_already_read": "No unread messages",
@@ -1622,7 +1622,10 @@
"toast_error_rename": "Failed to rename folder", "toast_error_rename": "Failed to rename folder",
"toast_error_delete": "Failed to delete folder", "toast_error_delete": "Failed to delete folder",
"toast_error_delete_has_children": "Folder has subfolders. Remove them first.", "toast_error_delete_has_children": "Folder has subfolders. Remove them first.",
"toast_error_delete_has_email": "Folder is not empty. Empty it first." "toast_error_delete_has_email": "Folder is not empty. Empty it first.",
"placeholder_folder_name": "Folder name",
"create": "Create",
"rename_confirm": "Rename"
}, },
"shortcuts": { "shortcuts": {
"title": "Сочетания клавиш", "title": "Сочетания клавиш",
+7 -4
View File
@@ -1605,9 +1605,9 @@
"mark_all_confirm_message": "Mark every unread message in your personal account as read?", "mark_all_confirm_message": "Mark every unread message in your personal account as read?",
"delete_confirm_title": "Delete folder", "delete_confirm_title": "Delete folder",
"delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.", "delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.",
"prompt_new_subfolder": "New subfolder name:", "prompt_new_subfolder": "Enter a name for the new subfolder.",
"prompt_new_folder": "New folder name:", "prompt_new_folder": "Enter a name for the new folder.",
"prompt_rename": "Rename folder to:", "prompt_rename": "Enter a new name for this folder.",
"toast_marked_read": "Folder marked as read", "toast_marked_read": "Folder marked as read",
"toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read", "toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read",
"toast_already_read": "No unread messages", "toast_already_read": "No unread messages",
@@ -1622,7 +1622,10 @@
"toast_error_rename": "Failed to rename folder", "toast_error_rename": "Failed to rename folder",
"toast_error_delete": "Failed to delete folder", "toast_error_delete": "Failed to delete folder",
"toast_error_delete_has_children": "Folder has subfolders. Remove them first.", "toast_error_delete_has_children": "Folder has subfolders. Remove them first.",
"toast_error_delete_has_email": "Folder is not empty. Empty it first." "toast_error_delete_has_email": "Folder is not empty. Empty it first.",
"placeholder_folder_name": "Folder name",
"create": "Create",
"rename_confirm": "Rename"
}, },
"shortcuts": { "shortcuts": {
"title": "Комбінації клавіш", "title": "Комбінації клавіш",
+7 -4
View File
@@ -1605,9 +1605,9 @@
"mark_all_confirm_message": "Mark every unread message in your personal account as read?", "mark_all_confirm_message": "Mark every unread message in your personal account as read?",
"delete_confirm_title": "Delete folder", "delete_confirm_title": "Delete folder",
"delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.", "delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.",
"prompt_new_subfolder": "New subfolder name:", "prompt_new_subfolder": "Enter a name for the new subfolder.",
"prompt_new_folder": "New folder name:", "prompt_new_folder": "Enter a name for the new folder.",
"prompt_rename": "Rename folder to:", "prompt_rename": "Enter a new name for this folder.",
"toast_marked_read": "Folder marked as read", "toast_marked_read": "Folder marked as read",
"toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read", "toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read",
"toast_already_read": "No unread messages", "toast_already_read": "No unread messages",
@@ -1622,7 +1622,10 @@
"toast_error_rename": "Failed to rename folder", "toast_error_rename": "Failed to rename folder",
"toast_error_delete": "Failed to delete folder", "toast_error_delete": "Failed to delete folder",
"toast_error_delete_has_children": "Folder has subfolders. Remove them first.", "toast_error_delete_has_children": "Folder has subfolders. Remove them first.",
"toast_error_delete_has_email": "Folder is not empty. Empty it first." "toast_error_delete_has_email": "Folder is not empty. Empty it first.",
"placeholder_folder_name": "Folder name",
"create": "Create",
"rename_confirm": "Rename"
}, },
"shortcuts": { "shortcuts": {
"title": "键盘快捷键", "title": "键盘快捷键",