Skip to content
Draft
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
259 changes: 194 additions & 65 deletions src/renderer/App.tsx

Large diffs are not rendered by default.

94 changes: 63 additions & 31 deletions src/renderer/components/CommandPalette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,26 +103,24 @@ export function CommandPalette({ isOpen, onClose }: CommandPaletteProps) {
const inputRef = useRef<HTMLInputElement>(null);
const listRef = useRef<HTMLDivElement>(null);

const {
accounts,
currentAccountId,
selectedEmailId,
selectedThreadId,
viewMode,
themePreference,
inboxDensity,
openSearch,
openCompose,
setShowSettings,
setViewMode,
setThemePreference,
setInboxDensity,
setCurrentAccountId,
setSelectedEmailId,
setSelectedThreadId,
setShowSnoozeMenu,
emails,
} = useAppStore();
const accounts = useAppStore((s) => s.accounts);
const currentAccountId = useAppStore((s) => s.currentAccountId);
const selectedEmailId = useAppStore((s) => s.selectedEmailId);
const selectedThreadId = useAppStore((s) => s.selectedThreadId);
const viewMode = useAppStore((s) => s.viewMode);
const themePreference = useAppStore((s) => s.themePreference);
const inboxDensity = useAppStore((s) => s.inboxDensity);
const openSearch = useAppStore((s) => s.openSearch);
const openCompose = useAppStore((s) => s.openCompose);
const setShowSettings = useAppStore((s) => s.setShowSettings);
const setViewMode = useAppStore((s) => s.setViewMode);
const setThemePreference = useAppStore((s) => s.setThemePreference);
const setInboxDensity = useAppStore((s) => s.setInboxDensity);
const setCurrentAccountId = useAppStore((s) => s.setCurrentAccountId);
const setSelectedEmailId = useAppStore((s) => s.setSelectedEmailId);
const setSelectedThreadId = useAppStore((s) => s.setSelectedThreadId);
const setShowSnoozeMenu = useAppStore((s) => s.setShowSnoozeMenu);
const emails = useAppStore((s) => s.emails);

const { threads } = useThreadedEmails();

Expand Down Expand Up @@ -228,8 +226,13 @@ export function CommandPalette({ isOpen, onClose }: CommandPaletteProps) {
available: () => hasSelectedEmail,
execute: () => {
const state = useAppStore.getState();
if (state.selectedThreadId && state.currentAccountId) {
const threadEmails = state.emails.filter((e) => e.threadId === state.selectedThreadId);
const accountId =
state.emails.find((email) => email.id === state.selectedEmailId)?.accountId ??
state.currentAccountId;
if (state.selectedThreadId && accountId) {
const threadEmails = state.emails.filter(
(e) => e.threadId === state.selectedThreadId && e.accountId === accountId,
);
state.removeEmailsAndAdvance(
threadEmails.map((e) => e.id),
null,
Expand All @@ -239,7 +242,7 @@ export function CommandPalette({ isOpen, onClose }: CommandPaletteProps) {
id: `archive-${state.selectedThreadId}-${Date.now()}`,
type: "archive",
threadCount: 1,
accountId: state.currentAccountId,
accountId,
emails: [...threadEmails],
scheduledAt: Date.now(),
delayMs: 5000,
Expand All @@ -256,8 +259,13 @@ export function CommandPalette({ isOpen, onClose }: CommandPaletteProps) {
available: () => hasSelectedEmail,
execute: () => {
const state = useAppStore.getState();
if (state.selectedThreadId && state.currentAccountId) {
const threadEmails = state.emails.filter((e) => e.threadId === state.selectedThreadId);
const accountId =
state.emails.find((email) => email.id === state.selectedEmailId)?.accountId ??
state.currentAccountId;
if (state.selectedThreadId && accountId) {
const threadEmails = state.emails.filter(
(e) => e.threadId === state.selectedThreadId && e.accountId === accountId,
);
state.removeEmailsAndAdvance(
threadEmails.map((e) => e.id),
null,
Expand All @@ -267,7 +275,7 @@ export function CommandPalette({ isOpen, onClose }: CommandPaletteProps) {
id: `trash-${state.selectedThreadId}-${Date.now()}`,
type: "trash",
threadCount: 1,
accountId: state.currentAccountId,
accountId,
emails: [...threadEmails],
scheduledAt: Date.now(),
delayMs: 5000,
Expand Down Expand Up @@ -301,8 +309,13 @@ export function CommandPalette({ isOpen, onClose }: CommandPaletteProps) {
available: () => hasSelectedThread,
execute: () => {
const state = useAppStore.getState();
if (state.selectedThreadId && state.currentAccountId) {
const threadEmails = state.emails.filter((e) => e.threadId === state.selectedThreadId);
const accountId =
state.emails.find((email) => email.id === state.selectedEmailId)?.accountId ??
state.currentAccountId;
if (state.selectedThreadId && accountId) {
const threadEmails = state.emails.filter(
(e) => e.threadId === state.selectedThreadId && e.accountId === accountId,
);
const latest = threadEmails.reduce(
(a, b) => (new Date(a.date).getTime() >= new Date(b.date).getTime() ? a : b),
threadEmails[0],
Expand All @@ -316,7 +329,7 @@ export function CommandPalette({ isOpen, onClose }: CommandPaletteProps) {
id: `mark-unread-${state.selectedThreadId}-${Date.now()}`,
type: "mark-unread",
threadCount: 1,
accountId: state.currentAccountId,
accountId,
emails: [latest],
scheduledAt: Date.now(),
delayMs: 5000,
Expand Down Expand Up @@ -346,7 +359,10 @@ export function CommandPalette({ isOpen, onClose }: CommandPaletteProps) {
available: () => hasSelectedEmail,
execute: () => {
const state = useAppStore.getState();
if (state.selectedEmailId && state.currentAccountId) {
const accountId =
state.emails.find((email) => email.id === state.selectedEmailId)?.accountId ??
state.currentAccountId;
if (state.selectedEmailId && accountId) {
const email = state.emails.find((e) => e.id === state.selectedEmailId);
if (email) {
const currentLabels = email.labelIds || [];
Expand All @@ -360,7 +376,7 @@ export function CommandPalette({ isOpen, onClose }: CommandPaletteProps) {
id: `${isStarred ? "unstar" : "star"}-${email.threadId}-${Date.now()}`,
type: isStarred ? "unstar" : "star",
threadCount: 1,
accountId: state.currentAccountId,
accountId,
emails: [email],
scheduledAt: Date.now(),
delayMs: 5000,
Expand Down Expand Up @@ -390,6 +406,10 @@ export function CommandPalette({ isOpen, onClose }: CommandPaletteProps) {
const state = useAppStore.getState();
if (state.currentAccountId) {
window.api.sync.now(state.currentAccountId);
} else {
state.accounts.forEach((account) => {
window.api.sync.now(account.id).catch(console.error);
});
}
},
},
Expand Down Expand Up @@ -580,6 +600,17 @@ export function CommandPalette({ isOpen, onClose }: CommandPaletteProps) {
},

// --- Account switching ---
{
id: "switch-account-all",
label: "Switch to all accounts",
category: "Accounts",
icon: ICONS.user,
available: () => currentAccountId !== null,
execute: () => {
setCurrentAccountId(null);
window.api.settings.set({ selectedAccountId: null }).catch(console.error);
},
},
...accounts.map((account) => ({
id: `switch-account-${account.id}`,
label: `Switch to ${account.email}`,
Expand All @@ -588,6 +619,7 @@ export function CommandPalette({ isOpen, onClose }: CommandPaletteProps) {
available: () => account.id !== currentAccountId,
execute: () => {
setCurrentAccountId(account.id);
window.api.settings.set({ selectedAccountId: account.id }).catch(console.error);
window.api.sync.getEmails(account.id).then((result: IpcResponse<DashboardEmail[]>) => {
if (result.success && result.data) {
const otherEmails = useAppStore
Expand Down
15 changes: 14 additions & 1 deletion src/renderer/components/DraftRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ interface DraftRowProps {
draft: LocalDraft;
isSelected: boolean;
density: InboxDensity;
accountLabel?: string;
onClick: () => void;
}

Expand Down Expand Up @@ -80,7 +81,7 @@ function stripHtmlTags(html: string): string {
}

export const DraftRow = React.memo(
function DraftRow({ draft, isSelected, density, onClick }: DraftRowProps) {
function DraftRow({ draft, isSelected, density, accountLabel, onClick }: DraftRowProps) {
const ds = densityStyles[density] ?? densityStyles.default;
const recipients = draft.to.join(", ");
const snippet = draft.bodyText || stripHtmlTags(draft.bodyHtml);
Expand Down Expand Up @@ -115,6 +116,17 @@ export const DraftRow = React.memo(
{recipients || "(no recipients)"}
</div>

{accountLabel && (
<span
className={`
${ds.badge} rounded flex-shrink-0 uppercase font-medium
${isSelected ? "bg-white/20 text-white" : "bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400"}
`}
>
{accountLabel}
</span>
)}

{/* Draft badge */}
<span
className={`
Expand Down Expand Up @@ -172,5 +184,6 @@ export const DraftRow = React.memo(
prev.draft.id === next.draft.id &&
prev.draft.updatedAt === next.draft.updatedAt &&
prev.isSelected === next.isSelected &&
prev.accountLabel === next.accountLabel &&
prev.density === next.density,
);
Loading