mirror of
https://github.com/bulwarkmail/native.git
synced 2026-11-06 16:07:43 +00:00
fix: open the event a calendar link points at
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { describe, it, expect, vi } from 'vitest';
|
import { describe, it, expect, vi } from 'vitest';
|
||||||
import { handleDeepLink, parseDeepLink, shareToDeepLink } from '../linking';
|
import { handleDeepLink, parseDeepLink, shareToDeepLink } from '../linking';
|
||||||
import { usePendingSettingsTab } from '../pending-settings-tab';
|
import { usePendingSettingsTab } from '../pending-settings-tab';
|
||||||
|
import { usePendingCalendarOpen } from '../pending-calendar-open';
|
||||||
|
|
||||||
describe('parseDeepLink', () => {
|
describe('parseDeepLink', () => {
|
||||||
it('parses app-scheme mail links', () => {
|
it('parses app-scheme mail links', () => {
|
||||||
@@ -107,6 +108,28 @@ describe('handleDeepLink', () => {
|
|||||||
expect(navigation.navigate).not.toHaveBeenCalled();
|
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 () => {
|
it('parks the settings tab and opens the Settings tab', async () => {
|
||||||
const navigation = nav();
|
const navigation = nav();
|
||||||
await handleDeepLink(
|
await handleDeepLink(
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import type { EmailAddress } from '../api/types';
|
|||||||
import { parseMailtoUrl } from '../lib/mailto';
|
import { parseMailtoUrl } from '../lib/mailto';
|
||||||
import type { RootStackParamList } from './types';
|
import type { RootStackParamList } from './types';
|
||||||
import { setPendingSettingsTab } from './pending-settings-tab';
|
import { setPendingSettingsTab } from './pending-settings-tab';
|
||||||
|
import { setPendingCalendarOpen } from './pending-calendar-open';
|
||||||
|
|
||||||
export const APP_SCHEME = 'bulwarkmobile';
|
export const APP_SCHEME = 'bulwarkmobile';
|
||||||
|
|
||||||
@@ -14,7 +15,9 @@ export type DeepLink =
|
|||||||
| { kind: 'message'; emailId: string; accountId?: string }
|
| { kind: 'message'; emailId: string; accountId?: string }
|
||||||
| { kind: 'thread'; threadId: string; accountId?: string }
|
| { kind: 'thread'; threadId: string; accountId?: string }
|
||||||
| { kind: 'folder'; ref: 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: 'contact'; contactId: string }
|
||||||
| { kind: 'contacts' }
|
| { kind: 'contacts' }
|
||||||
| { kind: 'files' }
|
| { kind: 'files' }
|
||||||
@@ -88,7 +91,12 @@ export function parseDeepLink(url: string): DeepLink | null {
|
|||||||
return { kind: 'folder', ref: 'inbox', accountId };
|
return { kind: 'folder', ref: 'inbox', accountId };
|
||||||
}
|
}
|
||||||
case 'calendar': {
|
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));
|
const date = [kind, value].find((s) => s && /^\d{4}-\d{2}-\d{2}$/.test(s));
|
||||||
return { kind: 'calendar', date };
|
return { kind: 'calendar', date };
|
||||||
}
|
}
|
||||||
@@ -154,6 +162,16 @@ export async function handleDeepLink(link: DeepLink, nav: DeepLinkNavigator): Pr
|
|||||||
navigation.navigate('MainTabs', { screen: 'Mail' } as never);
|
navigation.navigate('MainTabs', { screen: 'Mail' } as never);
|
||||||
return true;
|
return true;
|
||||||
case 'calendar':
|
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);
|
navigation.navigate('MainTabs', { screen: 'Calendar' } as never);
|
||||||
return true;
|
return true;
|
||||||
case 'contact':
|
case 'contact':
|
||||||
|
|||||||
Reference in New Issue
Block a user