mirror of
https://github.com/Mail-0/Zero.git
synced 2026-07-01 08:16:28 +00:00
Merge branch 'staging' into feat/signatures
This commit is contained in:
@@ -23,6 +23,7 @@ import {
|
||||
Star,
|
||||
StarOff,
|
||||
Trash,
|
||||
MailOpen,
|
||||
} from 'lucide-react';
|
||||
import { moveThreadsTo, ThreadDestination } from '@/lib/thread-actions';
|
||||
import { useSearchValue } from '@/hooks/use-search-value';
|
||||
@@ -277,7 +278,7 @@ export function ThreadContextMenu({
|
||||
{
|
||||
id: 'toggle-read',
|
||||
label: isUnread ? t('common.mail.markAsRead') : t('common.mail.markAsUnread'),
|
||||
icon: <Mail className="mr-2.5 h-4 w-4" />,
|
||||
icon: isUnread ? <MailOpen className="mr-2.5 h-4 w-4" /> : <Mail className="mr-2.5 h-4 w-4" />,
|
||||
shortcut: 'U',
|
||||
action: handleReadUnread,
|
||||
disabled: false,
|
||||
|
||||
@@ -556,7 +556,12 @@ export const MailList = memo(({ isCompact }: MailListProps) => {
|
||||
const messageThreadId = message.threadId ?? message.id;
|
||||
|
||||
// Update local state immediately for optimistic UI
|
||||
setMail((prev) => ({ ...prev, selected: messageThreadId }));
|
||||
setMail((prev) => ({
|
||||
...prev,
|
||||
selected: messageThreadId,
|
||||
replyComposerOpen: false,
|
||||
forwardComposerOpen: false
|
||||
}));
|
||||
|
||||
// Update URL param without navigation
|
||||
void setThreadId(messageThreadId);
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
Archive,
|
||||
RotateCw,
|
||||
Mail,
|
||||
MailOpen,
|
||||
} from 'lucide-react';
|
||||
import {
|
||||
Dialog,
|
||||
@@ -54,9 +55,9 @@ import { useSession } from '@/lib/auth-client';
|
||||
import { useStats } from '@/hooks/use-stats';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { getMail } from '@/actions/mail';
|
||||
import { getMail, markAsRead } from '@/actions/mail';
|
||||
import { SearchBar } from './search-bar';
|
||||
import { ParsedMessage } from '@/types';
|
||||
import { useQueryState } from 'nuqs';
|
||||
import { cn } from '@/lib/utils';
|
||||
import items from './demo.json';
|
||||
import { useAtom } from 'jotai';
|
||||
@@ -72,8 +73,7 @@ export function DemoMailLayout() {
|
||||
const isValidating = false;
|
||||
const isLoading = false;
|
||||
const isDesktop = true;
|
||||
const searchParams = useSearchParams();
|
||||
const threadIdParam = searchParams?.get('threadId');
|
||||
const threadIdParam = useQueryState('threadId');
|
||||
const [activeCategory, setActiveCategory] = useState('Primary');
|
||||
const [filteredItems, setFilteredItems] = useState(items);
|
||||
|
||||
@@ -247,17 +247,12 @@ export function MailLayout() {
|
||||
return () => window.removeEventListener('resize', checkIsMobile);
|
||||
}, []);
|
||||
|
||||
const searchParams = useSearchParams();
|
||||
const threadIdParam = searchParams.get('threadId');
|
||||
|
||||
// No need to track threadIdParam with a separate state
|
||||
const [threadId, setThreadId] = useQueryState('threadId');
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
// Update URL to remove threadId parameter
|
||||
const currentParams = new URLSearchParams(searchParams.toString());
|
||||
currentParams.delete('threadId');
|
||||
router.push(`/mail/${folder}?${currentParams.toString()}`);
|
||||
}, [router, folder, searchParams]);
|
||||
setThreadId(null);
|
||||
router.push(`/mail/${folder}`);
|
||||
}, [router, folder, setThreadId]);
|
||||
|
||||
// Search bar is always visible now, no need for keyboard shortcuts to toggle it
|
||||
useHotKey('Esc', (event) => {
|
||||
@@ -265,8 +260,6 @@ export function MailLayout() {
|
||||
// Handle other Esc key functionality if needed
|
||||
});
|
||||
|
||||
const searchIconRef = useRef<any>(null);
|
||||
|
||||
// Add mailto protocol handler registration
|
||||
useEffect(() => {
|
||||
// Register as a mailto protocol handler if browser supports it
|
||||
@@ -298,7 +291,7 @@ export function MailLayout() {
|
||||
className="rounded-inherit gap-1.5 overflow-hidden"
|
||||
>
|
||||
<ResizablePanel
|
||||
className={cn('border-none !bg-transparent', threadIdParam ? 'md:hidden lg:block' : '')}
|
||||
className={cn('border-none !bg-transparent', threadId ? 'md:hidden lg:block' : '')}
|
||||
defaultSize={isMobile ? 100 : 25}
|
||||
minSize={isMobile ? 100 : 25}
|
||||
>
|
||||
@@ -362,7 +355,7 @@ export function MailLayout() {
|
||||
<div className="flex flex-1 justify-center">
|
||||
<SearchBar />
|
||||
</div>
|
||||
{!threadIdParam && (
|
||||
{!threadId && (
|
||||
<div className="flex items-center">
|
||||
<CategorySelect />
|
||||
</div>
|
||||
@@ -402,13 +395,13 @@ export function MailLayout() {
|
||||
<ResizablePanel
|
||||
className={cn(
|
||||
'bg-offsetLight dark:bg-offsetDark shadow-sm md:rounded-2xl md:border md:shadow-sm',
|
||||
threadIdParam ? 'md:flex' : 'hidden',
|
||||
threadId ? 'md:flex' : 'hidden',
|
||||
)}
|
||||
defaultSize={75}
|
||||
minSize={25}
|
||||
>
|
||||
<div className="relative hidden h-[calc(100vh-(12px+14px))] flex-1 md:block">
|
||||
<ThreadDisplay onClose={handleClose} id={threadIdParam ?? undefined} />
|
||||
<ThreadDisplay onClose={handleClose} id={threadId ?? undefined} />
|
||||
</div>
|
||||
</ResizablePanel>
|
||||
</>
|
||||
@@ -418,7 +411,7 @@ export function MailLayout() {
|
||||
{/* Mobile Drawer */}
|
||||
{isMobile && (
|
||||
<Drawer
|
||||
open={!!threadIdParam}
|
||||
open={!!threadId}
|
||||
onOpenChange={(isOpen) => {
|
||||
if (!isOpen) handleClose();
|
||||
}}
|
||||
@@ -429,8 +422,8 @@ export function MailLayout() {
|
||||
</DrawerHeader>
|
||||
<div className="flex h-full flex-col overflow-hidden">
|
||||
<div className="flex-1 overflow-hidden">
|
||||
{threadIdParam ? (
|
||||
<ThreadDisplay onClose={handleClose} isMobile={true} id={threadIdParam} />
|
||||
{threadId ? (
|
||||
<ThreadDisplay onClose={handleClose} isMobile={true} id={threadId} />
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
@@ -480,6 +473,25 @@ function BulkSelectActions() {
|
||||
const { mutate: mutateThreads } = useThreads();
|
||||
const { mutate: mutateStats } = useStats();
|
||||
|
||||
const handleMarkAsRead = useCallback(async () => {
|
||||
try {
|
||||
const response = await markAsRead({ ids: mail.bulkSelected });
|
||||
if (response.success) {
|
||||
await mutateThreads();
|
||||
await mutateStats();
|
||||
setMail((prev) => ({
|
||||
...prev,
|
||||
bulkSelected: []
|
||||
}));
|
||||
toast.success(t('common.mail.markedAsRead'));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error marking as read", error);
|
||||
toast.error(t("common.mail.failedToMarkAsRead"));
|
||||
}
|
||||
}, [mail, setMail, mutateThreads, mutateStats, t]);
|
||||
|
||||
|
||||
const onMoveSuccess = useCallback(async () => {
|
||||
await mutateThreads();
|
||||
await mutateStats();
|
||||
@@ -548,6 +560,18 @@ function BulkSelectActions() {
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{t('common.mail.mute')}</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="md:h-fit md:px-2"
|
||||
onClick={handleMarkAsRead}
|
||||
>
|
||||
<MailOpen />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{t('common.mail.markAsRead')}</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
{availableActions.map((action) => (
|
||||
<Tooltip key={action}>
|
||||
@@ -632,18 +656,10 @@ const Categories = () => {
|
||||
function CategorySelect() {
|
||||
const [, setSearchValue] = useSearchValue();
|
||||
const categories = Categories();
|
||||
const [defaultCategory, setDefaultCategory] = useState('Primary');
|
||||
|
||||
// Safely access localStorage on the client side only
|
||||
useEffect(() => {
|
||||
// Check if we're in the browser environment
|
||||
if (typeof window !== 'undefined') {
|
||||
const savedCategory = localStorage.getItem('mailActiveCategory');
|
||||
if (savedCategory) {
|
||||
setDefaultCategory(savedCategory);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
const router = useRouter();
|
||||
const [category, setCategory] = useQueryState('category', {
|
||||
defaultValue: 'Primary'
|
||||
});
|
||||
|
||||
return (
|
||||
<Select
|
||||
@@ -651,9 +667,6 @@ function CategorySelect() {
|
||||
// Find the category and trigger its selection
|
||||
const category = categories.find((cat) => cat.id === value);
|
||||
|
||||
// Always update the state to match the selected value
|
||||
setDefaultCategory(value);
|
||||
|
||||
if (category) {
|
||||
// Update search value based on category
|
||||
const searchValueState = {
|
||||
@@ -663,13 +676,11 @@ function CategorySelect() {
|
||||
};
|
||||
setSearchValue(searchValueState);
|
||||
|
||||
// Save to localStorage (safely on client-side)
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.setItem('mailActiveCategory', value);
|
||||
}
|
||||
// Update category in URL - nuqs will preserve other params automatically
|
||||
setCategory(value);
|
||||
}
|
||||
}}
|
||||
defaultValue={defaultCategory}
|
||||
value={category}
|
||||
>
|
||||
<SelectTrigger className="bg-popover h-9 w-36">
|
||||
<SelectValue placeholder="Select category" />
|
||||
|
||||
@@ -43,6 +43,8 @@ import { toast } from 'sonner';
|
||||
import type { z } from 'zod';
|
||||
import { useSettings } from '@/hooks/use-settings';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { useMail } from '@/components/mail/use-mail';
|
||||
|
||||
|
||||
// Define state interfaces
|
||||
interface ComposerState {
|
||||
@@ -116,8 +118,6 @@ const aiReducer = (state: AIState, action: AIAction): AIState => {
|
||||
|
||||
interface ReplyComposeProps {
|
||||
emailData: ParsedMessage[];
|
||||
isOpen?: boolean;
|
||||
setIsOpen?: Dispatch<SetStateAction<boolean>>;
|
||||
mode?: 'reply' | 'forward';
|
||||
}
|
||||
|
||||
@@ -126,9 +126,20 @@ type FormData = {
|
||||
to: string;
|
||||
};
|
||||
|
||||
export default function ReplyCompose({ emailData, isOpen, setIsOpen, mode = 'reply' }: ReplyComposeProps) {
|
||||
export default function ReplyCompose({ emailData, mode = 'reply' }: ReplyComposeProps) {
|
||||
const [attachments, setAttachments] = useState<File[]>([]);
|
||||
const { data: session } = useSession();
|
||||
const [mail, setMail] = useMail();
|
||||
|
||||
// Use global state instead of local state
|
||||
const composerIsOpen = mode === 'reply' ? mail.replyComposerOpen : mail.forwardComposerOpen;
|
||||
const setComposerIsOpen = (value: boolean) => {
|
||||
setMail((prev: typeof mail) => ({
|
||||
...prev,
|
||||
replyComposerOpen: mode === 'reply' ? value : prev.replyComposerOpen,
|
||||
forwardComposerOpen: mode === 'forward' ? value : prev.forwardComposerOpen,
|
||||
}));
|
||||
};
|
||||
|
||||
// Use reducers instead of multiple useState
|
||||
const [composerState, composerDispatch] = useReducer(composerReducer, {
|
||||
@@ -150,16 +161,6 @@ export default function ReplyCompose({ emailData, isOpen, setIsOpen, mode = 'rep
|
||||
const composerRef = useRef<HTMLFormElement>(null);
|
||||
const t = useTranslations();
|
||||
|
||||
// Use external state if provided, otherwise use internal state
|
||||
const composerIsOpen = isOpen !== undefined ? isOpen : composerState.isComposerOpen;
|
||||
const setComposerIsOpen = (value: boolean) => {
|
||||
if (setIsOpen) {
|
||||
setIsOpen(value);
|
||||
} else {
|
||||
composerDispatch({ type: 'SET_COMPOSER_OPEN', payload: value });
|
||||
}
|
||||
};
|
||||
|
||||
// Handle keyboard shortcuts for sending email
|
||||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||||
// Check for Cmd/Ctrl + Enter
|
||||
@@ -644,11 +645,30 @@ ${email.decodedBody || 'No content'}
|
||||
}
|
||||
};
|
||||
|
||||
// Add this effect near other useEffects
|
||||
useEffect(() => {
|
||||
if (!composerIsOpen) {
|
||||
// Reset form state
|
||||
form.reset();
|
||||
// Reset attachments
|
||||
setAttachments([]);
|
||||
// Reset AI state
|
||||
aiDispatch({ type: 'RESET' });
|
||||
// Reset to emails if in forward mode
|
||||
if (mode === 'forward') {
|
||||
setToEmails([]);
|
||||
setToInput('');
|
||||
}
|
||||
// Reset editor key to force a fresh instance
|
||||
composerDispatch({ type: 'INCREMENT_EDITOR_KEY' });
|
||||
}
|
||||
}, [composerIsOpen, form, mode]);
|
||||
|
||||
// Simplified composer visibility check
|
||||
if (!composerIsOpen) {
|
||||
if (mode === 'reply') {
|
||||
return (
|
||||
<div className="bg-offsetLight dark:bg-offsetDark w-full p-2">
|
||||
<div className="bg-offsetLight dark:bg-offsetDark w-full px-2">
|
||||
<Button
|
||||
onClick={toggleComposer}
|
||||
className="flex h-12 w-full items-center justify-center gap-2 rounded-md"
|
||||
@@ -667,11 +687,11 @@ ${email.decodedBody || 'No content'}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-offsetLight dark:bg-offsetDark w-full p-2">
|
||||
<div className="bg-offsetLight dark:bg-offsetDark w-full px-2">
|
||||
<form
|
||||
ref={composerRef}
|
||||
className={cn(
|
||||
'border-border ring-offset-background flex flex-col space-y-2.5 rounded-[10px] border px-2 py-2 transition-all duration-300 ease-in-out',
|
||||
'border-border ring-offset-background relative z-20 flex flex-col space-y-2.5 rounded-[10px] border px-2 py-2 transition-all duration-300 ease-in-out',
|
||||
composerState.isEditorFocused ? 'ring-2 ring-[#3D3D3D] ring-offset-1' : '',
|
||||
)}
|
||||
style={{
|
||||
@@ -896,14 +916,17 @@ ${email.decodedBody || 'No content'}
|
||||
}
|
||||
|
||||
// Extract smaller components
|
||||
const DragOverlay = () => (
|
||||
<div className="bg-background/80 border-primary/30 absolute inset-0 z-50 m-4 flex items-center justify-center rounded-2xl border-2 border-dashed backdrop-blur-sm">
|
||||
<div className="text-muted-foreground flex flex-col items-center gap-2">
|
||||
<Paperclip className="text-muted-foreground h-12 w-12" />
|
||||
<p className="text-lg font-medium">{t('common.replyCompose.dropFiles')}</p>
|
||||
const DragOverlay = () => {
|
||||
const t = useTranslations();
|
||||
return (
|
||||
<div className="bg-background/80 border-primary/30 absolute inset-0 z-50 m-4 flex items-center justify-center rounded-2xl border-2 border-dashed backdrop-blur-sm">
|
||||
<div className="text-muted-foreground flex flex-col items-center gap-2">
|
||||
<Paperclip className="text-muted-foreground h-12 w-12" />
|
||||
<p className="text-lg font-medium">{t('common.replyCompose.dropFiles')}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
const CloseButton = ({ onClick }: { onClick: (e: React.MouseEvent) => void }) => (
|
||||
<Button
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
ArchiveX,
|
||||
Expand,
|
||||
Forward,
|
||||
ForwardIcon,
|
||||
Mail,
|
||||
MoreVertical,
|
||||
Reply,
|
||||
@@ -11,14 +12,13 @@ import {
|
||||
StarOff,
|
||||
X,
|
||||
} from 'lucide-react';
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { DropdownMenuContent, DropdownMenuItem } from '@/components/ui/dropdown-menu';
|
||||
import { DropdownMenu, DropdownMenuTrigger } from '@/components/ui/dropdown-menu';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { useSearchParams, useParams } from 'next/navigation';
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
|
||||
import { moveThreadsTo, ThreadDestination } from '@/lib/thread-actions';
|
||||
import { markAsUnread } from '@/actions/mail';
|
||||
import { MoreVerticalIcon } from '../icons/animated/more-vertical';
|
||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { useThread, useThreads } from '@/hooks/use-threads';
|
||||
@@ -27,12 +27,13 @@ import { ExpandIcon } from '../icons/animated/expand';
|
||||
import { MailDisplaySkeleton } from './mail-skeleton';
|
||||
import { ReplyIcon } from '../icons/animated/reply';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { markAsUnread } from '@/actions/mail';
|
||||
import { modifyLabels } from '@/actions/mail';
|
||||
import { useStats } from '@/hooks/use-stats';
|
||||
import ThreadSubject from './thread-subject';
|
||||
import { XIcon } from '../icons/animated/x';
|
||||
import ReplyCompose from './reply-composer';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useMail } from '../mail/use-mail';
|
||||
import { NotesPanel } from './note-panel';
|
||||
import { cn, FOLDERS } from '@/lib/utils';
|
||||
import MailDisplay from './mail-display';
|
||||
@@ -40,7 +41,6 @@ import { ParsedMessage } from '@/types';
|
||||
import { Inbox } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
import Link from 'next/link';
|
||||
import { useMail } from '../mail/use-mail';
|
||||
|
||||
interface ThreadDisplayProps {
|
||||
threadParam?: any;
|
||||
@@ -52,6 +52,7 @@ interface ThreadDisplayProps {
|
||||
|
||||
export function ThreadDemo({ messages, isMobile }: ThreadDisplayProps) {
|
||||
const isFullscreen = false;
|
||||
const [mail, setMail] = useMail();
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -68,69 +69,6 @@ export function ThreadDemo({ messages, isMobile }: ThreadDisplayProps) {
|
||||
isFullscreen ? 'fixed inset-0 z-50' : '',
|
||||
)}
|
||||
>
|
||||
<div className="flex flex-shrink-0 items-center border-b p-2">
|
||||
<div className="flex flex-1 items-center gap-2">
|
||||
<Button variant="ghost" className="md:h-fit md:px-2" disabled={!messages}>
|
||||
<X className="h-4 w-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</Button>
|
||||
|
||||
<ThreadSubject subject={'Join the Email Revolution with Zero!'} />
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="ghost" className="md:h-fit md:px-2" disabled={!messages}>
|
||||
{isFullscreen ? (
|
||||
<ExpandIcon className="h-4 w-4" />
|
||||
) : (
|
||||
<ExpandIcon className="h-4 w-4" />
|
||||
)}
|
||||
<span className="sr-only">
|
||||
{isFullscreen ? 'Exit fullscreen' : 'Enter fullscreen'}
|
||||
</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
{isFullscreen ? 'Exit fullscreen' : 'Enter fullscreen'}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="ghost" className="md:h-fit md:px-2" disabled={!messages}>
|
||||
<ArchiveIcon className="relative top-0.5 h-4 w-4" />
|
||||
<span className="sr-only">Archive</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Archive</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="ghost" className="md:h-fit md:px-2" disabled={!messages}>
|
||||
<ReplyIcon className="h-4 w-4" />
|
||||
<span className="sr-only">Reply</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Reply</TooltipContent>
|
||||
</Tooltip>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" className="md:h-fit md:px-2" disabled={!messages}>
|
||||
<MoreVerticalIcon className="h-4 w-4" />
|
||||
<span className="sr-only">More</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem>
|
||||
<ArchiveX className="mr-2 h-4 w-4" /> Move to spam
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<Forward className="mr-2 h-4 w-4" /> Forward
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex min-h-0 flex-1 flex-col overflow-hidden">
|
||||
<ScrollArea className="flex-1" type="scroll">
|
||||
<div className="pb-4">
|
||||
@@ -154,9 +92,12 @@ export function ThreadDemo({ messages, isMobile }: ThreadDisplayProps) {
|
||||
))}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
<div className="relative flex-shrink-0 md:top-2">
|
||||
<div className="relative flex-shrink-0 md:top-1">
|
||||
{messages ? (
|
||||
<ReplyCompose emailData={messages} isOpen={false} setIsOpen={() => {}} />
|
||||
<ReplyCompose
|
||||
emailData={messages}
|
||||
mode={mail.forwardComposerOpen ? 'forward' : 'reply'}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
@@ -184,17 +125,24 @@ function ThreadActionButton({
|
||||
const iconRef = useRef<any>(null);
|
||||
|
||||
return (
|
||||
<Button
|
||||
disabled={disabled}
|
||||
onClick={onClick}
|
||||
variant="ghost"
|
||||
className={cn('md:h-fit md:px-2', className)}
|
||||
onMouseEnter={() => iconRef.current?.startAnimation?.()}
|
||||
onMouseLeave={() => iconRef.current?.stopAnimation?.()}
|
||||
>
|
||||
<Icon ref={iconRef} className="h-4 w-4" />
|
||||
<span className="sr-only">{label}</span>
|
||||
</Button>
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
disabled={disabled}
|
||||
onClick={onClick}
|
||||
variant="ghost"
|
||||
className={cn('md:h-fit md:px-2', className)}
|
||||
onMouseEnter={() => iconRef.current?.startAnimation?.()}
|
||||
onMouseLeave={() => iconRef.current?.stopAnimation?.()}
|
||||
>
|
||||
<Icon ref={iconRef} className="h-4 w-4" />
|
||||
<span className="sr-only">{label}</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{label}</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -203,15 +151,13 @@ export function ThreadDisplay({ threadParam, onClose, isMobile, id }: ThreadDisp
|
||||
const { mutate: mutateThreads } = useThreads();
|
||||
const searchParams = useSearchParams();
|
||||
const [isMuted, setIsMuted] = useState(false);
|
||||
const [isReplyOpen, setIsReplyOpen] = useState(false);
|
||||
const [isFullscreen, setIsFullscreen] = useState(false);
|
||||
const [isForwardOpen, setIsForwardOpen] = useState(false);
|
||||
const [mail, setMail] = useMail();
|
||||
const t = useTranslations();
|
||||
const { mutate: mutateStats } = useStats();
|
||||
const { folder } = useParams<{ folder: string }>();
|
||||
const threadIdParam = searchParams.get('threadId');
|
||||
const threadId = threadParam ?? threadIdParam ?? '';
|
||||
const [, setMailState] = useMail();
|
||||
|
||||
const moreVerticalIconRef = useRef<any>(null);
|
||||
|
||||
@@ -220,8 +166,14 @@ export function ThreadDisplay({ threadParam, onClose, isMobile, id }: ThreadDisp
|
||||
const isInSpam = folder === FOLDERS.SPAM;
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
// Reset reply composer state when closing thread display
|
||||
setMail((prev) => ({
|
||||
...prev,
|
||||
replyComposerOpen: false,
|
||||
forwardComposerOpen: false
|
||||
}));
|
||||
onClose?.();
|
||||
}, [onClose]);
|
||||
}, [onClose, setMail]);
|
||||
|
||||
const moveThreadTo = useCallback(
|
||||
async (destination: ThreadDestination) => {
|
||||
@@ -237,11 +189,12 @@ export function ThreadDisplay({ threadParam, onClose, isMobile, id }: ThreadDisp
|
||||
|
||||
toast.promise(promise(), {
|
||||
loading: t('common.actions.moving'),
|
||||
success: destination === 'inbox'
|
||||
? t('common.actions.movedToInbox')
|
||||
: destination === 'spam'
|
||||
? t('common.actions.movedToSpam')
|
||||
: t('common.actions.archived'),
|
||||
success:
|
||||
destination === 'inbox'
|
||||
? t('common.actions.movedToInbox')
|
||||
: destination === 'spam'
|
||||
? t('common.actions.movedToSpam')
|
||||
: t('common.actions.archived'),
|
||||
error: t('common.actions.failedToMove'),
|
||||
});
|
||||
},
|
||||
@@ -250,12 +203,12 @@ export function ThreadDisplay({ threadParam, onClose, isMobile, id }: ThreadDisp
|
||||
|
||||
const handleMarkAsUnread = useCallback(async () => {
|
||||
if (!emailData || !threadId) return;
|
||||
|
||||
|
||||
const promise = async () => {
|
||||
const result = await markAsUnread({ ids: [threadId] });
|
||||
if (!result.success) throw new Error('Failed to mark as unread');
|
||||
|
||||
setMailState(prev => ({ ...prev, bulkSelected: [] }));
|
||||
|
||||
setMail((prev) => ({ ...prev, bulkSelected: [] }));
|
||||
await Promise.all([mutateStats(), mutateThreads()]);
|
||||
handleClose();
|
||||
};
|
||||
@@ -265,7 +218,7 @@ export function ThreadDisplay({ threadParam, onClose, isMobile, id }: ThreadDisp
|
||||
success: t('common.mail.markedAsUnread'),
|
||||
error: t('common.mail.failedToMarkAsUnread'),
|
||||
});
|
||||
}, [emailData, threadId, mutateStats, mutateThreads, t, handleClose, setMailState]);
|
||||
}, [emailData, threadId, mutateStats, mutateThreads, t, handleClose, setMail]);
|
||||
|
||||
const handleFavourites = async () => {
|
||||
if (!emailData || !threadId) return;
|
||||
@@ -274,27 +227,23 @@ export function ThreadDisplay({ threadParam, onClose, isMobile, id }: ThreadDisp
|
||||
toast.promise(
|
||||
modifyLabels({ threadId: [threadId], removeLabels: ['STARRED'] }).then(() => done),
|
||||
{
|
||||
success: 'Removed from favourites.',
|
||||
loading: 'Removing from favourites',
|
||||
error: 'Failed to remove from favourites.',
|
||||
success: t('common.actions.removedFromFavorites'),
|
||||
loading: t('common.actions.removingFromFavorites'),
|
||||
error: t('common.actions.failedToRemoveFromFavorites'),
|
||||
},
|
||||
);
|
||||
} else {
|
||||
toast.promise(
|
||||
modifyLabels({ threadId: [threadId], addLabels: ['STARRED'] }).then(() => done),
|
||||
{
|
||||
success: 'Added to favourites.',
|
||||
loading: 'Adding to favourites.',
|
||||
error: 'Failed to add to favourites.',
|
||||
success: t('common.actions.addedToFavorites'),
|
||||
loading: t('common.actions.addingToFavorites'),
|
||||
error: t('common.actions.failedToAddToFavorites'),
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleForward = () => {
|
||||
setIsForwardOpen(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const handleEsc = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape') {
|
||||
@@ -321,67 +270,6 @@ export function ThreadDisplay({ threadParam, onClose, isMobile, id }: ThreadDisp
|
||||
isFullscreen ? 'fixed inset-0 z-50' : '',
|
||||
)}
|
||||
>
|
||||
<div className="flex flex-shrink-0 items-center border-b px-1 pb-1 md:px-3 md:pb-2 md:pt-[10px]">
|
||||
<div className="flex flex-1 items-center">
|
||||
<ThreadActionButton
|
||||
icon={XIcon}
|
||||
label={t('common.actions.close')}
|
||||
onClick={handleClose}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-1 sm:gap-2 md:gap-6">
|
||||
<ThreadActionButton
|
||||
icon={isFullscreen ? ExpandIcon : ExpandIcon}
|
||||
label={
|
||||
isFullscreen
|
||||
? t('common.threadDisplay.exitFullscreen')
|
||||
: t('common.threadDisplay.enterFullscreen')
|
||||
}
|
||||
onClick={() => setIsFullscreen(!isFullscreen)}
|
||||
/>
|
||||
|
||||
<ThreadActionButton
|
||||
icon={ArchiveIcon}
|
||||
label={t('common.threadDisplay.archive')}
|
||||
disabled={true}
|
||||
className="relative top-0.5"
|
||||
/>
|
||||
|
||||
<ThreadActionButton
|
||||
icon={!emailData || emailData[0]?.tags?.includes('STARRED') ? StarOff : Star}
|
||||
label={t('common.threadDisplay.favourites')}
|
||||
onClick={handleFavourites}
|
||||
className="relative top-0.5"
|
||||
/>
|
||||
|
||||
<ThreadActionButton
|
||||
icon={ReplyIcon}
|
||||
label={t('common.threadDisplay.reply')}
|
||||
disabled={true}
|
||||
/>
|
||||
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="h-8 w-8 p-0 md:h-fit md:w-auto md:px-2"
|
||||
disabled={true}
|
||||
>
|
||||
<MoreVerticalIcon className="h-4 w-4" />
|
||||
<span className="sr-only">{t('common.threadDisplay.moreOptions')}</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem>
|
||||
<ArchiveX className="mr-2 h-4 w-4" /> {t('common.threadDisplay.moveToSpam')}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<ReplyAll className="mr-2 h-4 w-4" /> {t('common.threadDisplay.replyAll')}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex min-h-0 flex-1 flex-col overflow-hidden">
|
||||
<ScrollArea className="h-full flex-1" type="auto">
|
||||
<div className="pb-4">
|
||||
@@ -402,7 +290,7 @@ export function ThreadDisplay({ threadParam, onClose, isMobile, id }: ThreadDisp
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'bg-offsetLight dark:bg-offsetDark relative flex flex-col overflow-hidden transition-all duration-300',
|
||||
'bg-offsetLight dark:bg-offsetDark relative flex flex-col transition-all duration-300',
|
||||
isMobile ? 'h-full' : 'h-full',
|
||||
!isMobile && !isFullscreen && 'rounded-r-lg',
|
||||
isFullscreen ? 'fixed inset-0 z-50' : '',
|
||||
@@ -410,15 +298,16 @@ export function ThreadDisplay({ threadParam, onClose, isMobile, id }: ThreadDisp
|
||||
>
|
||||
<div className="flex flex-shrink-0 items-center border-b px-1 pb-1 md:px-3 md:pb-2 md:pt-[10px]">
|
||||
<div className="flex flex-1 items-center gap-2">
|
||||
<Link prefetch href={`/mail/${folder}`}>
|
||||
<Button variant="ghost" className="md:h-fit md:px-2">
|
||||
<X className="h-4 w-4 hover:text-red-500" />
|
||||
</Button>
|
||||
</Link>
|
||||
<ThreadActionButton
|
||||
icon={X}
|
||||
label={t('common.actions.close')}
|
||||
onClick={handleClose}
|
||||
/>
|
||||
<ThreadSubject subject={emailData[0]?.subject} />
|
||||
</div>
|
||||
<div className="flex items-center md:gap-2">
|
||||
<NotesPanel threadId={threadId} />
|
||||
{/* disable notes for now, it's still a bit buggy and not ready for prod. */}
|
||||
{/* <NotesPanel threadId={threadId} /> */}
|
||||
<ThreadActionButton
|
||||
icon={Expand}
|
||||
label={
|
||||
@@ -429,7 +318,7 @@ export function ThreadDisplay({ threadParam, onClose, isMobile, id }: ThreadDisp
|
||||
disabled={!emailData}
|
||||
onClick={() => setIsFullscreen(!isFullscreen)}
|
||||
/>
|
||||
{(isInSpam || isInArchive) ? (
|
||||
{isInSpam || isInArchive ? (
|
||||
<ThreadActionButton
|
||||
icon={Inbox}
|
||||
label={t('common.mail.moveToInbox')}
|
||||
@@ -462,53 +351,49 @@ export function ThreadDisplay({ threadParam, onClose, isMobile, id }: ThreadDisp
|
||||
icon={Reply}
|
||||
label={t('common.threadDisplay.reply')}
|
||||
disabled={!emailData}
|
||||
onClick={() => setIsReplyOpen(true)}
|
||||
className={cn(mail.replyComposerOpen && "bg-primary/10")}
|
||||
onClick={() => {
|
||||
if (mail.forwardComposerOpen) {
|
||||
// If forward is open, close it and open reply
|
||||
setMail((prev) => ({
|
||||
...prev,
|
||||
forwardComposerOpen: false,
|
||||
replyComposerOpen: true
|
||||
}));
|
||||
} else {
|
||||
// Toggle reply
|
||||
setMail((prev) => ({
|
||||
...prev,
|
||||
replyComposerOpen: !prev.replyComposerOpen
|
||||
}));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<ThreadActionButton
|
||||
icon={Forward}
|
||||
label={t('common.threadDisplay.forward')}
|
||||
disabled={!emailData}
|
||||
className={cn(mail.forwardComposerOpen && "bg-primary/10")}
|
||||
onClick={() => {
|
||||
if (mail.replyComposerOpen) {
|
||||
// If reply is open, close it and open forward
|
||||
setMail((prev) => ({
|
||||
...prev,
|
||||
replyComposerOpen: false,
|
||||
forwardComposerOpen: true
|
||||
}));
|
||||
} else {
|
||||
// Toggle forward
|
||||
setMail((prev) => ({
|
||||
...prev,
|
||||
forwardComposerOpen: !prev.forwardComposerOpen
|
||||
}));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/* <DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="md:h-fit md:px-2"
|
||||
disabled={!emailData}
|
||||
onMouseEnter={() => moreVerticalIconRef.current?.startAnimation?.()}
|
||||
onMouseLeave={() => moreVerticalIconRef.current?.stopAnimation?.()}
|
||||
>
|
||||
<MoreVertical ref={moreVerticalIconRef} className="h-4 w-4" />
|
||||
<span className="sr-only">{t('common.threadDisplay.moreOptions')}</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
{isInInbox && (
|
||||
<DropdownMenuItem onClick={() => moveThreadTo('spam')}>
|
||||
<ArchiveX className="mr-2 h-4 w-4" /> {t('common.threadDisplay.moveToSpam')}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{isInSpam && (
|
||||
<DropdownMenuItem onClick={() => moveThreadTo('inbox')}>
|
||||
<Inbox className="mr-2 h-4 w-4" /> {t('common.mail.moveToInbox')}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{isInArchive && (
|
||||
<DropdownMenuItem onClick={() => moveThreadTo('inbox')}>
|
||||
<Inbox className="mr-2 h-4 w-4" /> {t('common.mail.moveToInbox')}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuItem>
|
||||
<ReplyAll className="mr-2 h-4 w-4" /> {t('common.threadDisplay.replyAll')}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={handleForward}>
|
||||
<Forward className="mr-2 h-4 w-4" /> {t('common.threadDisplay.forward')}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={handleMarkAsUnread}>
|
||||
<Mail className="mr-2 h-4 w-4" /> {t('common.mail.markAsUnread')}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>{t('common.threadDisplay.addLabel')}</DropdownMenuItem>
|
||||
<DropdownMenuItem>{t('common.threadDisplay.muteThread')}</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu> */}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex min-h-0 flex-1 flex-col overflow-hidden">
|
||||
<div className="flex min-h-0 flex-1 flex-col">
|
||||
<ScrollArea className="h-full flex-1" type="auto">
|
||||
<div className="pb-4">
|
||||
{(emailData || []).map((message, index) => (
|
||||
@@ -531,18 +416,13 @@ export function ThreadDisplay({ threadParam, onClose, isMobile, id }: ThreadDisp
|
||||
))}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
<div className={`relative ${isFullscreen ? '' : 'top-1'} flex-shrink-0`}>
|
||||
<div className={cn(
|
||||
'relative z-10 bg-offsetLight dark:bg-offsetDark',
|
||||
isFullscreen ? 'mb-2' : ''
|
||||
)}>
|
||||
<ReplyCompose
|
||||
emailData={emailData}
|
||||
isOpen={isReplyOpen || isForwardOpen}
|
||||
setIsOpen={(open) => {
|
||||
if (isForwardOpen) {
|
||||
setIsForwardOpen(open);
|
||||
} else {
|
||||
setIsReplyOpen(open);
|
||||
}
|
||||
}}
|
||||
mode={isForwardOpen ? 'forward' : 'reply'}
|
||||
mode={mail.forwardComposerOpen ? 'forward' : 'reply'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,11 +5,15 @@ import { type Mail } from "@/components/mail/data";
|
||||
type Config = {
|
||||
selected: Mail["id"] | null;
|
||||
bulkSelected: Mail["id"][];
|
||||
replyComposerOpen: boolean;
|
||||
forwardComposerOpen: boolean;
|
||||
};
|
||||
|
||||
const configAtom = atom<Config>({
|
||||
selected: null,
|
||||
bulkSelected: [],
|
||||
replyComposerOpen: false,
|
||||
forwardComposerOpen: false,
|
||||
});
|
||||
|
||||
export function useMail() {
|
||||
|
||||
@@ -153,7 +153,7 @@ export const navigationConfig: Record<string, NavConfig> = {
|
||||
},
|
||||
{
|
||||
title: 'navigation.settings.security',
|
||||
url: '#',
|
||||
url: '/settings/security',
|
||||
icon: ShieldCheckIcon,
|
||||
disabled: true,
|
||||
},
|
||||
@@ -169,7 +169,7 @@ export const navigationConfig: Record<string, NavConfig> = {
|
||||
},
|
||||
{
|
||||
title: 'navigation.settings.shortcuts',
|
||||
url: '#',
|
||||
url: '/settings/shortcuts',
|
||||
icon: KeyboardIcon,
|
||||
disabled: true,
|
||||
},
|
||||
|
||||
@@ -16,6 +16,7 @@ export function useMailNavigation({ items, containerRef, onNavigate }: UseMailNa
|
||||
const [quickActionIndex, setQuickActionIndex] = useState(0);
|
||||
const hoveredMailRef = useRef<string | null>(null);
|
||||
const keyboardActiveRef = useRef(false);
|
||||
const lastMoveTime = useRef(0);
|
||||
|
||||
useEffect(() => {
|
||||
if (!keyboardActiveRef.current) {
|
||||
@@ -186,6 +187,64 @@ export function useMailNavigation({ items, containerRef, onNavigate }: UseMailNa
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
let isProcessingKey = false;
|
||||
const MOVE_DELAY = 100; // Decreased from 150ms to 100ms for faster movement
|
||||
|
||||
const handleKeyDown = async (event: KeyboardEvent) => {
|
||||
if (isQuickActionMode) return;
|
||||
|
||||
// For non-repeat events (initial press), let the useHotKey handlers manage it
|
||||
if (!event.repeat) return;
|
||||
|
||||
if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
|
||||
event.preventDefault(); // Prevent default browser behavior
|
||||
|
||||
// If we're already processing a previous key event, don't stack them
|
||||
if (isProcessingKey) return;
|
||||
|
||||
// Check if enough time has passed since the last movement
|
||||
const now = Date.now();
|
||||
if (now - lastMoveTime.current < MOVE_DELAY) return;
|
||||
|
||||
isProcessingKey = true;
|
||||
lastMoveTime.current = now;
|
||||
|
||||
await new Promise<void>(resolve => {
|
||||
requestAnimationFrame(() => {
|
||||
if (event.key === 'ArrowUp') {
|
||||
setFocusedIndex(prev => {
|
||||
const newIndex = prev === null ? items.length - 1 : Math.max(0, prev - 1);
|
||||
const threadElement = getThreadElement(newIndex);
|
||||
if (threadElement && containerRef.current) {
|
||||
threadElement.scrollIntoView({ block: 'nearest', behavior: 'auto' });
|
||||
}
|
||||
return newIndex;
|
||||
});
|
||||
} else if (event.key === 'ArrowDown') {
|
||||
setFocusedIndex(prev => {
|
||||
const newIndex = prev === null ? 0 : Math.min(items.length - 1, prev + 1);
|
||||
const threadElement = getThreadElement(newIndex);
|
||||
if (threadElement && containerRef.current) {
|
||||
threadElement.scrollIntoView({ block: 'nearest', behavior: 'auto' });
|
||||
}
|
||||
return newIndex;
|
||||
});
|
||||
}
|
||||
isProcessingKey = false;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
};
|
||||
}, [handleArrowUp, handleArrowDown, isQuickActionMode, items.length, getThreadElement, containerRef]);
|
||||
|
||||
return {
|
||||
focusedIndex,
|
||||
isQuickActionMode,
|
||||
|
||||
@@ -1,18 +1,36 @@
|
||||
{
|
||||
"common": {
|
||||
"actions": {
|
||||
"logout": "Logout",
|
||||
"back": "Back",
|
||||
"create": "Create Email",
|
||||
"saveChanges": "Save changes",
|
||||
"saving": "Saving...",
|
||||
"resetToDefaults": "Reset to Defaults",
|
||||
"close": "Close",
|
||||
"signingOut": "Signing out...",
|
||||
"signedOutSuccess": "Signed out successfully!",
|
||||
"logout": "خروج",
|
||||
"back": "رجوع",
|
||||
"create": "إنشاء بريد إلكتروني",
|
||||
"saveChanges": "حفظ التغييرات",
|
||||
"saving": "جاري الحفظ...",
|
||||
"resetToDefaults": "إعادة التعيين إلى الوضع الافتراضي",
|
||||
"close": "غلق",
|
||||
"signingOut": "تسجيل الخروج...",
|
||||
"signedOutSuccess": "تم تسجيل الخروج بنجاح!",
|
||||
"signOutError": "Error signing out",
|
||||
"refresh": "Refresh",
|
||||
"loading": "Loading..."
|
||||
"loading": "Loading...",
|
||||
"featureNotImplemented": "This feature is not implemented yet",
|
||||
"moving": "Moving...",
|
||||
"movedToInbox": "Moved to inbox",
|
||||
"movingToInbox": "Moving to inbox...",
|
||||
"movedToSpam": "Moved to spam",
|
||||
"movingToSpam": "Moving to spam...",
|
||||
"archiving": "Archiving...",
|
||||
"archived": "Archived",
|
||||
"failedToMove": "Failed to move message",
|
||||
"addingToFavorites": "Adding to favorites...",
|
||||
"removingFromFavorites": "Removing from favorites...",
|
||||
"addedToFavorites": "Added to favorites",
|
||||
"removedFromFavorites": "Removed from favorites",
|
||||
"failedToAddToFavorites": "Failed to add to favorites",
|
||||
"failedToRemoveFromFavorites": "Failed to remove from favorites",
|
||||
"failedToModifyFavorites": "Failed to modify favorites",
|
||||
"markingAsRead": "Marking as read...",
|
||||
"markingAsUnread": "Marking as unread..."
|
||||
},
|
||||
"themes": {
|
||||
"dark": "Dark",
|
||||
@@ -25,9 +43,9 @@
|
||||
"placeholder": "Type a command or search...",
|
||||
"noResults": "No results found",
|
||||
"groups": {
|
||||
"mail": "Mail",
|
||||
"settings": "Settings",
|
||||
"actions": "Actions",
|
||||
"mail": "البريد",
|
||||
"settings": "الإعدادات",
|
||||
"actions": "الإجراءات",
|
||||
"help": "Help",
|
||||
"navigation": "Navigation"
|
||||
},
|
||||
@@ -83,6 +101,7 @@
|
||||
},
|
||||
"mailCategories": {
|
||||
"primary": "Primary",
|
||||
"allMail": "All Mail",
|
||||
"important": "Important",
|
||||
"personal": "Personal",
|
||||
"updates": "Updates",
|
||||
@@ -219,7 +238,8 @@
|
||||
"moveToTrash": "Move to Trash",
|
||||
"markAsUnread": "Mark as Unread",
|
||||
"markAsRead": "Mark as Read",
|
||||
"addStar": "Add Star",
|
||||
"addFavorite": "Favorite",
|
||||
"removeFavorite": "Unfavorite",
|
||||
"muteThread": "Mute Thread",
|
||||
"moving": "Moving...",
|
||||
"moved": "Moved",
|
||||
@@ -242,7 +262,6 @@
|
||||
"archive": "Archive",
|
||||
"bin": "Bin",
|
||||
"feedback": "Feedback",
|
||||
"contact": "Contact",
|
||||
"settings": "Settings"
|
||||
},
|
||||
"settings": {
|
||||
|
||||
@@ -12,7 +12,25 @@
|
||||
"signedOutSuccess": "S'ha tancat la sessió amb èxit!",
|
||||
"signOutError": "Error tancant la sessió",
|
||||
"refresh": "Refresh",
|
||||
"loading": "Loading..."
|
||||
"loading": "Loading...",
|
||||
"featureNotImplemented": "This feature is not implemented yet",
|
||||
"moving": "Moving...",
|
||||
"movedToInbox": "Moved to inbox",
|
||||
"movingToInbox": "Moving to inbox...",
|
||||
"movedToSpam": "Moved to spam",
|
||||
"movingToSpam": "Moving to spam...",
|
||||
"archiving": "Archiving...",
|
||||
"archived": "Archived",
|
||||
"failedToMove": "Failed to move message",
|
||||
"addingToFavorites": "Adding to favorites...",
|
||||
"removingFromFavorites": "Removing from favorites...",
|
||||
"addedToFavorites": "Added to favorites",
|
||||
"removedFromFavorites": "Removed from favorites",
|
||||
"failedToAddToFavorites": "Failed to add to favorites",
|
||||
"failedToRemoveFromFavorites": "Failed to remove from favorites",
|
||||
"failedToModifyFavorites": "Failed to modify favorites",
|
||||
"markingAsRead": "Marking as read...",
|
||||
"markingAsUnread": "Marking as unread..."
|
||||
},
|
||||
"themes": {
|
||||
"dark": "Fosc",
|
||||
@@ -83,6 +101,7 @@
|
||||
},
|
||||
"mailCategories": {
|
||||
"primary": "Primari",
|
||||
"allMail": "All Mail",
|
||||
"important": "Important",
|
||||
"personal": "Personal",
|
||||
"updates": "Actualitzacions",
|
||||
@@ -219,7 +238,8 @@
|
||||
"moveToTrash": "Move to Trash",
|
||||
"markAsUnread": "Mark as Unread",
|
||||
"markAsRead": "Mark as Read",
|
||||
"addStar": "Add Star",
|
||||
"addFavorite": "Favorite",
|
||||
"removeFavorite": "Unfavorite",
|
||||
"muteThread": "Mute Thread",
|
||||
"moving": "Moving...",
|
||||
"moved": "Moved",
|
||||
@@ -242,7 +262,6 @@
|
||||
"archive": "Arxivats",
|
||||
"bin": "Paperera",
|
||||
"feedback": "Feedback",
|
||||
"contact": "Contact",
|
||||
"settings": "Configuració"
|
||||
},
|
||||
"settings": {
|
||||
|
||||
@@ -12,7 +12,25 @@
|
||||
"signedOutSuccess": "Odhlášení proběhlo úspěšně!",
|
||||
"signOutError": "Chyba při odhlášení",
|
||||
"refresh": "Refresh",
|
||||
"loading": "Loading..."
|
||||
"loading": "Loading...",
|
||||
"featureNotImplemented": "This feature is not implemented yet",
|
||||
"moving": "Moving...",
|
||||
"movedToInbox": "Moved to inbox",
|
||||
"movingToInbox": "Moving to inbox...",
|
||||
"movedToSpam": "Moved to spam",
|
||||
"movingToSpam": "Moving to spam...",
|
||||
"archiving": "Archiving...",
|
||||
"archived": "Archived",
|
||||
"failedToMove": "Failed to move message",
|
||||
"addingToFavorites": "Adding to favorites...",
|
||||
"removingFromFavorites": "Removing from favorites...",
|
||||
"addedToFavorites": "Added to favorites",
|
||||
"removedFromFavorites": "Removed from favorites",
|
||||
"failedToAddToFavorites": "Failed to add to favorites",
|
||||
"failedToRemoveFromFavorites": "Failed to remove from favorites",
|
||||
"failedToModifyFavorites": "Failed to modify favorites",
|
||||
"markingAsRead": "Marking as read...",
|
||||
"markingAsUnread": "Marking as unread..."
|
||||
},
|
||||
"themes": {
|
||||
"dark": "Tmavé",
|
||||
@@ -83,6 +101,7 @@
|
||||
},
|
||||
"mailCategories": {
|
||||
"primary": "Hlavní",
|
||||
"allMail": "All Mail",
|
||||
"important": "Důležité",
|
||||
"personal": "Osobní",
|
||||
"updates": "Aktualizace",
|
||||
@@ -219,7 +238,8 @@
|
||||
"moveToTrash": "Přesunout do Odstraněné pošty",
|
||||
"markAsUnread": "Označit jako nepřečtené",
|
||||
"markAsRead": "Označit jako přečtené",
|
||||
"addStar": "Přidat hvězdu",
|
||||
"addFavorite": "Favorite",
|
||||
"removeFavorite": "Unfavorite",
|
||||
"muteThread": "Ztlumit vlákno",
|
||||
"moving": "Přesouvání...",
|
||||
"moved": "Přesunuto",
|
||||
@@ -242,7 +262,6 @@
|
||||
"archive": "Archiv",
|
||||
"bin": "Odstraněná pošta",
|
||||
"feedback": "Zpětná vazba",
|
||||
"contact": "Kontakt",
|
||||
"settings": "Nastavení"
|
||||
},
|
||||
"settings": {
|
||||
|
||||
@@ -12,7 +12,25 @@
|
||||
"signedOutSuccess": "Erfolgreich abgemeldet!",
|
||||
"signOutError": "Fehler beim Abmelden",
|
||||
"refresh": "Aktualisieren",
|
||||
"loading": "Wird geladen..."
|
||||
"loading": "Wird geladen...",
|
||||
"featureNotImplemented": "This feature is not implemented yet",
|
||||
"moving": "Moving...",
|
||||
"movedToInbox": "Moved to inbox",
|
||||
"movingToInbox": "Moving to inbox...",
|
||||
"movedToSpam": "Moved to spam",
|
||||
"movingToSpam": "Moving to spam...",
|
||||
"archiving": "Archiving...",
|
||||
"archived": "Archived",
|
||||
"failedToMove": "Failed to move message",
|
||||
"addingToFavorites": "Adding to favorites...",
|
||||
"removingFromFavorites": "Removing from favorites...",
|
||||
"addedToFavorites": "Added to favorites",
|
||||
"removedFromFavorites": "Removed from favorites",
|
||||
"failedToAddToFavorites": "Failed to add to favorites",
|
||||
"failedToRemoveFromFavorites": "Failed to remove from favorites",
|
||||
"failedToModifyFavorites": "Failed to modify favorites",
|
||||
"markingAsRead": "Marking as read...",
|
||||
"markingAsUnread": "Marking as unread..."
|
||||
},
|
||||
"themes": {
|
||||
"dark": "Dunkel",
|
||||
@@ -83,6 +101,7 @@
|
||||
},
|
||||
"mailCategories": {
|
||||
"primary": "Primär",
|
||||
"allMail": "All Mail",
|
||||
"important": "Wichtig",
|
||||
"personal": "Persönlich",
|
||||
"updates": "Updates",
|
||||
@@ -219,7 +238,8 @@
|
||||
"moveToTrash": "In den Papierkorb verschieben",
|
||||
"markAsUnread": "Als ungelesen markieren",
|
||||
"markAsRead": "Als gelesen markieren",
|
||||
"addStar": "Stern hinzufügen",
|
||||
"addFavorite": "Favorite",
|
||||
"removeFavorite": "Unfavorite",
|
||||
"muteThread": "Thread stummschalten",
|
||||
"moving": "Wird verschoben...",
|
||||
"moved": "Verschoben",
|
||||
@@ -242,7 +262,6 @@
|
||||
"archive": "Archiv",
|
||||
"bin": "Papierkorb",
|
||||
"feedback": "Rückmeldung",
|
||||
"contact": "Kontakt",
|
||||
"settings": "Einstellungen"
|
||||
},
|
||||
"settings": {
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
"removingFromFavorites": "Removing from favorites...",
|
||||
"addedToFavorites": "Added to favorites",
|
||||
"removedFromFavorites": "Removed from favorites",
|
||||
"failedToAddToFavorites": "Failed to add to favorites",
|
||||
"failedToRemoveFromFavorites": "Failed to remove from favorites",
|
||||
"failedToModifyFavorites": "Failed to modify favorites",
|
||||
"markingAsRead": "Marking as read...",
|
||||
"markingAsUnread": "Marking as unread..."
|
||||
|
||||
@@ -12,7 +12,25 @@
|
||||
"signedOutSuccess": "Sesión cerrada correctamente",
|
||||
"signOutError": "Error al cerrar sesión",
|
||||
"refresh": "Actualizar",
|
||||
"loading": "Loading..."
|
||||
"loading": "Loading...",
|
||||
"featureNotImplemented": "This feature is not implemented yet",
|
||||
"moving": "Moving...",
|
||||
"movedToInbox": "Moved to inbox",
|
||||
"movingToInbox": "Moving to inbox...",
|
||||
"movedToSpam": "Moved to spam",
|
||||
"movingToSpam": "Moving to spam...",
|
||||
"archiving": "Archiving...",
|
||||
"archived": "Archived",
|
||||
"failedToMove": "Failed to move message",
|
||||
"addingToFavorites": "Adding to favorites...",
|
||||
"removingFromFavorites": "Removing from favorites...",
|
||||
"addedToFavorites": "Added to favorites",
|
||||
"removedFromFavorites": "Removed from favorites",
|
||||
"failedToAddToFavorites": "Failed to add to favorites",
|
||||
"failedToRemoveFromFavorites": "Failed to remove from favorites",
|
||||
"failedToModifyFavorites": "Failed to modify favorites",
|
||||
"markingAsRead": "Marking as read...",
|
||||
"markingAsUnread": "Marking as unread..."
|
||||
},
|
||||
"themes": {
|
||||
"dark": "Oscuro",
|
||||
@@ -83,6 +101,7 @@
|
||||
},
|
||||
"mailCategories": {
|
||||
"primary": "Principal",
|
||||
"allMail": "All Mail",
|
||||
"important": "Importante",
|
||||
"personal": "Personal",
|
||||
"updates": "Actualizaciones",
|
||||
@@ -219,7 +238,8 @@
|
||||
"moveToTrash": "Mover a la papelera",
|
||||
"markAsUnread": "Marcar como no leído",
|
||||
"markAsRead": "Marcar como leído",
|
||||
"addStar": "Destacar",
|
||||
"addFavorite": "Favorite",
|
||||
"removeFavorite": "Unfavorite",
|
||||
"muteThread": "Silencia hilo",
|
||||
"moving": "Moviendo...",
|
||||
"moved": "Movido",
|
||||
@@ -242,7 +262,6 @@
|
||||
"archive": "Archivar",
|
||||
"bin": "Papelera de reciclaje",
|
||||
"feedback": "Sugerencias",
|
||||
"contact": "Contacto",
|
||||
"settings": "Configuración"
|
||||
},
|
||||
"settings": {
|
||||
|
||||
@@ -12,7 +12,25 @@
|
||||
"signedOutSuccess": "Déconnecté avec succès !",
|
||||
"signOutError": "Erreur de déconnexion",
|
||||
"refresh": "Actualiser",
|
||||
"loading": "Loading..."
|
||||
"loading": "Chargement...",
|
||||
"featureNotImplemented": "This feature is not implemented yet",
|
||||
"moving": "Moving...",
|
||||
"movedToInbox": "Moved to inbox",
|
||||
"movingToInbox": "Moving to inbox...",
|
||||
"movedToSpam": "Moved to spam",
|
||||
"movingToSpam": "Moving to spam...",
|
||||
"archiving": "Archiving...",
|
||||
"archived": "Archived",
|
||||
"failedToMove": "Failed to move message",
|
||||
"addingToFavorites": "Adding to favorites...",
|
||||
"removingFromFavorites": "Removing from favorites...",
|
||||
"addedToFavorites": "Added to favorites",
|
||||
"removedFromFavorites": "Removed from favorites",
|
||||
"failedToAddToFavorites": "Failed to add to favorites",
|
||||
"failedToRemoveFromFavorites": "Failed to remove from favorites",
|
||||
"failedToModifyFavorites": "Failed to modify favorites",
|
||||
"markingAsRead": "Marking as read...",
|
||||
"markingAsUnread": "Marking as unread..."
|
||||
},
|
||||
"themes": {
|
||||
"dark": "Sombre",
|
||||
@@ -67,12 +85,12 @@
|
||||
"starred": "Étoilé",
|
||||
"applyFilters": "Appliquer les filtres",
|
||||
"reset": "Réinitialiser",
|
||||
"searching": "Searching...",
|
||||
"aiSuggestions": "AI Suggestions",
|
||||
"aiSearching": "AI is searching...",
|
||||
"aiSearchError": "AI search failed. Please try again.",
|
||||
"aiNoResults": "No AI suggestions found",
|
||||
"aiEnhancedQuery": "Enhanced search query"
|
||||
"searching": "Recherche en cours...",
|
||||
"aiSuggestions": "Suggestions de l'IA",
|
||||
"aiSearching": "L'IA est en cours de recherche...",
|
||||
"aiSearchError": "La recherche de l'IA a échouée. Veuillez réessayer.",
|
||||
"aiNoResults": "Aucune suggestion de l'IA trouvée",
|
||||
"aiEnhancedQuery": "Requête de recherche avancée"
|
||||
},
|
||||
"navUser": {
|
||||
"customerSupport": "Support aux clients",
|
||||
@@ -83,6 +101,7 @@
|
||||
},
|
||||
"mailCategories": {
|
||||
"primary": "Principale",
|
||||
"allMail": "All Mail",
|
||||
"important": "Important",
|
||||
"personal": "Personnel",
|
||||
"updates": "Mises à jour",
|
||||
@@ -91,14 +110,14 @@
|
||||
},
|
||||
"replyCompose": {
|
||||
"replyTo": "Répondre à",
|
||||
"thisEmail": "cet email",
|
||||
"thisEmail": "ce courriel",
|
||||
"dropFiles": "Déposer les fichiers à joindre",
|
||||
"attachments": "Pièces jointes",
|
||||
"attachmentCount": "{count, plural, =0 {pièces jointes} one {pièce jointe} other {pièces jointes}}",
|
||||
"fileCount": "{count, plural, =0 {fichiers} one {fichier} other {fichiers}}",
|
||||
"saveDraft": "Enregistrer le brouillon",
|
||||
"send": "Envoyer",
|
||||
"forward": "Forward"
|
||||
"forward": "Transférer"
|
||||
},
|
||||
"mailDisplay": {
|
||||
"details": "Détails",
|
||||
@@ -106,11 +125,11 @@
|
||||
"to": "À",
|
||||
"cc": "Cc",
|
||||
"date": "Date",
|
||||
"mailedBy": "Expédié par",
|
||||
"mailedBy": "Envoyé par",
|
||||
"signedBy": "Signé par",
|
||||
"security": "Sécurité",
|
||||
"standardEncryption": "Chiffrement standard (TLS)",
|
||||
"loadingMailContent": "Chargement du contenu de l'email...",
|
||||
"standardEncryption": "Encryptage standard (TLS)",
|
||||
"loadingMailContent": "Chargement du contenu du courriel...",
|
||||
"unsubscribe": "Se désabonner",
|
||||
"unsubscribed": "Désabonné",
|
||||
"unsubscribeDescription": "Êtes-vous sûr de vouloir vous désabonner de cette liste de diffusion ?",
|
||||
@@ -125,14 +144,14 @@
|
||||
"archive": "Archiver",
|
||||
"reply": "Répondre",
|
||||
"moreOptions": "Plus d'options",
|
||||
"moveToSpam": "Move to Spam",
|
||||
"moveToSpam": "Déplacer dans les indésirables",
|
||||
"replyAll": "Répondre à tous",
|
||||
"forward": "Transférer",
|
||||
"markAsUnread": "Marquer comme non lu",
|
||||
"markAsRead": "Mark as Read",
|
||||
"addLabel": "Ajouter un label",
|
||||
"markAsRead": "Marquer comme lu",
|
||||
"addLabel": "Ajouter une étiquette",
|
||||
"muteThread": "Mettre en sourdine",
|
||||
"favourites": "Favourites"
|
||||
"favourites": "Favoris"
|
||||
},
|
||||
"notes": {
|
||||
"title": "Notes",
|
||||
@@ -195,10 +214,10 @@
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"notFound": "Settings not found",
|
||||
"notFound": "Paramètres introuvables",
|
||||
"saved": "Paramètres enregistrés",
|
||||
"failedToSave": "Failed to save settings",
|
||||
"languageChanged": "Language changed to {locale}"
|
||||
"failedToSave": "Échec de la sauvegarde des paramètres",
|
||||
"languageChanged": "Langue changée en {locale}"
|
||||
},
|
||||
"mail": {
|
||||
"replies": "{count, plural, =0 {réponses} one {# réponse} other {# réponses}}",
|
||||
@@ -218,17 +237,18 @@
|
||||
"archive": "Archiver",
|
||||
"moveToTrash": "Déplacer dans la Corbeille",
|
||||
"markAsUnread": "Marquer comme non lu",
|
||||
"markAsRead": "Mark as Read",
|
||||
"addStar": "Add Star",
|
||||
"markAsRead": "Marquer comme lu",
|
||||
"addFavorite": "Favorite",
|
||||
"removeFavorite": "Unfavorite",
|
||||
"muteThread": "Mettre en sourdine",
|
||||
"moving": "Déplacement...",
|
||||
"moved": "Déplacé",
|
||||
"errorMoving": "Erreur lors du déplacement",
|
||||
"reply": "Reply",
|
||||
"reply": "Répondre",
|
||||
"replyAll": "Répondre à tous",
|
||||
"forward": "Transférer",
|
||||
"labels": "Labels",
|
||||
"createNewLabel": "Create New Label",
|
||||
"labels": "Étiquettes",
|
||||
"createNewLabel": "Créer une nouvelle étiquette",
|
||||
"noLabelsAvailable": "Aucune étiquette disponible",
|
||||
"loadMore": "Charger plus d'éléments"
|
||||
}
|
||||
@@ -242,7 +262,6 @@
|
||||
"archive": "Archives",
|
||||
"bin": "Corbeille",
|
||||
"feedback": "Vos commentaires",
|
||||
"contact": "Contact",
|
||||
"settings": "Paramètres"
|
||||
},
|
||||
"settings": {
|
||||
@@ -265,18 +284,18 @@
|
||||
"settings": {
|
||||
"general": {
|
||||
"title": "Paramètres généraux",
|
||||
"description": "Gérez les paramètres de votre langue et vos préférences d'affichage des courriels.",
|
||||
"description": "Gérez les paramètres de langue et vos préférences d'affichage des courriels.",
|
||||
"language": "Langue",
|
||||
"selectLanguage": "Select a language",
|
||||
"selectLanguage": "Sélectionnez la langue",
|
||||
"timezone": "Fuseau horaire",
|
||||
"selectTimezone": "Select a timezone",
|
||||
"selectTimezone": "Sélectionner un fuseau horaire",
|
||||
"dynamicContent": "Contenu dynamique",
|
||||
"dynamicContentDescription": "Autoriser les courriels à afficher du contenu dynamique.",
|
||||
"externalImages": "Afficher les images externes",
|
||||
"externalImagesDescription": "Autoriser les courriels à afficher des images provenant de sources externes.",
|
||||
"languageChangedTo": "Language changed to {locale}",
|
||||
"customPrompt": "Custom AI Prompt",
|
||||
"customPromptPlaceholder": "",
|
||||
"languageChangedTo": "Langue changée en {locale}",
|
||||
"customPrompt": "Requêtes IA personnalisées",
|
||||
"customPromptPlaceholder": "Entrez votre requête personnalisée pour l'IA...",
|
||||
"customPromptDescription": "Personnalisez la façon dont l'IA écrit vos réponses. Cela sera ajouté à la requête par défaut."
|
||||
},
|
||||
"connections": {
|
||||
@@ -312,8 +331,8 @@
|
||||
"title": "Raccourcis clavier",
|
||||
"description": "Consultez et personnalisez les raccourcis clavier pour des actions rapides.",
|
||||
"actions": {
|
||||
"newEmail": "Nouvel email",
|
||||
"sendEmail": "Envoyer l'email",
|
||||
"newEmail": "Nouveau courriel",
|
||||
"sendEmail": "Envoyer le courriel",
|
||||
"reply": "Répondre",
|
||||
"replyAll": "Répondre à tous",
|
||||
"forward": "Transférer",
|
||||
|
||||
@@ -12,7 +12,25 @@
|
||||
"signedOutSuccess": "सफलतापूर्वक साइन आउट हो गया!",
|
||||
"signOutError": "साइन आउट करने में एरर",
|
||||
"refresh": "Refresh",
|
||||
"loading": "Loading..."
|
||||
"loading": "Loading...",
|
||||
"featureNotImplemented": "This feature is not implemented yet",
|
||||
"moving": "Moving...",
|
||||
"movedToInbox": "Moved to inbox",
|
||||
"movingToInbox": "Moving to inbox...",
|
||||
"movedToSpam": "Moved to spam",
|
||||
"movingToSpam": "Moving to spam...",
|
||||
"archiving": "Archiving...",
|
||||
"archived": "Archived",
|
||||
"failedToMove": "Failed to move message",
|
||||
"addingToFavorites": "Adding to favorites...",
|
||||
"removingFromFavorites": "Removing from favorites...",
|
||||
"addedToFavorites": "Added to favorites",
|
||||
"removedFromFavorites": "Removed from favorites",
|
||||
"failedToAddToFavorites": "Failed to add to favorites",
|
||||
"failedToRemoveFromFavorites": "Failed to remove from favorites",
|
||||
"failedToModifyFavorites": "Failed to modify favorites",
|
||||
"markingAsRead": "Marking as read...",
|
||||
"markingAsUnread": "Marking as unread..."
|
||||
},
|
||||
"themes": {
|
||||
"dark": "काला",
|
||||
@@ -83,6 +101,7 @@
|
||||
},
|
||||
"mailCategories": {
|
||||
"primary": "प्राइमरी",
|
||||
"allMail": "All Mail",
|
||||
"important": "महत्वपूर्ण",
|
||||
"personal": "पर्सनल",
|
||||
"updates": "अपडेट",
|
||||
@@ -219,7 +238,8 @@
|
||||
"moveToTrash": "Move to Trash",
|
||||
"markAsUnread": "Mark as Unread",
|
||||
"markAsRead": "Mark as Read",
|
||||
"addStar": "Add Star",
|
||||
"addFavorite": "Favorite",
|
||||
"removeFavorite": "Unfavorite",
|
||||
"muteThread": "Mute Thread",
|
||||
"moving": "Moving...",
|
||||
"moved": "Moved",
|
||||
@@ -242,7 +262,6 @@
|
||||
"archive": "आर्काइव",
|
||||
"bin": "बिन",
|
||||
"feedback": "Feedback",
|
||||
"contact": "Contact",
|
||||
"settings": "सेटिंग्स"
|
||||
},
|
||||
"settings": {
|
||||
|
||||
@@ -11,8 +11,26 @@
|
||||
"signingOut": "ログアウトしています...",
|
||||
"signedOutSuccess": "正常にログアウトしました!",
|
||||
"signOutError": "サインアウトエラー",
|
||||
"refresh": "Refresh",
|
||||
"loading": "Loading..."
|
||||
"refresh": "更新",
|
||||
"loading": "読み込み中...",
|
||||
"featureNotImplemented": "This feature is not implemented yet",
|
||||
"moving": "Moving...",
|
||||
"movedToInbox": "Moved to inbox",
|
||||
"movingToInbox": "Moving to inbox...",
|
||||
"movedToSpam": "Moved to spam",
|
||||
"movingToSpam": "Moving to spam...",
|
||||
"archiving": "Archiving...",
|
||||
"archived": "Archived",
|
||||
"failedToMove": "Failed to move message",
|
||||
"addingToFavorites": "Adding to favorites...",
|
||||
"removingFromFavorites": "Removing from favorites...",
|
||||
"addedToFavorites": "Added to favorites",
|
||||
"removedFromFavorites": "Removed from favorites",
|
||||
"failedToAddToFavorites": "Failed to add to favorites",
|
||||
"failedToRemoveFromFavorites": "Failed to remove from favorites",
|
||||
"failedToModifyFavorites": "Failed to modify favorites",
|
||||
"markingAsRead": "Marking as read...",
|
||||
"markingAsUnread": "Marking as unread..."
|
||||
},
|
||||
"themes": {
|
||||
"dark": "ダーク",
|
||||
@@ -67,12 +85,12 @@
|
||||
"starred": "スター付き",
|
||||
"applyFilters": "フィルタを適用",
|
||||
"reset": "リセット",
|
||||
"searching": "Searching...",
|
||||
"aiSuggestions": "AI Suggestions",
|
||||
"aiSearching": "AI is searching...",
|
||||
"aiSearchError": "AI search failed. Please try again.",
|
||||
"aiNoResults": "No AI suggestions found",
|
||||
"aiEnhancedQuery": "Enhanced search query"
|
||||
"searching": "検索中...",
|
||||
"aiSuggestions": "AIの提案",
|
||||
"aiSearching": "AIが検索中...",
|
||||
"aiSearchError": "AI検索に失敗しました。もう一度お試しください。",
|
||||
"aiNoResults": "AIの提案が見つかりませんでした",
|
||||
"aiEnhancedQuery": "拡張検索クエリ"
|
||||
},
|
||||
"navUser": {
|
||||
"customerSupport": "カスタマーサポート",
|
||||
@@ -83,6 +101,7 @@
|
||||
},
|
||||
"mailCategories": {
|
||||
"primary": "プライマリ",
|
||||
"allMail": "All Mail",
|
||||
"important": "重要",
|
||||
"personal": "個人用",
|
||||
"updates": "更新",
|
||||
@@ -98,7 +117,7 @@
|
||||
"fileCount": "{count, plural, =0 {files} other {files}}",
|
||||
"saveDraft": "下書きを保存",
|
||||
"send": "送信",
|
||||
"forward": "Forward"
|
||||
"forward": "転送"
|
||||
},
|
||||
"mailDisplay": {
|
||||
"details": "詳細",
|
||||
@@ -219,7 +238,8 @@
|
||||
"moveToTrash": "ゴミ箱へ移動",
|
||||
"markAsUnread": "未読にする",
|
||||
"markAsRead": "既読にする",
|
||||
"addStar": "スターを追加",
|
||||
"addFavorite": "Favorite",
|
||||
"removeFavorite": "Unfavorite",
|
||||
"muteThread": "スレッドをミュート",
|
||||
"moving": "移動中...",
|
||||
"moved": "移動しました",
|
||||
@@ -230,7 +250,7 @@
|
||||
"labels": "ラベル",
|
||||
"createNewLabel": "新しいラベルを作成",
|
||||
"noLabelsAvailable": "利用可能なラベルがありません",
|
||||
"loadMore": "Load more"
|
||||
"loadMore": "さらに読み込む"
|
||||
}
|
||||
},
|
||||
"navigation": {
|
||||
@@ -242,7 +262,6 @@
|
||||
"archive": "アーカイブ",
|
||||
"bin": "ごみ箱",
|
||||
"feedback": "フィードバック",
|
||||
"contact": "コンタクト",
|
||||
"settings": "設定"
|
||||
},
|
||||
"settings": {
|
||||
@@ -274,10 +293,10 @@
|
||||
"dynamicContentDescription": "電子メールの動的なコンテンツの表示を許可します。",
|
||||
"externalImages": "外部画像を表示する",
|
||||
"externalImagesDescription": "電子メールによる外部ソースからの画像の表示を許可します。",
|
||||
"languageChangedTo": "Language changed to {locale}",
|
||||
"customPrompt": "Custom AI Prompt",
|
||||
"customPromptPlaceholder": "Enter your custom prompt for the AI...",
|
||||
"customPromptDescription": "Customize how the AI writes your email replies. This will be added to the base prompt."
|
||||
"languageChangedTo": "言語を {locale}に変更しました",
|
||||
"customPrompt": "カスタムAIプロンプト",
|
||||
"customPromptPlaceholder": "AIのカスタムプロンプトを入力...",
|
||||
"customPromptDescription": "AIがメールの返信を書く方法をカスタマイズします。これはベースプロンプトに追加されます。"
|
||||
},
|
||||
"connections": {
|
||||
"title": "メールの接続",
|
||||
|
||||
@@ -12,7 +12,25 @@
|
||||
"signedOutSuccess": "로그아웃에 성공했습니다!",
|
||||
"signOutError": "로그아웃 오류 발생",
|
||||
"refresh": "Refresh",
|
||||
"loading": "Loading..."
|
||||
"loading": "Loading...",
|
||||
"featureNotImplemented": "This feature is not implemented yet",
|
||||
"moving": "Moving...",
|
||||
"movedToInbox": "Moved to inbox",
|
||||
"movingToInbox": "Moving to inbox...",
|
||||
"movedToSpam": "Moved to spam",
|
||||
"movingToSpam": "Moving to spam...",
|
||||
"archiving": "Archiving...",
|
||||
"archived": "Archived",
|
||||
"failedToMove": "Failed to move message",
|
||||
"addingToFavorites": "Adding to favorites...",
|
||||
"removingFromFavorites": "Removing from favorites...",
|
||||
"addedToFavorites": "Added to favorites",
|
||||
"removedFromFavorites": "Removed from favorites",
|
||||
"failedToAddToFavorites": "Failed to add to favorites",
|
||||
"failedToRemoveFromFavorites": "Failed to remove from favorites",
|
||||
"failedToModifyFavorites": "Failed to modify favorites",
|
||||
"markingAsRead": "Marking as read...",
|
||||
"markingAsUnread": "Marking as unread..."
|
||||
},
|
||||
"themes": {
|
||||
"dark": "다크 모드",
|
||||
@@ -83,6 +101,7 @@
|
||||
},
|
||||
"mailCategories": {
|
||||
"primary": "기본",
|
||||
"allMail": "All Mail",
|
||||
"important": "중요",
|
||||
"personal": "개인",
|
||||
"updates": "업데이트",
|
||||
@@ -219,7 +238,8 @@
|
||||
"moveToTrash": "휴지통으로 이동",
|
||||
"markAsUnread": "읽지 않음으로 표시",
|
||||
"markAsRead": "읽음으로 표시",
|
||||
"addStar": "별표 표시",
|
||||
"addFavorite": "Favorite",
|
||||
"removeFavorite": "Unfavorite",
|
||||
"muteThread": "스레드 음소거",
|
||||
"moving": "이동 중...",
|
||||
"moved": "이동됨",
|
||||
@@ -242,7 +262,6 @@
|
||||
"archive": "보관함",
|
||||
"bin": "휴지통",
|
||||
"feedback": "피드백",
|
||||
"contact": "Contact",
|
||||
"settings": "설정"
|
||||
},
|
||||
"settings": {
|
||||
|
||||
@@ -12,7 +12,25 @@
|
||||
"signedOutSuccess": "Veiksmīgi izrakstījies!",
|
||||
"signOutError": "Kļūda, izrakstoties no sistēmas",
|
||||
"refresh": "Atjaunot",
|
||||
"loading": "Ielādē..."
|
||||
"loading": "Ielādē...",
|
||||
"featureNotImplemented": "This feature is not implemented yet",
|
||||
"moving": "Moving...",
|
||||
"movedToInbox": "Moved to inbox",
|
||||
"movingToInbox": "Moving to inbox...",
|
||||
"movedToSpam": "Moved to spam",
|
||||
"movingToSpam": "Moving to spam...",
|
||||
"archiving": "Archiving...",
|
||||
"archived": "Archived",
|
||||
"failedToMove": "Failed to move message",
|
||||
"addingToFavorites": "Adding to favorites...",
|
||||
"removingFromFavorites": "Removing from favorites...",
|
||||
"addedToFavorites": "Added to favorites",
|
||||
"removedFromFavorites": "Removed from favorites",
|
||||
"failedToAddToFavorites": "Failed to add to favorites",
|
||||
"failedToRemoveFromFavorites": "Failed to remove from favorites",
|
||||
"failedToModifyFavorites": "Failed to modify favorites",
|
||||
"markingAsRead": "Marking as read...",
|
||||
"markingAsUnread": "Marking as unread..."
|
||||
},
|
||||
"themes": {
|
||||
"dark": "Tumšs",
|
||||
@@ -83,6 +101,7 @@
|
||||
},
|
||||
"mailCategories": {
|
||||
"primary": "Galvenā",
|
||||
"allMail": "All Mail",
|
||||
"important": "Svarīgi",
|
||||
"personal": "Personīgi",
|
||||
"updates": "Atjauninājumi",
|
||||
@@ -219,7 +238,8 @@
|
||||
"moveToTrash": "Pārvietot uz Miskasti",
|
||||
"markAsUnread": "Atzīmēt kā nelasītu",
|
||||
"markAsRead": "Atzīmēt kā lasītu",
|
||||
"addStar": "Pievienot Zvaigzni",
|
||||
"addFavorite": "Favorite",
|
||||
"removeFavorite": "Unfavorite",
|
||||
"muteThread": "Apklusināt pavedienu",
|
||||
"moving": "Pārvieto...",
|
||||
"moved": "Pārvietots",
|
||||
@@ -242,7 +262,6 @@
|
||||
"archive": "Arhīvs",
|
||||
"bin": "Miskaste",
|
||||
"feedback": "Atsauksmes",
|
||||
"contact": "Kontakti",
|
||||
"settings": "Iestatījumi"
|
||||
},
|
||||
"settings": {
|
||||
|
||||
@@ -12,7 +12,25 @@
|
||||
"signedOutSuccess": "Signed out successfully!",
|
||||
"signOutError": "Error signing out",
|
||||
"refresh": "Refresh",
|
||||
"loading": "Loading..."
|
||||
"loading": "Loading...",
|
||||
"featureNotImplemented": "This feature is not implemented yet",
|
||||
"moving": "Moving...",
|
||||
"movedToInbox": "Moved to inbox",
|
||||
"movingToInbox": "Moving to inbox...",
|
||||
"movedToSpam": "Moved to spam",
|
||||
"movingToSpam": "Moving to spam...",
|
||||
"archiving": "Archiving...",
|
||||
"archived": "Archived",
|
||||
"failedToMove": "Failed to move message",
|
||||
"addingToFavorites": "Adding to favorites...",
|
||||
"removingFromFavorites": "Removing from favorites...",
|
||||
"addedToFavorites": "Added to favorites",
|
||||
"removedFromFavorites": "Removed from favorites",
|
||||
"failedToAddToFavorites": "Failed to add to favorites",
|
||||
"failedToRemoveFromFavorites": "Failed to remove from favorites",
|
||||
"failedToModifyFavorites": "Failed to modify favorites",
|
||||
"markingAsRead": "Marking as read...",
|
||||
"markingAsUnread": "Marking as unread..."
|
||||
},
|
||||
"themes": {
|
||||
"dark": "Dark",
|
||||
@@ -83,6 +101,7 @@
|
||||
},
|
||||
"mailCategories": {
|
||||
"primary": "Primary",
|
||||
"allMail": "All Mail",
|
||||
"important": "Important",
|
||||
"personal": "Personal",
|
||||
"updates": "Updates",
|
||||
@@ -219,7 +238,8 @@
|
||||
"moveToTrash": "Move to Trash",
|
||||
"markAsUnread": "Mark as Unread",
|
||||
"markAsRead": "Mark as Read",
|
||||
"addStar": "Add Star",
|
||||
"addFavorite": "Favorite",
|
||||
"removeFavorite": "Unfavorite",
|
||||
"muteThread": "Mute Thread",
|
||||
"moving": "Moving...",
|
||||
"moved": "Moved",
|
||||
@@ -242,7 +262,6 @@
|
||||
"archive": "Archive",
|
||||
"bin": "Bin",
|
||||
"feedback": "Feedback",
|
||||
"contact": "Contact",
|
||||
"settings": "Settings"
|
||||
},
|
||||
"settings": {
|
||||
|
||||
@@ -12,7 +12,25 @@
|
||||
"signedOutSuccess": "Sessão terminada com sucesso!",
|
||||
"signOutError": "Erro ao sair",
|
||||
"refresh": "Refresh",
|
||||
"loading": "Loading..."
|
||||
"loading": "Loading...",
|
||||
"featureNotImplemented": "This feature is not implemented yet",
|
||||
"moving": "Moving...",
|
||||
"movedToInbox": "Moved to inbox",
|
||||
"movingToInbox": "Moving to inbox...",
|
||||
"movedToSpam": "Moved to spam",
|
||||
"movingToSpam": "Moving to spam...",
|
||||
"archiving": "Archiving...",
|
||||
"archived": "Archived",
|
||||
"failedToMove": "Failed to move message",
|
||||
"addingToFavorites": "Adding to favorites...",
|
||||
"removingFromFavorites": "Removing from favorites...",
|
||||
"addedToFavorites": "Added to favorites",
|
||||
"removedFromFavorites": "Removed from favorites",
|
||||
"failedToAddToFavorites": "Failed to add to favorites",
|
||||
"failedToRemoveFromFavorites": "Failed to remove from favorites",
|
||||
"failedToModifyFavorites": "Failed to modify favorites",
|
||||
"markingAsRead": "Marking as read...",
|
||||
"markingAsUnread": "Marking as unread..."
|
||||
},
|
||||
"themes": {
|
||||
"dark": "Escuro",
|
||||
@@ -83,6 +101,7 @@
|
||||
},
|
||||
"mailCategories": {
|
||||
"primary": "Principal",
|
||||
"allMail": "All Mail",
|
||||
"important": "Importante",
|
||||
"personal": "Pessoal",
|
||||
"updates": "Atualizações",
|
||||
@@ -219,7 +238,8 @@
|
||||
"moveToTrash": "Mover para o Lixo",
|
||||
"markAsUnread": "Marcar como não lido",
|
||||
"markAsRead": "Mark as Read",
|
||||
"addStar": "Adicionar Estrela",
|
||||
"addFavorite": "Favorite",
|
||||
"removeFavorite": "Unfavorite",
|
||||
"muteThread": "Silenciar esta conversa",
|
||||
"moving": "Movendo...",
|
||||
"moved": "Movido",
|
||||
@@ -242,7 +262,6 @@
|
||||
"archive": "Arquivados",
|
||||
"bin": "Lixeira",
|
||||
"feedback": "Feedback",
|
||||
"contact": "Contact",
|
||||
"settings": "Configurações"
|
||||
},
|
||||
"settings": {
|
||||
|
||||
@@ -11,8 +11,26 @@
|
||||
"signingOut": "Выход...",
|
||||
"signedOutSuccess": "Выход успешен",
|
||||
"signOutError": "Ошибка при выходе",
|
||||
"refresh": "Refresh",
|
||||
"loading": "Loading..."
|
||||
"refresh": "Обновить",
|
||||
"loading": "Загрузка...",
|
||||
"featureNotImplemented": "This feature is not implemented yet",
|
||||
"moving": "Moving...",
|
||||
"movedToInbox": "Moved to inbox",
|
||||
"movingToInbox": "Moving to inbox...",
|
||||
"movedToSpam": "Moved to spam",
|
||||
"movingToSpam": "Moving to spam...",
|
||||
"archiving": "Archiving...",
|
||||
"archived": "Archived",
|
||||
"failedToMove": "Failed to move message",
|
||||
"addingToFavorites": "Adding to favorites...",
|
||||
"removingFromFavorites": "Removing from favorites...",
|
||||
"addedToFavorites": "Added to favorites",
|
||||
"removedFromFavorites": "Removed from favorites",
|
||||
"failedToAddToFavorites": "Failed to add to favorites",
|
||||
"failedToRemoveFromFavorites": "Failed to remove from favorites",
|
||||
"failedToModifyFavorites": "Failed to modify favorites",
|
||||
"markingAsRead": "Marking as read...",
|
||||
"markingAsUnread": "Marking as unread..."
|
||||
},
|
||||
"themes": {
|
||||
"dark": "Тёмный",
|
||||
@@ -21,7 +39,7 @@
|
||||
},
|
||||
"commandPalette": {
|
||||
"title": "Команда",
|
||||
"description": "Быстрый переход и действия для Mail-0",
|
||||
"description": "Выход выполнен успешно",
|
||||
"placeholder": "Введите команду или поиск...",
|
||||
"noResults": "Нет результатов",
|
||||
"groups": {
|
||||
@@ -64,15 +82,15 @@
|
||||
"allMail": "Все Письма",
|
||||
"unread": "Непрочитанные",
|
||||
"hasAttachment": "Есть вложения",
|
||||
"starred": "Звездочечка",
|
||||
"starred": "Отмеченные",
|
||||
"applyFilters": "Применить фильтры",
|
||||
"reset": "Сбросить",
|
||||
"searching": "Searching...",
|
||||
"aiSuggestions": "AI Suggestions",
|
||||
"aiSearching": "AI is searching...",
|
||||
"aiSearchError": "AI search failed. Please try again.",
|
||||
"aiNoResults": "No AI suggestions found",
|
||||
"aiEnhancedQuery": "Enhanced search query"
|
||||
"searching": "Поиск...",
|
||||
"aiSuggestions": "Предложения ИИ",
|
||||
"aiSearching": "ИИ ищет...",
|
||||
"aiSearchError": "Поиск ИИ не удался. Пожалуйста, попробуйте снова.",
|
||||
"aiNoResults": "ИИ предложения не найдены",
|
||||
"aiEnhancedQuery": "Расширенный поиск"
|
||||
},
|
||||
"navUser": {
|
||||
"customerSupport": "Поддержка",
|
||||
@@ -83,8 +101,9 @@
|
||||
},
|
||||
"mailCategories": {
|
||||
"primary": "Основные",
|
||||
"allMail": "All Mail",
|
||||
"important": "Важные",
|
||||
"personal": "Персональные",
|
||||
"personal": "Личные",
|
||||
"updates": "Обновления",
|
||||
"promotions": "Промо",
|
||||
"social": "Социальные"
|
||||
@@ -94,11 +113,11 @@
|
||||
"thisEmail": "это письмо",
|
||||
"dropFiles": "Поместите файлы, чтобы прикрепить",
|
||||
"attachments": "Приложения",
|
||||
"attachmentCount": "{count, plural, =0 {attachments} one {attachment} other {attachments}}",
|
||||
"fileCount": "{count, plural, =0 {files} one {file} other {files}}",
|
||||
"attachmentCount": "{count, plural, =0 {вложений} one {вложение} few {вложения} many {вложений} other {вложений}}",
|
||||
"fileCount": "{count, plural, =0 {файлов} one {файл} few {файла} many {файлов} other {файла}}",
|
||||
"saveDraft": "Сохранить черновик",
|
||||
"send": "Отправить",
|
||||
"forward": "Forward"
|
||||
"forward": "Переслать"
|
||||
},
|
||||
"mailDisplay": {
|
||||
"details": "Подробнее",
|
||||
@@ -125,14 +144,14 @@
|
||||
"archive": "Архив",
|
||||
"reply": "Ответить",
|
||||
"moreOptions": "Больше опций",
|
||||
"moveToSpam": "Move to Spam",
|
||||
"moveToSpam": "Переместить в Спам",
|
||||
"replyAll": "Ответить всем",
|
||||
"forward": "Переслать",
|
||||
"markAsUnread": "Mark as Unread",
|
||||
"markAsRead": "Mark as Read",
|
||||
"markAsUnread": "Отметить как непрочитанное",
|
||||
"markAsRead": "Отметить как прочитанное",
|
||||
"addLabel": "Добавить метку",
|
||||
"muteThread": "Выключить поток",
|
||||
"favourites": "Favourites"
|
||||
"favourites": "Избранное"
|
||||
},
|
||||
"notes": {
|
||||
"title": "Заметки",
|
||||
@@ -195,10 +214,10 @@
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"notFound": "Settings not found",
|
||||
"saved": "Settings saved",
|
||||
"failedToSave": "Failed to save settings",
|
||||
"languageChanged": "Language changed to {locale}"
|
||||
"notFound": "Настройки не найдены",
|
||||
"saved": "Настройки сохранены",
|
||||
"failedToSave": "Не удалось сохранить настройки",
|
||||
"languageChanged": "Язык изменен на {locale}"
|
||||
},
|
||||
"mail": {
|
||||
"replies": "{count, plural, =0 {ответы} one {# ответ} few {# ответа} many {# ответов} other {# ответы}}",
|
||||
@@ -218,19 +237,20 @@
|
||||
"archive": "Архивировать",
|
||||
"moveToTrash": "Переместить в Корзину",
|
||||
"markAsUnread": "Отметить как непрочитанное",
|
||||
"markAsRead": "Mark as Read",
|
||||
"addStar": "В избранное",
|
||||
"markAsRead": "Отметить как прочитанное",
|
||||
"addFavorite": "Favorite",
|
||||
"removeFavorite": "Unfavorite",
|
||||
"muteThread": "Заглушить цепочку",
|
||||
"moving": "Перемещение...",
|
||||
"moved": "Перемещено",
|
||||
"errorMoving": "Ошибка при перемещении",
|
||||
"reply": "Reply",
|
||||
"replyAll": "Reply All",
|
||||
"forward": "Forward",
|
||||
"labels": "Labels",
|
||||
"createNewLabel": "Create New Label",
|
||||
"noLabelsAvailable": "No labels available",
|
||||
"loadMore": "Load more"
|
||||
"reply": "Ответить",
|
||||
"replyAll": "Ответить всем",
|
||||
"forward": "Переслать",
|
||||
"labels": "Ярлыки",
|
||||
"createNewLabel": "Создать новый ярлык",
|
||||
"noLabelsAvailable": "Нет доступных ярлыков",
|
||||
"loadMore": "Загрузить ещё"
|
||||
}
|
||||
},
|
||||
"navigation": {
|
||||
@@ -242,7 +262,6 @@
|
||||
"archive": "Архив",
|
||||
"bin": "Корзина",
|
||||
"feedback": "Отзывы",
|
||||
"contact": "Контакты",
|
||||
"settings": "Настройки"
|
||||
},
|
||||
"settings": {
|
||||
@@ -267,17 +286,17 @@
|
||||
"title": "Общие",
|
||||
"description": "Управление настройками языка и отображения электронной почты.",
|
||||
"language": "Язык",
|
||||
"selectLanguage": "Select a language",
|
||||
"selectLanguage": "Выберите язык",
|
||||
"timezone": "Часовой пояс",
|
||||
"selectTimezone": "Select a timezone",
|
||||
"selectTimezone": "Выбрать часовой пояс",
|
||||
"dynamicContent": "Динамический контент",
|
||||
"dynamicContentDescription": "Позволяет электронной почте отображать динамический контент.",
|
||||
"externalImages": "Отображать внешние изображения",
|
||||
"externalImagesDescription": "Позволяет электронной почте отображать изображения из внешних источников.",
|
||||
"languageChangedTo": "Language changed to {locale}",
|
||||
"customPrompt": "Custom AI Prompt",
|
||||
"customPromptPlaceholder": "Enter your custom prompt for the AI...",
|
||||
"customPromptDescription": "Customize how the AI writes your email replies. This will be added to the base prompt."
|
||||
"languageChangedTo": "Язык изменен на {locale}",
|
||||
"customPrompt": "Пользовательский ИИ Запрос",
|
||||
"customPromptPlaceholder": "Введите ваш запрос для ИИ...",
|
||||
"customPromptDescription": "Настройте, как ИИ будет писать ваши ответы на письма. Этот текст будет добавлен к основному запросу."
|
||||
},
|
||||
"connections": {
|
||||
"title": "Подключения",
|
||||
|
||||
@@ -12,7 +12,25 @@
|
||||
"signedOutSuccess": "Çıkış yapıldı!",
|
||||
"signOutError": "Çıkış yapılırken hata oluştu",
|
||||
"refresh": "Refresh",
|
||||
"loading": "Loading..."
|
||||
"loading": "Loading...",
|
||||
"featureNotImplemented": "This feature is not implemented yet",
|
||||
"moving": "Moving...",
|
||||
"movedToInbox": "Moved to inbox",
|
||||
"movingToInbox": "Moving to inbox...",
|
||||
"movedToSpam": "Moved to spam",
|
||||
"movingToSpam": "Moving to spam...",
|
||||
"archiving": "Archiving...",
|
||||
"archived": "Archived",
|
||||
"failedToMove": "Failed to move message",
|
||||
"addingToFavorites": "Adding to favorites...",
|
||||
"removingFromFavorites": "Removing from favorites...",
|
||||
"addedToFavorites": "Added to favorites",
|
||||
"removedFromFavorites": "Removed from favorites",
|
||||
"failedToAddToFavorites": "Failed to add to favorites",
|
||||
"failedToRemoveFromFavorites": "Failed to remove from favorites",
|
||||
"failedToModifyFavorites": "Failed to modify favorites",
|
||||
"markingAsRead": "Marking as read...",
|
||||
"markingAsUnread": "Marking as unread..."
|
||||
},
|
||||
"themes": {
|
||||
"dark": "Koyu",
|
||||
@@ -83,6 +101,7 @@
|
||||
},
|
||||
"mailCategories": {
|
||||
"primary": "Birincil",
|
||||
"allMail": "All Mail",
|
||||
"important": "Önemli",
|
||||
"personal": "Kişisel",
|
||||
"updates": "Güncellemeler",
|
||||
@@ -219,7 +238,8 @@
|
||||
"moveToTrash": "Çöp Kutusuna Taşı",
|
||||
"markAsUnread": "Okunmamış Olarak İşaretle",
|
||||
"markAsRead": "Mark as Read",
|
||||
"addStar": "Yıldız Ekle",
|
||||
"addFavorite": "Favorite",
|
||||
"removeFavorite": "Unfavorite",
|
||||
"muteThread": "Konuyu Sessize Al",
|
||||
"moving": "Taşınıyor...",
|
||||
"moved": "Taşındı",
|
||||
@@ -242,7 +262,6 @@
|
||||
"archive": "Arşiv",
|
||||
"bin": "Çöp",
|
||||
"feedback": "Geribildirim",
|
||||
"contact": "İletişim",
|
||||
"settings": "Ayarlar"
|
||||
},
|
||||
"settings": {
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
import { EU_COUNTRIES } from './constants/countries';
|
||||
import { navigationConfig } from '@/config/navigation';
|
||||
import { geolocation } from '@vercel/functions';
|
||||
import type { NextRequest } from 'next/server';
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
const disabledRoutes = Object.values(navigationConfig)
|
||||
.flatMap(section => section.sections)
|
||||
.flatMap(group => group.items)
|
||||
.filter(item => item.disabled && item.url !== '#')
|
||||
.map(item => item.url);
|
||||
|
||||
export function middleware(request: NextRequest) {
|
||||
const response = NextResponse.next();
|
||||
const geo = geolocation(request);
|
||||
@@ -17,6 +24,11 @@ export function middleware(request: NextRequest) {
|
||||
response.headers.set('x-user-eu-region', 'true');
|
||||
}
|
||||
|
||||
const pathname = request.nextUrl.pathname;
|
||||
if (disabledRoutes.some(route => pathname.startsWith(route))) {
|
||||
return NextResponse.redirect(new URL('/mail/inbox', request.url));
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user