fixes from review
This commit is contained in:
@@ -117,6 +117,30 @@ export default function Home() {
|
|||||||
const sendDelaySeconds = useSettingsStore((state) => state.sendDelaySeconds);
|
const sendDelaySeconds = useSettingsStore((state) => state.sendDelaySeconds);
|
||||||
const { loadTrustedSendersBook, trustedSendersLoaded } = useContactStore();
|
const { loadTrustedSendersBook, trustedSendersLoaded } = useContactStore();
|
||||||
|
|
||||||
|
const promptForRescheduleDelayedUntil = useCallback((): string | null => {
|
||||||
|
const value = window.prompt(t('email_viewer.reschedule_prompt'));
|
||||||
|
if (!value) return null;
|
||||||
|
const time = new Date(value).getTime();
|
||||||
|
if (!Number.isFinite(time)) {
|
||||||
|
toast.error(t('email_composer.schedule_send_invalid'));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (time <= Date.now()) {
|
||||||
|
toast.error(t('email_composer.schedule_send_future'));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!client?.hasDelayedSend()) {
|
||||||
|
toast.error(t('email_composer.schedule_send_unsupported'));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const maxDelayedSend = client.getMaxDelayedSend();
|
||||||
|
if (maxDelayedSend > 0 && time > Date.now() + maxDelayedSend * 1000) {
|
||||||
|
toast.error(t('email_composer.schedule_send_too_late'));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new Date(time).toISOString();
|
||||||
|
}, [client, t]);
|
||||||
|
|
||||||
// Load trusted senders address book when feature is enabled
|
// Load trusted senders address book when feature is enabled
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (trustedSendersAddressBook && client && !trustedSendersLoaded) {
|
if (trustedSendersAddressBook && client && !trustedSendersLoaded) {
|
||||||
@@ -2721,6 +2745,12 @@ export default function Home() {
|
|||||||
setShowComposer(true);
|
setShowComposer(true);
|
||||||
if (isMobile) setActiveView('viewer');
|
if (isMobile) setActiveView('viewer');
|
||||||
}}
|
}}
|
||||||
|
onRescheduleScheduled={async (email) => {
|
||||||
|
const delayedUntil = promptForRescheduleDelayedUntil();
|
||||||
|
if (delayedUntil && client && email.emailSubmissionId && email.scheduledIdentityId) {
|
||||||
|
await rescheduleScheduledEmail(client, email.emailSubmissionId, email.id, email.scheduledIdentityId, delayedUntil);
|
||||||
|
}
|
||||||
|
}}
|
||||||
onEmailSelect={handleEmailSelect}
|
onEmailSelect={handleEmailSelect}
|
||||||
onEmailDoubleClick={isEmbedded ? ((email) => {
|
onEmailDoubleClick={isEmbedded ? ((email) => {
|
||||||
useProTabStore.getState().openEmailTab({
|
useProTabStore.getState().openEmailTab({
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ interface EmailListProps {
|
|||||||
onLoadMoreScheduled?: () => void;
|
onLoadMoreScheduled?: () => void;
|
||||||
onCancelScheduled?: (email: Email) => void | Promise<void>;
|
onCancelScheduled?: (email: Email) => void | Promise<void>;
|
||||||
onCancelScheduledForEdit?: (email: Email) => void | Promise<void>;
|
onCancelScheduledForEdit?: (email: Email) => void | Promise<void>;
|
||||||
|
onRescheduleScheduled?: (email: Email) => void | Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function EmailList({
|
export function EmailList({
|
||||||
@@ -73,6 +74,7 @@ export function EmailList({
|
|||||||
onLoadMoreScheduled,
|
onLoadMoreScheduled,
|
||||||
onCancelScheduled,
|
onCancelScheduled,
|
||||||
onCancelScheduledForEdit,
|
onCancelScheduledForEdit,
|
||||||
|
onRescheduleScheduled,
|
||||||
}: EmailListProps) {
|
}: EmailListProps) {
|
||||||
const t = useTranslations('email_list');
|
const t = useTranslations('email_list');
|
||||||
const { client } = useAuthStore();
|
const { client } = useAuthStore();
|
||||||
@@ -535,9 +537,9 @@ export function EmailList({
|
|||||||
onMarkAsSpam={() => onMarkAsSpam?.(contextMenu.data!)}
|
onMarkAsSpam={() => onMarkAsSpam?.(contextMenu.data!)}
|
||||||
onUndoSpam={() => onUndoSpam?.(contextMenu.data!)}
|
onUndoSpam={() => onUndoSpam?.(contextMenu.data!)}
|
||||||
onEditDraft={() => onEditDraft?.(contextMenu.data!)}
|
onEditDraft={() => onEditDraft?.(contextMenu.data!)}
|
||||||
onCancelScheduled={() => onCancelScheduled?.(contextMenu.data!)}
|
onCancelScheduled={onCancelScheduled ? () => onCancelScheduled(contextMenu.data!) : undefined}
|
||||||
onCancelScheduledForEdit={() => onCancelScheduledForEdit?.(contextMenu.data!)}
|
onCancelScheduledForEdit={onCancelScheduledForEdit ? () => onCancelScheduledForEdit(contextMenu.data!) : undefined}
|
||||||
onRescheduleScheduled={undefined}
|
onRescheduleScheduled={onRescheduleScheduled ? () => onRescheduleScheduled(contextMenu.data!) : undefined}
|
||||||
onBatchMarkAsRead={(read) => client && batchMarkAsRead(client, read)}
|
onBatchMarkAsRead={(read) => client && batchMarkAsRead(client, read)}
|
||||||
onBatchDelete={() => client && batchDelete(client)}
|
onBatchDelete={() => client && batchDelete(client)}
|
||||||
onBatchArchive={async () => {
|
onBatchArchive={async () => {
|
||||||
|
|||||||
@@ -335,16 +335,18 @@ const SingleEmailItem = React.forwardRef<HTMLDivElement, SingleEmailItemProps>(
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Hover Quick Actions */}
|
{/* Hover Quick Actions */}
|
||||||
<EmailHoverActions
|
{!email.isScheduled && (
|
||||||
email={email}
|
<EmailHoverActions
|
||||||
backgroundClassName={resolvedColorTag ? resolvedColorTag : ((selected || isChecked) ? "bg-accent" : "bg-muted")}
|
email={email}
|
||||||
onToggleStar={onToggleStar}
|
backgroundClassName={resolvedColorTag ? resolvedColorTag : ((selected || isChecked) ? "bg-accent" : "bg-muted")}
|
||||||
onMarkAsRead={onMarkAsRead}
|
onToggleStar={onToggleStar}
|
||||||
onDelete={onDelete}
|
onMarkAsRead={onMarkAsRead}
|
||||||
onArchive={onArchive}
|
onDelete={onDelete}
|
||||||
onSetColorTag={onSetColorTag}
|
onArchive={onArchive}
|
||||||
onMarkAsSpam={onMarkAsSpam}
|
onSetColorTag={onSetColorTag}
|
||||||
/>
|
onMarkAsSpam={onMarkAsSpam}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -751,16 +753,18 @@ export const ThreadListItem = React.forwardRef<HTMLDivElement, ThreadListItemPro
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Hover Quick Actions for thread header */}
|
{/* Hover Quick Actions for thread header */}
|
||||||
<EmailHoverActions
|
{!latestEmail.isScheduled && (
|
||||||
email={latestEmail}
|
<EmailHoverActions
|
||||||
backgroundClassName={colorTag ? colorTag : ((isSelected || isChecked) ? "bg-accent" : "bg-muted")}
|
email={latestEmail}
|
||||||
onToggleStar={onToggleStar ? () => onToggleStar(latestEmail) : undefined}
|
backgroundClassName={colorTag ? colorTag : ((isSelected || isChecked) ? "bg-accent" : "bg-muted")}
|
||||||
onMarkAsRead={onMarkAsRead ? (read) => onMarkAsRead(latestEmail, read) : undefined}
|
onToggleStar={onToggleStar ? () => onToggleStar(latestEmail) : undefined}
|
||||||
onDelete={onDelete ? () => onDelete(latestEmail) : undefined}
|
onMarkAsRead={onMarkAsRead ? (read) => onMarkAsRead(latestEmail, read) : undefined}
|
||||||
onArchive={onArchive ? () => onArchive(latestEmail) : undefined}
|
onDelete={onDelete ? () => onDelete(latestEmail) : undefined}
|
||||||
onSetColorTag={onSetColorTag ? (color) => onSetColorTag(latestEmail.id, color) : undefined}
|
onArchive={onArchive ? () => onArchive(latestEmail) : undefined}
|
||||||
onMarkAsSpam={onMarkAsSpam ? () => onMarkAsSpam(latestEmail) : undefined}
|
onSetColorTag={onSetColorTag ? (color) => onSetColorTag(latestEmail.id, color) : undefined}
|
||||||
/>
|
onMarkAsSpam={onMarkAsSpam ? () => onMarkAsSpam(latestEmail) : undefined}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isExpanded && !isMobile && !isFocusedMailLayout && (
|
{isExpanded && !isMobile && !isFocusedMailLayout && (
|
||||||
|
|||||||
@@ -2566,6 +2566,15 @@ export const useEmailStore = create<EmailStore>((set, get) => ({
|
|||||||
const submissionId = email.emailSubmissionId;
|
const submissionId = email.emailSubmissionId;
|
||||||
if (!submissionId) return null;
|
if (!submissionId) return null;
|
||||||
await client.cancelEmailSubmission(submissionId);
|
await client.cancelEmailSubmission(submissionId);
|
||||||
|
if (email.isSmimeScheduled) {
|
||||||
|
await client.deleteEmail(email.id);
|
||||||
|
set(state => ({
|
||||||
|
selectedEmail: state.selectedEmail?.id === email.id ? null : state.selectedEmail,
|
||||||
|
selectedEmailIds: new Set(Array.from(state.selectedEmailIds).filter(id => id !== email.id)),
|
||||||
|
}));
|
||||||
|
await get().fetchScheduledEmails(client);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
const mailboxes = get().mailboxes.length > 0 ? get().mailboxes : await client.getMailboxes();
|
const mailboxes = get().mailboxes.length > 0 ? get().mailboxes : await client.getMailboxes();
|
||||||
const draftsMailbox = mailboxes.find(mb => mb.role === 'drafts');
|
const draftsMailbox = mailboxes.find(mb => mb.role === 'drafts');
|
||||||
const sentMailbox = mailboxes.find(mb => mb.role === 'sent');
|
const sentMailbox = mailboxes.find(mb => mb.role === 'sent');
|
||||||
|
|||||||
Reference in New Issue
Block a user