mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 00:12:39 +00:00
A reopened draft is saved over and removed by id through the login and account it is stamped with, and message ids repeat across servers. Two ways of opening a draft stamped it with a login that was only on screen, not the one it was read through: - A Pro email tab opened by double-click named no login. It read the message through whatever login the main list was browsing and stamped the active one: with the 0.16.23 login active and the 1.0 login's Drafts browsed, the first autosave destroyed the draft's id in the 0.16.23 account and left the draft itself behind. A Pro view now stamps only a login its host names, and reads the message through that login's client. A double-click on a row of the mail list or of a folder tab names the row's login and account, and the search preview and a folder tab's reading pane name theirs for the active login too. Without a login the composer goes by the draft's From, as it did before. - "Cancel and edit" in Scheduled took the login browsed before, which stays set while Scheduled lists the active login's mail. The draft was restored through the active login and then saved over and discarded through the other one. It now takes the active login, and a send undone or cancelled for editing never falls back to what is on screen. Also: - A draft version saved through another login is removed only once the message is out. A host without a connection sent nothing and the version was removed anyway; it now says so and the draft stays. - An email tab opened for one login's message no longer focuses the tab of another login's message with the same id and account.
97 lines
4.0 KiB
TypeScript
97 lines
4.0 KiB
TypeScript
import type { Email, Identity, Mailbox } from '@/lib/jmap/types';
|
|
import { findDraftIdentityId } from '@/lib/reply-identity';
|
|
import { stripCrossAccountIdentityPrefix } from '@/hooks/use-pro-multi-account-identities';
|
|
|
|
/**
|
|
* Where a draft opened for editing lives: the connected account (login) that
|
|
* reaches it, and the JMAP account there when it is not that login's own. The
|
|
* composer removes the old version there when it saves, sends or discards the
|
|
* draft. Email and account ids repeat across accounts and servers, so any
|
|
* other login or account may hold an unrelated message under the same id.
|
|
*/
|
|
export interface DraftSource {
|
|
localAccountId?: string;
|
|
accountId?: string;
|
|
}
|
|
|
|
/**
|
|
* The DraftSource of `draft`, a message of the list on screen:
|
|
* - `source` as it is when the caller knows it: a send undone or a scheduled
|
|
* send cancelled for editing is back in the account it was sent from, put
|
|
* there through the login `source` names (none: the composing one). What is
|
|
* on screen says nothing about it: Scheduled lists the active login's mail
|
|
* while a login browsed before still counts as viewed;
|
|
* - in a list that mixes accounts (the unified views, a tag view, an "All
|
|
* folders" search) the login and account the message is stamped with. The
|
|
* folder still selected underneath a tag view or a search says nothing
|
|
* about it;
|
|
* - otherwise the list's: the viewed login (Pro multi-account sidebar) or the
|
|
* active one, and for a shared or group folder the folder's account.
|
|
*/
|
|
export function resolveDraftSource(
|
|
draft: Pick<Email, 'sourceClientAccountId' | 'sourceAccountId'>,
|
|
context: {
|
|
source?: DraftSource;
|
|
aggregate: boolean;
|
|
mailbox?: Pick<Mailbox, 'isShared' | 'accountId'>;
|
|
viewingAccountId?: string | null;
|
|
activeAccountId?: string | null;
|
|
},
|
|
): DraftSource {
|
|
if (context.source) {
|
|
return { localAccountId: context.source.localAccountId, accountId: context.source.accountId };
|
|
}
|
|
const mailbox = context.aggregate ? undefined : context.mailbox;
|
|
return {
|
|
localAccountId: draft.sourceClientAccountId
|
|
?? context.viewingAccountId
|
|
?? context.activeAccountId
|
|
?? undefined,
|
|
accountId: draft.sourceAccountId
|
|
?? (mailbox?.isShared ? mailbox.accountId : undefined),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Where a row of the mail list was listed from, for a view that reads it
|
|
* again on its own (a Pro email tab): the login and account the row is
|
|
* stamped with in a list that mixes accounts, else the login browsed - the
|
|
* active one when no other is - and `list.accountId`, the account the list
|
|
* shows (a shared or group folder's). Nothing for a row of Scheduled: that
|
|
* list is the active login's, whatever login was browsed before.
|
|
*/
|
|
export function listRowSource(
|
|
row: Pick<Email, 'sourceClientAccountId' | 'sourceAccountId'>,
|
|
list: {
|
|
isScheduledView: boolean;
|
|
viewingAccountId?: string | null;
|
|
activeAccountId?: string | null;
|
|
accountId?: string;
|
|
},
|
|
): DraftSource {
|
|
if (row.sourceClientAccountId && row.sourceAccountId) {
|
|
return { localAccountId: row.sourceClientAccountId, accountId: row.sourceAccountId };
|
|
}
|
|
if (list.isScheduledView) return {};
|
|
return { localAccountId: list.viewingAccountId ?? list.activeAccountId ?? undefined, accountId: list.accountId };
|
|
}
|
|
|
|
/**
|
|
* The identity to reopen a draft with (findDraftIdentityId), taken from the
|
|
* identities of the login the draft lives in first: two logins can share an
|
|
* address - the same user on two servers - and the other one's identity
|
|
* would move the draft to its server on the first save.
|
|
*/
|
|
export function findDraftIdentityIdForLogin(
|
|
identities: Identity[],
|
|
from: { email?: string | null; name?: string | null } | null | undefined,
|
|
localAccountId: string | undefined,
|
|
): string | null {
|
|
if (localAccountId) {
|
|
const ofLogin = identities.filter((identity) => stripCrossAccountIdentityPrefix(identity.id).localAccountId === localAccountId);
|
|
const match = findDraftIdentityId(ofLogin, from);
|
|
if (match) return match;
|
|
}
|
|
return findDraftIdentityId(identities, from);
|
|
}
|