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