Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions app/[locale]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { useDeviceDetection } from "@/hooks/use-media-query";
import { useKeyboardShortcuts } from "@/hooks/use-keyboard-shortcuts";
import { debug } from "@/lib/debug";
import { playNotificationSound } from "@/lib/notification-sound";
import { cn } from "@/lib/utils";
import { cn, getMailboxDisplayName } from "@/lib/utils";
import {
ErrorBoundary,
SidebarErrorFallback,
Expand Down Expand Up @@ -224,7 +224,7 @@ export default function Home() {
// Mailbox view
const mailbox = mailboxes.find(mb => mb.id === selectedMailbox);
if (mailbox) {
const mailboxName = mailbox.name;
const mailboxName = getMailboxDisplayName(mailbox.role, mailbox.name, (key) => t(`sidebar.${key}`));
const unreadCount = mailbox.unreadEmails || 0;
title = unreadCount > 0
? `${mailboxName} (${unreadCount}) - ${tCommon('app_title')}`
Expand Down Expand Up @@ -666,7 +666,10 @@ export default function Home() {
}

// Get current mailbox name for mobile header
const currentMailboxName = mailboxes.find(m => m.id === selectedMailbox)?.name || "Inbox";
const currentMailbox = mailboxes.find(m => m.id === selectedMailbox);
const currentMailboxName = currentMailbox
? getMailboxDisplayName(currentMailbox.role, currentMailbox.name, (key) => t(`sidebar.${key}`))
: t('sidebar.mailboxes.inbox');

// Handle email selection with mobile view switching
const handleEmailSelect = async (email: { id: string }) => {
Expand Down
5 changes: 3 additions & 2 deletions components/email/email-composer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import React, { useState, useEffect, useRef, useCallback } from "react";
import { useFocusTrap } from "@/hooks/use-focus-trap";
import { useConfirmDialog } from "@/hooks/use-confirm-dialog";
import { useTranslations } from "next-intl";
import { useTranslations, useLocale } from "next-intl";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { ConfirmDialog } from "@/components/ui/confirm-dialog";
Expand Down Expand Up @@ -59,6 +59,7 @@ export function EmailComposer({
}: EmailComposerProps) {
const t = useTranslations('email_composer');
const tCommon = useTranslations('common');
const locale = useLocale();

// Initialize with reply/forward data if provided
const getInitialTo = () => {
Expand Down Expand Up @@ -94,7 +95,7 @@ export function EmailComposer({
const prefix = initialDraftText || "";
if (!replyTo?.body) return prefix;

const date = replyTo.receivedAt ? new Date(replyTo.receivedAt).toLocaleString() : "";
const date = replyTo.receivedAt ? new Date(replyTo.receivedAt).toLocaleString(locale) : "";
const from = replyTo.from?.[0];
const fromStr = from ? `${from.name || from.email}` : tCommon('unknown');

Expand Down
5 changes: 3 additions & 2 deletions components/email/email-context-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
ShieldAlert,
ShieldCheck,
} from "lucide-react";
import { cn, buildMailboxTree, flattenMailboxTree } from "@/lib/utils";
import { cn, buildMailboxTree, flattenMailboxTree, getMailboxDisplayName } from "@/lib/utils";

interface Position {
x: number;
Expand Down Expand Up @@ -125,6 +125,7 @@ export function EmailContextMenu({
}: EmailContextMenuProps) {
const t = useTranslations("context_menu");
const tColor = useTranslations("email_viewer.color_tag");
const tSidebar = useTranslations("sidebar");
const isUnread = !email.keywords?.$seen;
const isStarred = email.keywords?.$flagged;
const currentColor = getCurrentColor(email.keywords);
Expand Down Expand Up @@ -228,7 +229,7 @@ export function EmailContextMenu({
<ContextMenuItem
key={mailbox.id}
icon={Icon}
label={mailbox.name}
label={getMailboxDisplayName(mailbox.role, mailbox.name, tSidebar)}
style={mailbox.depth > 0 ? { paddingLeft: `${12 + mailbox.depth * 12}px` } : undefined}
onClick={() =>
handleAction(() =>
Expand Down
9 changes: 6 additions & 3 deletions components/email/email-list-item.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { useTranslations } from "next-intl";
import { useTranslations, useLocale } from "next-intl";
import { formatDate } from "@/lib/utils";
import { Email } from "@/lib/jmap/types";
import { cn } from "@/lib/utils";
Expand Down Expand Up @@ -44,6 +44,9 @@ const getEmailColor = (keywords: Record<string, boolean> | undefined) => {

export function EmailListItem({ email, selected, onClick, onContextMenu }: EmailListItemProps) {
const t = useTranslations('email_viewer');
const tList = useTranslations('email_list');
const tDate = useTranslations('date');
const locale = useLocale();
const { selectedEmailIds, toggleEmailSelection, selectedMailbox } = useEmailStore();
const showPreview = useSettingsStore((state) => state.showPreview);
const listDensity = useSettingsStore((state) => state.listDensity);
Expand Down Expand Up @@ -184,7 +187,7 @@ export function EmailListItem({ email, selected, onClick, onContextMenu }: Email
? "text-foreground font-semibold"
: "text-muted-foreground"
)}>
{formatDate(email.receivedAt)}
{formatDate(email.receivedAt, tDate, locale)}
</span>
</div>

Expand All @@ -206,7 +209,7 @@ export function EmailListItem({ email, selected, onClick, onContextMenu }: Email
? "text-muted-foreground"
: "text-muted-foreground/80"
)}>
{email.preview || "No preview available"}
{email.preview || tList('no_preview')}
</p>
)}
</div>
Expand Down
11 changes: 6 additions & 5 deletions components/email/email-viewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ import {
Sparkles,
Keyboard,
} from "lucide-react";
import { useTranslations } from "next-intl";
import { useTranslations, useLocale } from "next-intl";
import { useSettingsStore } from "@/stores/settings-store";
import { useUIStore } from "@/stores/ui-store";
import { useDeviceDetection } from "@/hooks/use-media-query";
Expand Down Expand Up @@ -186,6 +186,7 @@ export function EmailViewer({
className,
}: EmailViewerProps) {
const t = useTranslations('email_viewer');
const locale = useLocale();
const tCommon = useTranslations('common');
const externalContentPolicy = useSettingsStore((state) => state.externalContentPolicy);
const addTrustedSender = useSettingsStore((state) => state.addTrustedSender);
Expand Down Expand Up @@ -799,7 +800,7 @@ export function EmailViewer({
<div className="flex items-center gap-2 lg:gap-3 mt-1.5 lg:mt-2 text-xs lg:text-sm text-muted-foreground flex-wrap lg:flex-nowrap">
<span className="flex items-center gap-1 lg:gap-1.5 whitespace-nowrap">
<Clock className="w-3.5 h-3.5 lg:w-4 lg:h-4" />
{new Date(email.receivedAt).toLocaleString('en-US', {
{new Date(email.receivedAt).toLocaleString(locale, {
weekday: 'short',
year: 'numeric',
month: 'short',
Expand Down Expand Up @@ -839,7 +840,7 @@ export function EmailViewer({
title={t('tooltips.reply')}
>
<Reply className="w-4 h-4" />
<span className="ml-1.5 hidden lg:inline">Reply</span>
<span className="ml-1.5 hidden lg:inline">{t('reply')}</span>
</Button>

{/* Reply Options Dropdown - hidden on mobile/tablet */}
Expand All @@ -858,14 +859,14 @@ export function EmailViewer({
className="w-full px-3 py-2 text-sm text-left hover:bg-muted text-foreground flex items-center gap-2"
>
<ReplyAll className="w-4 h-4" />
Reply all
{t('reply_all')}
</button>
<button
onClick={onForward}
className="w-full px-3 py-2 text-sm text-left hover:bg-muted text-foreground flex items-center gap-2"
>
<Forward className="w-4 h-4" />
Forward
{t('forward')}
</button>
</div>
</div>
Expand Down
9 changes: 5 additions & 4 deletions components/email/move-to-popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { useState, useRef, useEffect, useMemo } from "react";
import { createPortal } from "react-dom";
import { FolderInput, Search, Inbox, Send, FileText, Archive, Trash2, AlertTriangle, Folder } from "lucide-react";
import { cn } from "@/lib/utils";
import { cn, getMailboxDisplayName } from "@/lib/utils";
import { useTranslations } from "next-intl";
import { Mailbox } from "@/lib/jmap/types";

Expand Down Expand Up @@ -44,6 +44,7 @@ function getMailboxDepth(mailbox: Mailbox, allMailboxes: Mailbox[]): number {

export function MoveToPopover({ mailboxes, currentMailboxId, onMove, disabled }: MoveToPopoverProps) {
const t = useTranslations('email_list');
const tSidebar = useTranslations('sidebar');
const [isOpen, setIsOpen] = useState(false);
const [search, setSearch] = useState("");
const buttonRef = useRef<HTMLButtonElement>(null);
Expand All @@ -59,14 +60,14 @@ export function MoveToPopover({ mailboxes, currentMailboxId, onMove, disabled }:
const systemRoles = ['inbox', 'sent', 'drafts', 'archive', 'trash', 'junk'];
const query = search.toLowerCase();
const filtered = availableMailboxes.filter(m =>
m.name.toLowerCase().includes(query)
getMailboxDisplayName(m.role, m.name, tSidebar).toLowerCase().includes(query)
);

const system = filtered.filter(m => m.role && systemRoles.includes(m.role));
const custom = filtered.filter(m => !m.role || !systemRoles.includes(m.role));

return { system, custom };
}, [availableMailboxes, search]);
}, [availableMailboxes, search, tSidebar]);

const allFiltered = [...filteredMailboxes.system, ...filteredMailboxes.custom];

Expand Down Expand Up @@ -214,7 +215,7 @@ export function MoveToPopover({ mailboxes, currentMailboxId, onMove, disabled }:
onClick={() => handleMove(mailbox.id)}
>
<Icon className="w-4 h-4 flex-shrink-0" />
<span>{highlightMatch(mailbox.name)}</span>
<span>{highlightMatch(getMailboxDisplayName(mailbox.role, mailbox.name, tSidebar))}</span>
</button>
);
})}
Expand Down
8 changes: 5 additions & 3 deletions components/email/thread-conversation-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
FileArchive,
File,
} from "lucide-react";
import { useTranslations } from "next-intl";
import { useTranslations, useLocale } from "next-intl";
import { useSettingsStore } from "@/stores/settings-store";
import { useAuthStore } from "@/stores/auth-store";

Expand Down Expand Up @@ -221,6 +221,8 @@ function EmailCard({
onMarkAsRead,
}: EmailCardProps) {
const t = useTranslations();
const tDate = useTranslations('date');
const locale = useLocale();
const resolvedTheme = useThemeStore((state) => state.resolvedTheme);
const sender = email.from?.[0];
const isUnread = !email.keywords?.$seen;
Expand Down Expand Up @@ -458,11 +460,11 @@ function EmailCard({
)}
</div>
<div className="text-sm text-muted-foreground">
{formatDate(email.receivedAt)}
{formatDate(email.receivedAt, tDate, locale)}
</div>
{!isExpanded && (
<p className="text-sm text-muted-foreground mt-1 line-clamp-2">
{email.preview || "No preview available"}
{email.preview || t('email_list.no_preview')}
</p>
)}
</div>
Expand Down
10 changes: 7 additions & 3 deletions components/email/thread-email-item.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

import { useTranslations, useLocale } from "next-intl";
import { formatDate } from "@/lib/utils";
import { Email } from "@/lib/jmap/types";
import { cn } from "@/lib/utils";
Expand All @@ -21,6 +22,9 @@ export function ThreadEmailItem({
onClick,
onContextMenu,
}: ThreadEmailItemProps) {
const t = useTranslations();
const tDate = useTranslations('date');
const locale = useLocale();
const isUnread = !email.keywords?.$seen;
const isStarred = email.keywords?.$flagged;
const sender = email.from?.[0];
Expand Down Expand Up @@ -70,7 +74,7 @@ export function ThreadEmailItem({
? "font-semibold text-foreground"
: "font-medium text-muted-foreground"
)}>
{sender?.name || sender?.email?.split('@')[0] || "Unknown"}
{sender?.name || sender?.email?.split('@')[0] || t('email_viewer.unknown_sender')}
</span>

{/* Indicators */}
Expand All @@ -90,7 +94,7 @@ export function ThreadEmailItem({
? "text-muted-foreground"
: "text-muted-foreground/70"
)}>
{email.preview || "No preview"}
{email.preview || t('email_list.no_preview')}
</span>

{/* Date */}
Expand All @@ -100,7 +104,7 @@ export function ThreadEmailItem({
? "text-foreground font-medium"
: "text-muted-foreground"
)}>
{formatDate(email.receivedAt)}
{formatDate(email.receivedAt, tDate, locale)}
</span>
</div>
</div>
Expand Down
20 changes: 13 additions & 7 deletions components/email/thread-list-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { useUIStore } from "@/stores/ui-store";
import { useEmailStore } from "@/stores/email-store";
import { getThreadColorTag } from "@/lib/thread-utils";
import { ThreadEmailItem } from "./thread-email-item";
import { useTranslations } from "next-intl";
import { useTranslations, useLocale } from "next-intl";

interface ThreadListItemProps {
thread: ThreadGroup;
Expand Down Expand Up @@ -51,6 +51,9 @@ interface SingleEmailItemProps {

const SingleEmailItem = React.forwardRef<HTMLDivElement, SingleEmailItemProps>(
function SingleEmailItem({ email, selected, onClick, onContextMenu, showPreview, colorTag, isChecked, onCheckboxClick, folderBadgeName }, ref) {
const t = useTranslations();
const tDate = useTranslations('date');
const locale = useLocale();
const isUnread = !email.keywords?.$seen;
const isStarred = email.keywords?.$flagged;
const sender = email.from?.[0];
Expand Down Expand Up @@ -146,7 +149,7 @@ const SingleEmailItem = React.forwardRef<HTMLDivElement, SingleEmailItemProps>(
? "text-foreground font-semibold"
: "text-muted-foreground"
)}>
{formatDate(email.receivedAt)}
{formatDate(email.receivedAt, tDate, locale)}
</span>
</div>

Expand All @@ -156,7 +159,7 @@ const SingleEmailItem = React.forwardRef<HTMLDivElement, SingleEmailItemProps>(
? "font-semibold text-foreground"
: "font-normal text-foreground/90"
)}>
{email.subject || "(no subject)"}
{email.subject || t('email_viewer.no_subject')}
</div>

{showPreview && (
Expand All @@ -166,7 +169,7 @@ const SingleEmailItem = React.forwardRef<HTMLDivElement, SingleEmailItemProps>(
? "text-muted-foreground"
: "text-muted-foreground/80"
)}>
{email.preview || "No preview available"}
{email.preview || t('email_list.no_preview')}
</p>
)}
</div>
Expand All @@ -191,6 +194,9 @@ export const ThreadListItem = React.forwardRef<HTMLDivElement, ThreadListItemPro
onCheckboxClick,
}, ref) {
const t = useTranslations('threads');
const tRoot = useTranslations();
const tDate = useTranslations('date');
const locale = useLocale();
const showPreview = useSettingsStore((state) => state.showPreview);
const isMobile = useUIStore((state) => state.isMobile);
const { currentQuery, mailboxes } = useEmailStore();
Expand Down Expand Up @@ -376,7 +382,7 @@ export const ThreadListItem = React.forwardRef<HTMLDivElement, ThreadListItemPro
? "text-foreground font-semibold"
: "text-muted-foreground"
)}>
{formatDate(latestEmail.receivedAt)}
{formatDate(latestEmail.receivedAt, tDate, locale)}
</span>
</div>

Expand All @@ -386,7 +392,7 @@ export const ThreadListItem = React.forwardRef<HTMLDivElement, ThreadListItemPro
? "font-semibold text-foreground"
: "font-normal text-foreground/90"
)}>
{latestEmail.subject || "(no subject)"}
{latestEmail.subject || tRoot('email_viewer.no_subject')}
</div>

{showPreview && (
Expand All @@ -396,7 +402,7 @@ export const ThreadListItem = React.forwardRef<HTMLDivElement, ThreadListItemPro
? "text-muted-foreground"
: "text-muted-foreground/80"
)}>
{latestEmail.preview || "No preview available"}
{latestEmail.preview || tRoot('email_list.no_preview')}
</p>
)}
</div>
Expand Down
Loading