fixes
This commit is contained in:
@@ -26,7 +26,10 @@ export function ProComposeTabBody({ tabId, data }: ProComposeTabBodyProps) {
|
||||
const client = useAuthStore((s) => s.client);
|
||||
const sendEmail = useEmailStore((s) => s.sendEmail);
|
||||
const fetchEmails = useEmailStore((s) => s.fetchEmails);
|
||||
const fetchScheduledEmails = useEmailStore((s) => s.fetchScheduledEmails);
|
||||
const refreshScheduledMetadata = useEmailStore((s) => s.refreshScheduledMetadata);
|
||||
const selectedMailbox = useEmailStore((s) => s.selectedMailbox);
|
||||
const isScheduledView = useEmailStore((s) => s.isScheduledView);
|
||||
const closeTab = useProTabStore((s) => s.closeTab);
|
||||
const updateTabTitle = useProTabStore((s) => s.updateTabTitle);
|
||||
const updateComposeDraft = useProTabStore((s) => s.updateComposeDraft);
|
||||
@@ -36,6 +39,14 @@ export function ProComposeTabBody({ tabId, data }: ProComposeTabBodyProps) {
|
||||
const tabIdRef = useRef(tabId);
|
||||
tabIdRef.current = tabId;
|
||||
|
||||
const handleScheduledSendCreated = useCallback(async () => {
|
||||
if (client) {
|
||||
await refreshScheduledMetadata(client);
|
||||
if (isScheduledView) await fetchScheduledEmails(client);
|
||||
}
|
||||
closeTab(tabIdRef.current);
|
||||
}, [client, refreshScheduledMetadata, isScheduledView, fetchScheduledEmails, closeTab]);
|
||||
|
||||
const handleSend = useCallback(async (sendData: Parameters<NonNullable<React.ComponentProps<typeof EmailComposer>['onSend']>>[0]) => {
|
||||
if (!client) return;
|
||||
try {
|
||||
@@ -59,7 +70,7 @@ export function ProComposeTabBody({ tabId, data }: ProComposeTabBodyProps) {
|
||||
);
|
||||
|
||||
if (result.scheduled) {
|
||||
closeTab(tabIdRef.current);
|
||||
await handleScheduledSendCreated();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -87,7 +98,7 @@ export function ProComposeTabBody({ tabId, data }: ProComposeTabBodyProps) {
|
||||
console.error('Failed to send email:', error);
|
||||
toast.error(t('notifications.error_sending'));
|
||||
}
|
||||
}, [client, sendEmail, fetchEmails, selectedMailbox, closeTab, data.sourceEmailId, data.mode, t]);
|
||||
}, [client, sendEmail, fetchEmails, selectedMailbox, closeTab, data.sourceEmailId, data.mode, t, handleScheduledSendCreated]);
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
closeTab(tabIdRef.current);
|
||||
@@ -131,6 +142,7 @@ export function ProComposeTabBody({ tabId, data }: ProComposeTabBodyProps) {
|
||||
initialDraftText={data.initialDraftText}
|
||||
initialData={data.initialData}
|
||||
onSend={handleSend}
|
||||
onScheduledSendCreated={handleScheduledSendCreated}
|
||||
onClose={handleClose}
|
||||
onDiscardDraft={handleDiscardDraft}
|
||||
onSaveState={handleSaveState}
|
||||
|
||||
@@ -959,7 +959,7 @@ export class DemoJMAPClient implements IJMAPClient {
|
||||
return delayedUntil ? { scheduled: true, emailId, emailSubmissionId, sendAt: delayedUntil, isSmime: true } : { scheduled: false, emailId, isSmime: true };
|
||||
}
|
||||
|
||||
async getScheduledEmails(limit = 50, position = 0): Promise<{ emails: ScheduledEmail[]; hasMore: boolean; total: number }> {
|
||||
async getScheduledEmails(limit = 50, position = 0): Promise<{ emails: ScheduledEmail[]; hasMore: boolean; total: number; nextPosition: number }> {
|
||||
const pending = Array.from(this.scheduledSubmissions.values())
|
||||
.filter(s => s.undoStatus === 'pending')
|
||||
.sort((a, b) => new Date(a.sendAt).getTime() - new Date(b.sendAt).getTime());
|
||||
@@ -977,7 +977,8 @@ export class DemoJMAPClient implements IJMAPClient {
|
||||
isSmimeScheduled: submission.isSmime,
|
||||
} satisfies ScheduledEmail;
|
||||
}).filter((email): email is ScheduledEmail => email !== null);
|
||||
return { emails, hasMore: position + emails.length < pending.length, total: pending.length };
|
||||
const nextPosition = position + page.length;
|
||||
return { emails, hasMore: nextPosition < pending.length, total: pending.length, nextPosition };
|
||||
}
|
||||
|
||||
async cancelEmailSubmission(submissionId: string): Promise<void> {
|
||||
|
||||
@@ -153,7 +153,7 @@ export interface IJMAPClient {
|
||||
): Promise<SendEmailResult>;
|
||||
|
||||
sendRawEmail(blob: Blob, identityId: string, sentMailboxId: string, draftMailboxId?: string, delayedUntil?: string, envelopeRecipients?: string[]): Promise<SendEmailResult>;
|
||||
getScheduledEmails(limit?: number, position?: number): Promise<{ emails: ScheduledEmail[]; hasMore: boolean; total: number }>;
|
||||
getScheduledEmails(limit?: number, position?: number): Promise<{ emails: ScheduledEmail[]; hasMore: boolean; total: number; nextPosition: number }>;
|
||||
cancelEmailSubmission(submissionId: string): Promise<void>;
|
||||
rescheduleEmailSubmission(submissionId: string, emailId: string, identityId: string, delayedUntil: string): Promise<SendEmailResult>;
|
||||
restoreEmailToDraft(emailId: string, draftMailboxId: string, sentMailboxId?: string): Promise<void>;
|
||||
|
||||
+5
-4
@@ -5617,9 +5617,9 @@ export class JMAPClient implements IJMAPClient {
|
||||
: { scheduled: false, emailId, emailSubmissionId, isSmime: true };
|
||||
}
|
||||
|
||||
async getScheduledEmails(limit = 50, position = 0): Promise<{ emails: ScheduledEmail[]; hasMore: boolean; total: number }> {
|
||||
async getScheduledEmails(limit = 50, position = 0): Promise<{ emails: ScheduledEmail[]; hasMore: boolean; total: number; nextPosition: number }> {
|
||||
if (!this.hasDelayedSend()) {
|
||||
return { emails: [], hasMore: false, total: 0 };
|
||||
return { emails: [], hasMore: false, total: 0, nextPosition: position };
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
@@ -5663,9 +5663,10 @@ export class JMAPClient implements IJMAPClient {
|
||||
submissions.sort((a, b) => new Date(a.sendAt || '').getTime() - new Date(b.sendAt || '').getTime());
|
||||
const total = submissions.length;
|
||||
const pageSubmissions = submissions.slice(position, position + limit);
|
||||
const nextPosition = position + pageSubmissions.length;
|
||||
|
||||
if (pageSubmissions.length === 0) {
|
||||
return { emails: [], hasMore: false, total };
|
||||
return { emails: [], hasMore: false, total, nextPosition };
|
||||
}
|
||||
|
||||
const emailResponse = await this.request([
|
||||
@@ -5704,7 +5705,7 @@ export class JMAPClient implements IJMAPClient {
|
||||
.filter((email): email is ScheduledEmail => email !== null)
|
||||
.sort((a, b) => new Date(a.scheduledSendAt).getTime() - new Date(b.scheduledSendAt).getTime());
|
||||
|
||||
return { emails, hasMore: position + emails.length < total, total };
|
||||
return { emails, hasMore: nextPosition < total, total, nextPosition };
|
||||
}
|
||||
|
||||
async cancelEmailSubmission(submissionId: string): Promise<void> {
|
||||
|
||||
+29
-9
@@ -84,6 +84,7 @@ interface EmailStore {
|
||||
scheduledSubmissionByEmailId: Map<string, ScheduledSubmissionMetadata>;
|
||||
scheduledTotal: number;
|
||||
scheduledHasMore: boolean;
|
||||
scheduledNextPosition: number;
|
||||
isLoadingScheduled: boolean;
|
||||
isScheduledView: boolean;
|
||||
pendingUndoSend: PendingUndoSend | null;
|
||||
@@ -456,6 +457,7 @@ export const useEmailStore = create<EmailStore>((set, get) => ({
|
||||
scheduledSubmissionByEmailId: new Map(),
|
||||
scheduledTotal: 0,
|
||||
scheduledHasMore: false,
|
||||
scheduledNextPosition: 0,
|
||||
isLoadingScheduled: false,
|
||||
isScheduledView: false,
|
||||
pendingUndoSend: null,
|
||||
@@ -2530,6 +2532,7 @@ export const useEmailStore = create<EmailStore>((set, get) => ({
|
||||
scheduledSubmissionByEmailId,
|
||||
scheduledTotal: result.total,
|
||||
scheduledHasMore: result.hasMore,
|
||||
scheduledNextPosition: result.nextPosition,
|
||||
isLoadingScheduled: false,
|
||||
pendingUndoSend: shouldClearPendingUndoSend(pendingUndoSend, result.emails) ? null : pendingUndoSend,
|
||||
});
|
||||
@@ -2542,18 +2545,19 @@ export const useEmailStore = create<EmailStore>((set, get) => ({
|
||||
scheduledSubmissionByEmailId: new Map(),
|
||||
scheduledTotal: 0,
|
||||
scheduledHasMore: false,
|
||||
scheduledNextPosition: 0,
|
||||
isLoadingScheduled: false,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
loadMoreScheduledEmails: async (client) => {
|
||||
const { isLoadingScheduled, scheduledHasMore, scheduledEmails } = get();
|
||||
const { isLoadingScheduled, scheduledHasMore, scheduledEmails, scheduledNextPosition } = get();
|
||||
if (isLoadingScheduled || !scheduledHasMore) return;
|
||||
set({ isLoadingScheduled: true, error: null });
|
||||
try {
|
||||
const emailsPerPage = useSettingsStore.getState().emailsPerPage;
|
||||
const result = await client.getScheduledEmails(emailsPerPage, scheduledEmails.length);
|
||||
const result = await client.getScheduledEmails(emailsPerPage, scheduledNextPosition);
|
||||
const merged = [...scheduledEmails, ...result.emails.filter(email => !scheduledEmails.some(existing => existing.id === email.id))];
|
||||
const pendingUndoSend = get().pendingUndoSend;
|
||||
set({
|
||||
@@ -2567,6 +2571,7 @@ export const useEmailStore = create<EmailStore>((set, get) => ({
|
||||
}])),
|
||||
scheduledTotal: result.total,
|
||||
scheduledHasMore: result.hasMore,
|
||||
scheduledNextPosition: result.nextPosition,
|
||||
isLoadingScheduled: false,
|
||||
pendingUndoSend: shouldClearPendingUndoSend(pendingUndoSend, merged) ? null : pendingUndoSend,
|
||||
});
|
||||
@@ -2577,20 +2582,32 @@ export const useEmailStore = create<EmailStore>((set, get) => ({
|
||||
|
||||
refreshScheduledMetadata: async (client) => {
|
||||
try {
|
||||
const result = await client.getScheduledEmails(useSettingsStore.getState().emailsPerPage, 0);
|
||||
const emailsPerPage = useSettingsStore.getState().emailsPerPage;
|
||||
const allEmails: ScheduledEmail[] = [];
|
||||
let position = 0;
|
||||
let hasMore = true;
|
||||
let total = 0;
|
||||
while (hasMore) {
|
||||
const page = await client.getScheduledEmails(emailsPerPage, position);
|
||||
allEmails.push(...page.emails.filter(email => !allEmails.some(existing => existing.id === email.id)));
|
||||
total = page.total;
|
||||
hasMore = page.hasMore && page.nextPosition > position;
|
||||
position = page.nextPosition;
|
||||
}
|
||||
const pendingUndoSend = get().pendingUndoSend;
|
||||
set({
|
||||
scheduledEmails: get().isScheduledView ? result.emails : get().scheduledEmails,
|
||||
scheduledEmailIds: new Set(result.emails.map(email => email.id)),
|
||||
scheduledSubmissionByEmailId: new Map(result.emails.map(email => [email.id, {
|
||||
scheduledEmails: get().isScheduledView ? allEmails : get().scheduledEmails,
|
||||
scheduledEmailIds: new Set(allEmails.map(email => email.id)),
|
||||
scheduledSubmissionByEmailId: new Map(allEmails.map(email => [email.id, {
|
||||
submissionId: email.emailSubmissionId,
|
||||
sendAt: email.scheduledSendAt,
|
||||
identityId: email.scheduledIdentityId,
|
||||
undoStatus: email.scheduledUndoStatus,
|
||||
}])),
|
||||
scheduledTotal: result.total,
|
||||
scheduledHasMore: result.hasMore,
|
||||
pendingUndoSend: shouldClearPendingUndoSend(pendingUndoSend, result.emails) ? null : pendingUndoSend,
|
||||
scheduledTotal: total,
|
||||
scheduledHasMore: false,
|
||||
scheduledNextPosition: position,
|
||||
pendingUndoSend: shouldClearPendingUndoSend(pendingUndoSend, allEmails) ? null : pendingUndoSend,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to refresh scheduled metadata:', error);
|
||||
@@ -2616,6 +2633,9 @@ export const useEmailStore = create<EmailStore>((set, get) => ({
|
||||
const submissionId = email.emailSubmissionId;
|
||||
if (!submissionId) return null;
|
||||
await client.cancelEmailSubmission(submissionId);
|
||||
if (get().pendingUndoSend?.submissionId === submissionId) {
|
||||
set({ pendingUndoSend: null });
|
||||
}
|
||||
if (email.isSmimeScheduled) {
|
||||
await client.deleteEmail(email.id);
|
||||
set(state => ({
|
||||
|
||||
Reference in New Issue
Block a user