feat: replace folder prompt() calls with proper modal dialog
This commit is contained in:
+29
-9
@@ -24,6 +24,7 @@ import { useDeviceDetection } from "@/hooks/use-media-query";
|
||||
import { useKeyboardShortcuts } from "@/hooks/use-keyboard-shortcuts";
|
||||
import { useRefreshGesture } from "@/hooks/use-refresh-gesture";
|
||||
import { useConfirmDialog } from "@/hooks/use-confirm-dialog";
|
||||
import { usePromptDialog } from "@/hooks/use-prompt-dialog";
|
||||
import { useBrowserNavigation, type NavSnapshot } from "@/hooks/use-browser-navigation";
|
||||
import { debug } from "@/lib/debug";
|
||||
import { playNotificationSound } from "@/lib/notification-sound";
|
||||
@@ -36,6 +37,7 @@ import {
|
||||
ComposerErrorFallback,
|
||||
} from "@/components/error";
|
||||
import { ConfirmDialog } from "@/components/ui/confirm-dialog";
|
||||
import { PromptDialog } from "@/components/ui/prompt-dialog";
|
||||
import { TotpReauthDialog } from "@/components/totp-reauth-dialog";
|
||||
import { DragDropProvider } from "@/contexts/drag-drop-context";
|
||||
import { isFilterEmpty, activeFilterCount } from "@/lib/jmap/search-utils";
|
||||
@@ -68,6 +70,7 @@ export default function Home() {
|
||||
const [pendingDraft, setPendingDraft] = useState<ComposerDraftData | null>(null);
|
||||
const [composerSessionId, setComposerSessionId] = useState(0);
|
||||
const { dialogProps: confirmDialogProps, confirm: confirmDialog } = useConfirmDialog();
|
||||
const { dialogProps: promptDialogProps, prompt: promptDialog } = usePromptDialog();
|
||||
const { showAppsModal, inlineApp, loadedApps, handleManageApps, handleInlineApp, closeInlineApp, closeAppsModal } = useSidebarApps();
|
||||
const [initialCheckDone, setInitialCheckDone] = useState(() => useAuthStore.getState().isAuthenticated && !!useAuthStore.getState().client);
|
||||
const [showShortcutsModal, setShowShortcutsModal] = useState(false);
|
||||
@@ -1204,10 +1207,15 @@ export default function Home() {
|
||||
|
||||
const handleCreateSubfolderFromContextMenu = async (parentId: string) => {
|
||||
if (!client) return;
|
||||
const name = window.prompt(tCtxMenu('mailbox_context_menu.prompt_new_subfolder'));
|
||||
if (!name || !name.trim()) return;
|
||||
const name = await promptDialog({
|
||||
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 {
|
||||
await createMailbox(client, name.trim(), parentId);
|
||||
await createMailbox(client, name, parentId);
|
||||
toast.success(tCtxMenu('mailbox_context_menu.toast_folder_created'));
|
||||
} catch {
|
||||
toast.error(tCtxMenu('mailbox_context_menu.toast_error_create'));
|
||||
@@ -1216,10 +1224,15 @@ export default function Home() {
|
||||
|
||||
const handleCreateFolderFromContextMenu = async () => {
|
||||
if (!client) return;
|
||||
const name = window.prompt(tCtxMenu('mailbox_context_menu.prompt_new_folder'));
|
||||
if (!name || !name.trim()) return;
|
||||
const name = await promptDialog({
|
||||
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 {
|
||||
await createMailbox(client, name.trim());
|
||||
await createMailbox(client, name);
|
||||
toast.success(tCtxMenu('mailbox_context_menu.toast_folder_created'));
|
||||
} catch {
|
||||
toast.error(tCtxMenu('mailbox_context_menu.toast_error_create'));
|
||||
@@ -1230,10 +1243,16 @@ export default function Home() {
|
||||
if (!client) return;
|
||||
const mailbox = mailboxes.find(mb => mb.id === mailboxId);
|
||||
if (!mailbox) return;
|
||||
const name = window.prompt(tCtxMenu('mailbox_context_menu.prompt_rename'), mailbox.name);
|
||||
if (!name || !name.trim() || name.trim() === mailbox.name) return;
|
||||
const name = await promptDialog({
|
||||
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 {
|
||||
await renameMailbox(client, mailboxId, name.trim());
|
||||
await renameMailbox(client, mailboxId, name);
|
||||
toast.success(tCtxMenu('mailbox_context_menu.toast_folder_renamed'));
|
||||
} catch {
|
||||
toast.error(tCtxMenu('mailbox_context_menu.toast_error_rename'));
|
||||
@@ -2195,6 +2214,7 @@ export default function Home() {
|
||||
|
||||
<SidebarAppsModal isOpen={showAppsModal} onClose={closeAppsModal} />
|
||||
<ConfirmDialog {...confirmDialogProps} />
|
||||
<PromptDialog {...promptDialogProps} />
|
||||
<TotpReauthDialog />
|
||||
</div>
|
||||
</DragDropProvider>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
@@ -1605,9 +1605,12 @@
|
||||
"mark_all_confirm_message": "Jede ungelesene Nachricht in deinem persönlichen Konto als gelesen markieren?",
|
||||
"delete_confirm_title": "Ordner löschen",
|
||||
"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_folder": "Name des neuen Ordners:",
|
||||
"prompt_rename": "Ordner umbenennen in:",
|
||||
"prompt_new_subfolder": "Gib einen Namen für den neuen Unterordner ein.",
|
||||
"prompt_new_folder": "Gib einen Namen für den neuen Ordner ein.",
|
||||
"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_count": "{count, plural, one {1 Nachricht} other {# Nachrichten}} als gelesen markiert",
|
||||
"toast_already_read": "Keine ungelesenen Nachrichten",
|
||||
|
||||
@@ -1609,9 +1609,12 @@
|
||||
"mark_all_confirm_message": "Mark every unread message in your personal account as read?",
|
||||
"delete_confirm_title": "Delete folder",
|
||||
"delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.",
|
||||
"prompt_new_subfolder": "New subfolder name:",
|
||||
"prompt_new_folder": "New folder name:",
|
||||
"prompt_rename": "Rename folder to:",
|
||||
"prompt_new_subfolder": "Enter a name for the new subfolder.",
|
||||
"prompt_new_folder": "Enter a name for the new folder.",
|
||||
"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_count": "Marked {count, plural, one {1 message} other {# messages}} as read",
|
||||
"toast_already_read": "No unread messages",
|
||||
|
||||
@@ -1605,9 +1605,9 @@
|
||||
"mark_all_confirm_message": "Mark every unread message in your personal account as read?",
|
||||
"delete_confirm_title": "Delete folder",
|
||||
"delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.",
|
||||
"prompt_new_subfolder": "New subfolder name:",
|
||||
"prompt_new_folder": "New folder name:",
|
||||
"prompt_rename": "Rename folder to:",
|
||||
"prompt_new_subfolder": "Enter a name for the new subfolder.",
|
||||
"prompt_new_folder": "Enter a name for the new folder.",
|
||||
"prompt_rename": "Enter a new name for this folder.",
|
||||
"toast_marked_read": "Folder marked as read",
|
||||
"toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read",
|
||||
"toast_already_read": "No unread messages",
|
||||
@@ -1622,7 +1622,10 @@
|
||||
"toast_error_rename": "Failed to rename folder",
|
||||
"toast_error_delete": "Failed to delete folder",
|
||||
"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": {
|
||||
"title": "Atajos de Teclado",
|
||||
|
||||
@@ -1605,9 +1605,9 @@
|
||||
"mark_all_confirm_message": "Mark every unread message in your personal account as read?",
|
||||
"delete_confirm_title": "Delete folder",
|
||||
"delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.",
|
||||
"prompt_new_subfolder": "New subfolder name:",
|
||||
"prompt_new_folder": "New folder name:",
|
||||
"prompt_rename": "Rename folder to:",
|
||||
"prompt_new_subfolder": "Enter a name for the new subfolder.",
|
||||
"prompt_new_folder": "Enter a name for the new folder.",
|
||||
"prompt_rename": "Enter a new name for this folder.",
|
||||
"toast_marked_read": "Folder marked as read",
|
||||
"toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read",
|
||||
"toast_already_read": "No unread messages",
|
||||
@@ -1622,7 +1622,10 @@
|
||||
"toast_error_rename": "Failed to rename folder",
|
||||
"toast_error_delete": "Failed to delete folder",
|
||||
"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": {
|
||||
"title": "Raccourcis clavier",
|
||||
|
||||
@@ -1605,9 +1605,9 @@
|
||||
"mark_all_confirm_message": "Mark every unread message in your personal account as read?",
|
||||
"delete_confirm_title": "Delete folder",
|
||||
"delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.",
|
||||
"prompt_new_subfolder": "New subfolder name:",
|
||||
"prompt_new_folder": "New folder name:",
|
||||
"prompt_rename": "Rename folder to:",
|
||||
"prompt_new_subfolder": "Enter a name for the new subfolder.",
|
||||
"prompt_new_folder": "Enter a name for the new folder.",
|
||||
"prompt_rename": "Enter a new name for this folder.",
|
||||
"toast_marked_read": "Folder marked as read",
|
||||
"toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read",
|
||||
"toast_already_read": "No unread messages",
|
||||
@@ -1622,7 +1622,10 @@
|
||||
"toast_error_rename": "Failed to rename folder",
|
||||
"toast_error_delete": "Failed to delete folder",
|
||||
"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": {
|
||||
"title": "Scorciatoie da tastiera",
|
||||
|
||||
@@ -1605,9 +1605,9 @@
|
||||
"mark_all_confirm_message": "Mark every unread message in your personal account as read?",
|
||||
"delete_confirm_title": "Delete folder",
|
||||
"delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.",
|
||||
"prompt_new_subfolder": "New subfolder name:",
|
||||
"prompt_new_folder": "New folder name:",
|
||||
"prompt_rename": "Rename folder to:",
|
||||
"prompt_new_subfolder": "Enter a name for the new subfolder.",
|
||||
"prompt_new_folder": "Enter a name for the new folder.",
|
||||
"prompt_rename": "Enter a new name for this folder.",
|
||||
"toast_marked_read": "Folder marked as read",
|
||||
"toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read",
|
||||
"toast_already_read": "No unread messages",
|
||||
@@ -1622,7 +1622,10 @@
|
||||
"toast_error_rename": "Failed to rename folder",
|
||||
"toast_error_delete": "Failed to delete folder",
|
||||
"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": {
|
||||
"title": "キーボードショートカット",
|
||||
|
||||
@@ -1605,9 +1605,9 @@
|
||||
"mark_all_confirm_message": "Mark every unread message in your personal account as read?",
|
||||
"delete_confirm_title": "Delete folder",
|
||||
"delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.",
|
||||
"prompt_new_subfolder": "New subfolder name:",
|
||||
"prompt_new_folder": "New folder name:",
|
||||
"prompt_rename": "Rename folder to:",
|
||||
"prompt_new_subfolder": "Enter a name for the new subfolder.",
|
||||
"prompt_new_folder": "Enter a name for the new folder.",
|
||||
"prompt_rename": "Enter a new name for this folder.",
|
||||
"toast_marked_read": "Folder marked as read",
|
||||
"toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read",
|
||||
"toast_already_read": "No unread messages",
|
||||
@@ -1622,7 +1622,10 @@
|
||||
"toast_error_rename": "Failed to rename folder",
|
||||
"toast_error_delete": "Failed to delete folder",
|
||||
"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": {
|
||||
"title": "단축키",
|
||||
|
||||
@@ -1605,9 +1605,9 @@
|
||||
"mark_all_confirm_message": "Mark every unread message in your personal account as read?",
|
||||
"delete_confirm_title": "Delete folder",
|
||||
"delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.",
|
||||
"prompt_new_subfolder": "New subfolder name:",
|
||||
"prompt_new_folder": "New folder name:",
|
||||
"prompt_rename": "Rename folder to:",
|
||||
"prompt_new_subfolder": "Enter a name for the new subfolder.",
|
||||
"prompt_new_folder": "Enter a name for the new folder.",
|
||||
"prompt_rename": "Enter a new name for this folder.",
|
||||
"toast_marked_read": "Folder marked as read",
|
||||
"toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read",
|
||||
"toast_already_read": "No unread messages",
|
||||
@@ -1622,7 +1622,10 @@
|
||||
"toast_error_rename": "Failed to rename folder",
|
||||
"toast_error_delete": "Failed to delete folder",
|
||||
"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": {
|
||||
"title": "Īsinājumtaustiņi",
|
||||
|
||||
@@ -1605,9 +1605,9 @@
|
||||
"mark_all_confirm_message": "Mark every unread message in your personal account as read?",
|
||||
"delete_confirm_title": "Delete folder",
|
||||
"delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.",
|
||||
"prompt_new_subfolder": "New subfolder name:",
|
||||
"prompt_new_folder": "New folder name:",
|
||||
"prompt_rename": "Rename folder to:",
|
||||
"prompt_new_subfolder": "Enter a name for the new subfolder.",
|
||||
"prompt_new_folder": "Enter a name for the new folder.",
|
||||
"prompt_rename": "Enter a new name for this folder.",
|
||||
"toast_marked_read": "Folder marked as read",
|
||||
"toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read",
|
||||
"toast_already_read": "No unread messages",
|
||||
@@ -1622,7 +1622,10 @@
|
||||
"toast_error_rename": "Failed to rename folder",
|
||||
"toast_error_delete": "Failed to delete folder",
|
||||
"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": {
|
||||
"title": "Sneltoetsen",
|
||||
|
||||
@@ -1605,9 +1605,9 @@
|
||||
"mark_all_confirm_message": "Mark every unread message in your personal account as read?",
|
||||
"delete_confirm_title": "Delete folder",
|
||||
"delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.",
|
||||
"prompt_new_subfolder": "New subfolder name:",
|
||||
"prompt_new_folder": "New folder name:",
|
||||
"prompt_rename": "Rename folder to:",
|
||||
"prompt_new_subfolder": "Enter a name for the new subfolder.",
|
||||
"prompt_new_folder": "Enter a name for the new folder.",
|
||||
"prompt_rename": "Enter a new name for this folder.",
|
||||
"toast_marked_read": "Folder marked as read",
|
||||
"toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read",
|
||||
"toast_already_read": "No unread messages",
|
||||
@@ -1622,7 +1622,10 @@
|
||||
"toast_error_rename": "Failed to rename folder",
|
||||
"toast_error_delete": "Failed to delete folder",
|
||||
"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": {
|
||||
"title": "Skróty klawiszowe",
|
||||
|
||||
@@ -1605,9 +1605,9 @@
|
||||
"mark_all_confirm_message": "Mark every unread message in your personal account as read?",
|
||||
"delete_confirm_title": "Delete folder",
|
||||
"delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.",
|
||||
"prompt_new_subfolder": "New subfolder name:",
|
||||
"prompt_new_folder": "New folder name:",
|
||||
"prompt_rename": "Rename folder to:",
|
||||
"prompt_new_subfolder": "Enter a name for the new subfolder.",
|
||||
"prompt_new_folder": "Enter a name for the new folder.",
|
||||
"prompt_rename": "Enter a new name for this folder.",
|
||||
"toast_marked_read": "Folder marked as read",
|
||||
"toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read",
|
||||
"toast_already_read": "No unread messages",
|
||||
@@ -1622,7 +1622,10 @@
|
||||
"toast_error_rename": "Failed to rename folder",
|
||||
"toast_error_delete": "Failed to delete folder",
|
||||
"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": {
|
||||
"title": "Atalhos de Teclado",
|
||||
|
||||
@@ -1605,9 +1605,9 @@
|
||||
"mark_all_confirm_message": "Mark every unread message in your personal account as read?",
|
||||
"delete_confirm_title": "Delete folder",
|
||||
"delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.",
|
||||
"prompt_new_subfolder": "New subfolder name:",
|
||||
"prompt_new_folder": "New folder name:",
|
||||
"prompt_rename": "Rename folder to:",
|
||||
"prompt_new_subfolder": "Enter a name for the new subfolder.",
|
||||
"prompt_new_folder": "Enter a name for the new folder.",
|
||||
"prompt_rename": "Enter a new name for this folder.",
|
||||
"toast_marked_read": "Folder marked as read",
|
||||
"toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read",
|
||||
"toast_already_read": "No unread messages",
|
||||
@@ -1622,7 +1622,10 @@
|
||||
"toast_error_rename": "Failed to rename folder",
|
||||
"toast_error_delete": "Failed to delete folder",
|
||||
"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": {
|
||||
"title": "Сочетания клавиш",
|
||||
|
||||
@@ -1605,9 +1605,9 @@
|
||||
"mark_all_confirm_message": "Mark every unread message in your personal account as read?",
|
||||
"delete_confirm_title": "Delete folder",
|
||||
"delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.",
|
||||
"prompt_new_subfolder": "New subfolder name:",
|
||||
"prompt_new_folder": "New folder name:",
|
||||
"prompt_rename": "Rename folder to:",
|
||||
"prompt_new_subfolder": "Enter a name for the new subfolder.",
|
||||
"prompt_new_folder": "Enter a name for the new folder.",
|
||||
"prompt_rename": "Enter a new name for this folder.",
|
||||
"toast_marked_read": "Folder marked as read",
|
||||
"toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read",
|
||||
"toast_already_read": "No unread messages",
|
||||
@@ -1622,7 +1622,10 @@
|
||||
"toast_error_rename": "Failed to rename folder",
|
||||
"toast_error_delete": "Failed to delete folder",
|
||||
"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": {
|
||||
"title": "Комбінації клавіш",
|
||||
|
||||
@@ -1605,9 +1605,9 @@
|
||||
"mark_all_confirm_message": "Mark every unread message in your personal account as read?",
|
||||
"delete_confirm_title": "Delete folder",
|
||||
"delete_confirm_message": "Permanently delete the folder \"{name}\"? This action cannot be undone.",
|
||||
"prompt_new_subfolder": "New subfolder name:",
|
||||
"prompt_new_folder": "New folder name:",
|
||||
"prompt_rename": "Rename folder to:",
|
||||
"prompt_new_subfolder": "Enter a name for the new subfolder.",
|
||||
"prompt_new_folder": "Enter a name for the new folder.",
|
||||
"prompt_rename": "Enter a new name for this folder.",
|
||||
"toast_marked_read": "Folder marked as read",
|
||||
"toast_marked_read_count": "Marked {count, plural, one {1 message} other {# messages}} as read",
|
||||
"toast_already_read": "No unread messages",
|
||||
@@ -1622,7 +1622,10 @@
|
||||
"toast_error_rename": "Failed to rename folder",
|
||||
"toast_error_delete": "Failed to delete folder",
|
||||
"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": {
|
||||
"title": "键盘快捷键",
|
||||
|
||||
Reference in New Issue
Block a user