mirror of
https://github.com/bulwarkmail/native.git
synced 2026-11-03 00:06:39 +00:00
fix: open the composer for mailto links sent with SENDTO
Most apps start an email with `ACTION_SENDTO` and a `mailto:` URI. The manifest offered the app for it, but React Native's Linking only forwards VIEW intents, so the app opened on the inbox instead of the composer. MainActivity now rewrites a `SENDTO mailto:` intent into the equivalent `VIEW mailto:` link before React Native sees it, on a cold start and when the running app receives it. The EXTRA_EMAIL, EXTRA_CC, EXTRA_BCC, EXTRA_SUBJECT and EXTRA_TEXT extras are folded into the link's query. Bcc recipients of a mailto link now reach the composer too; they were dropped for VIEW links as well.
This commit is contained in:
@@ -30,12 +30,32 @@ describe('parseDeepLink', () => {
|
||||
kind: 'compose',
|
||||
to: [{ email: 'a@b.co' }],
|
||||
cc: [{ email: 'c@d.co' }],
|
||||
bcc: [],
|
||||
subject: 'Hi',
|
||||
body: undefined,
|
||||
});
|
||||
expect(parseDeepLink('mailto:nope')).toBeNull();
|
||||
});
|
||||
|
||||
it('reads the link MainActivity builds from a SENDTO mailto: intent and its extras', () => {
|
||||
// SENDTO mailto: + EXTRA_EMAIL/CC/BCC/SUBJECT/TEXT, each value Uri.encode()d.
|
||||
expect(parseDeepLink(
|
||||
'mailto:?to=bob%40partner.example&cc=carol%40partner.example&bcc=dave%40partner.example'
|
||||
+ '&subject=Hi%20there&body=Line%201%0ALine%202',
|
||||
)).toEqual({
|
||||
kind: 'compose',
|
||||
to: [{ email: 'bob@partner.example' }],
|
||||
cc: [{ email: 'carol@partner.example' }],
|
||||
bcc: [{ email: 'dave@partner.example' }],
|
||||
subject: 'Hi there',
|
||||
body: 'Line 1\nLine 2',
|
||||
});
|
||||
// An address in the URI itself plus EXTRA_EMAIL.
|
||||
expect(parseDeepLink('mailto:bob@partner.example?to=eve%40partner.example')).toMatchObject({
|
||||
to: [{ email: 'bob@partner.example' }, { email: 'eve@partner.example' }],
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects unknown links', () => {
|
||||
expect(parseDeepLink('bulwarkmobile://whatever')).toBeNull();
|
||||
expect(parseDeepLink('garbage')).toBeNull();
|
||||
@@ -94,6 +114,17 @@ describe('handleDeepLink', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('passes Bcc recipients through to the composer', async () => {
|
||||
const navigation = nav();
|
||||
await handleDeepLink(
|
||||
{ kind: 'compose', to: [{ email: 'a@b.co' }], cc: [], bcc: [{ email: 'c@d.co' }] },
|
||||
{ navigation: navigation as never, resolveThreadId: async () => null },
|
||||
);
|
||||
expect(navigation.navigate).toHaveBeenCalledWith('Compose', expect.objectContaining({
|
||||
prefillBcc: [{ email: 'c@d.co' }],
|
||||
}));
|
||||
});
|
||||
|
||||
it('refuses when the linked account is not signed in', async () => {
|
||||
const navigation = nav();
|
||||
const ok = await handleDeepLink(
|
||||
|
||||
@@ -19,7 +19,7 @@ export type DeepLink =
|
||||
| { kind: 'contacts' }
|
||||
| { kind: 'files' }
|
||||
| { kind: 'settings'; tab?: string }
|
||||
| { kind: 'compose'; to: EmailAddress[]; cc: EmailAddress[]; subject?: string; body?: string };
|
||||
| { kind: 'compose'; to: EmailAddress[]; cc: EmailAddress[]; bcc?: EmailAddress[]; subject?: string; body?: string };
|
||||
|
||||
function decodeSegment(value: string): string {
|
||||
try {
|
||||
@@ -65,6 +65,7 @@ export function parseDeepLink(url: string): DeepLink | null {
|
||||
kind: 'compose',
|
||||
to: toAddresses(parsed.to),
|
||||
cc: toAddresses(parsed.cc),
|
||||
bcc: toAddresses(parsed.bcc),
|
||||
subject: parsed.subject,
|
||||
body: parsed.body,
|
||||
};
|
||||
@@ -168,6 +169,7 @@ export async function handleDeepLink(link: DeepLink, nav: DeepLinkNavigator): Pr
|
||||
navigation.navigate('Compose', {
|
||||
prefillTo: link.to,
|
||||
prefillCc: link.cc.length > 0 ? link.cc : undefined,
|
||||
prefillBcc: link.bcc?.length ? link.bcc : undefined,
|
||||
prefillSubject: link.subject,
|
||||
prefillBody: link.body,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user