fix: open the event a calendar link points at

This commit is contained in:
Linus Rath
2026-09-24 20:34:35 +02:00
parent a8e6f6e8c4
commit 9282ee6d72
2 changed files with 43 additions and 2 deletions
+23
View File
@@ -1,6 +1,7 @@
import { describe, it, expect, vi } from 'vitest';
import { handleDeepLink, parseDeepLink, shareToDeepLink } from '../linking';
import { usePendingSettingsTab } from '../pending-settings-tab';
import { usePendingCalendarOpen } from '../pending-calendar-open';
describe('parseDeepLink', () => {
it('parses app-scheme mail links', () => {
@@ -107,6 +108,28 @@ describe('handleDeepLink', () => {
expect(navigation.navigate).not.toHaveBeenCalled();
});
it('opens the event a calendar link names, also on a shared calendar', async () => {
const navigation = nav();
expect(parseDeepLink('https://mail.example.com/calendar/event/E1?account=acc-2'))
.toEqual({ kind: 'calendar', eventId: 'E1', jmapAccountId: 'acc-2' });
await handleDeepLink(
{ kind: 'calendar', eventId: 'E1', jmapAccountId: 'acc-2' },
{ navigation: navigation as never, resolveThreadId: async () => null },
);
expect(usePendingCalendarOpen.getState().consume()).toEqual({
kind: 'event', eventId: 'acc-2:E1', serverId: 'E1', accountId: 'acc-2',
});
expect(navigation.navigate).toHaveBeenCalledWith('MainTabs', { screen: 'Calendar' });
// A date or view link just opens the tab.
await handleDeepLink(
{ kind: 'calendar', date: '2026-08-29' },
{ navigation: navigation as never, resolveThreadId: async () => null },
);
expect(usePendingCalendarOpen.getState().consume()).toBeNull();
});
it('parks the settings tab and opens the Settings tab', async () => {
const navigation = nav();
await handleDeepLink(
+20 -2
View File
@@ -7,6 +7,7 @@ import type { EmailAddress } from '../api/types';
import { parseMailtoUrl } from '../lib/mailto';
import type { RootStackParamList } from './types';
import { setPendingSettingsTab } from './pending-settings-tab';
import { setPendingCalendarOpen } from './pending-calendar-open';
export const APP_SCHEME = 'bulwarkmobile';
@@ -14,7 +15,9 @@ export type DeepLink =
| { kind: 'message'; emailId: string; accountId?: string }
| { kind: 'thread'; threadId: string; accountId?: string }
| { kind: 'folder'; ref: string; accountId?: string }
| { kind: 'calendar'; eventId?: string; date?: string }
// `jmapAccountId`: the JMAP account owning the event (`?account=`), for
// one on a calendar shared with the user.
| { kind: 'calendar'; eventId?: string; date?: string; jmapAccountId?: string }
| { kind: 'contact'; contactId: string }
| { kind: 'contacts' }
| { kind: 'files' }
@@ -88,7 +91,12 @@ export function parseDeepLink(url: string): DeepLink | null {
return { kind: 'folder', ref: 'inbox', accountId };
}
case 'calendar': {
if (kind === 'event' && value) return { kind: 'calendar', eventId: decodeSegment(value) };
if (kind === 'event' && value) {
const jmapAccountId = search.get('account') ?? undefined;
return jmapAccountId
? { kind: 'calendar', eventId: decodeSegment(value), jmapAccountId }
: { kind: 'calendar', eventId: decodeSegment(value) };
}
const date = [kind, value].find((s) => s && /^\d{4}-\d{2}-\d{2}$/.test(s));
return { kind: 'calendar', date };
}
@@ -154,6 +162,16 @@ export async function handleDeepLink(link: DeepLink, nav: DeepLinkNavigator): Pr
navigation.navigate('MainTabs', { screen: 'Mail' } as never);
return true;
case 'calendar':
// An event link opens the event like a tapped reminder does: the
// Calendar tab looks it up by its server id.
if (link.eventId) {
setPendingCalendarOpen({
kind: 'event',
eventId: link.jmapAccountId ? `${link.jmapAccountId}:${link.eventId}` : link.eventId,
serverId: link.eventId,
accountId: link.jmapAccountId,
});
}
navigation.navigate('MainTabs', { screen: 'Calendar' } as never);
return true;
case 'contact':