Skip to content

Commit e08c416

Browse files
update ui deep link handler
1 parent e92d8b6 commit e08c416

2 files changed

Lines changed: 14 additions & 108 deletions

File tree

src/pages/client/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ export enum TauriEventKey {
113113
DEAD_CONNECTION_DROPPED = 'dead-connection-dropped',
114114
DEAD_CONNECTION_RECONNECTED = 'dead-connection-reconnected',
115115
APPLICATION_CONFIG_CHANGED = 'application-config-changed',
116+
ADD_INSTANCE = 'add-instance',
116117
MFA_TRIGGER = 'mfa-trigger',
117118
VERSION_MISMATCH = 'version-mismatch',
118119
UUID_MISMATCH = 'uuid-mismatch',
Lines changed: 13 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,125 +1,30 @@
1-
import { getCurrent, onOpenUrl } from '@tauri-apps/plugin-deep-link';
1+
import { listen } from '@tauri-apps/api/event';
22
import { error } from '@tauri-apps/plugin-log';
3-
import { type PropsWithChildren, useCallback, useEffect, useRef } from 'react';
4-
import z, { string } from 'zod';
3+
import { type PropsWithChildren, useEffect } from 'react';
4+
import { type AddInstancePayload, TauriEventKey } from '../../../pages/client/types';
55
import useAddInstance from '../../hooks/useAddInstance';
66
import { errorDetail } from '../../utils/errorDetail';
77

8-
enum DeepLink {
9-
AddInstance = 'addinstance',
10-
}
11-
128
export const linkStorageKey = 'lastSuccessfullyHandledDeepLink';
139

1410
export const storeLink = (value: string) => {
1511
sessionStorage.setItem(linkStorageKey, value);
1612
};
1713

18-
const readStoreLink = (): string | null => {
19-
return sessionStorage.getItem(linkStorageKey);
20-
};
21-
22-
const addInstanceLinkSchema = z.object({
23-
token: string().trim().min(1),
24-
url: string().trim().min(1).url(),
25-
});
26-
27-
const AddInstanceLink = z.object({
28-
link: z.literal(DeepLink.AddInstance),
29-
data: addInstanceLinkSchema,
30-
});
31-
32-
const validLinkPayload = z.discriminatedUnion('link', [AddInstanceLink]);
33-
34-
type LinkPayload = z.infer<typeof validLinkPayload>;
35-
36-
const linkIntoPayload = (link: URL | null): LinkPayload | null => {
37-
if (link == null) return null;
38-
39-
const searchData = Object.fromEntries(new URLSearchParams(link.search));
40-
const linkKey = [link.hostname, link.pathname]
41-
.map((l) => l.trim().replaceAll('/', ''))
42-
.filter((l) => l !== '')[0] as string;
43-
const payload = {
44-
link: linkKey,
45-
data: searchData,
46-
};
47-
const result = validLinkPayload.safeParse(payload);
48-
if (result.success) {
49-
return result.data;
50-
} else {
51-
error(`Link ${link} was rejected due to schema validation.`);
52-
}
53-
return null;
54-
};
55-
5614
export const DeepLinkProvider = ({ children }: PropsWithChildren) => {
57-
const mounted = useRef(false);
58-
5915
const { handleAddInstance } = useAddInstance();
6016

61-
const handleValidLink = useCallback(
62-
async (payload: LinkPayload, rawLink?: string) => {
63-
const { data, link } = payload;
64-
switch (link) {
65-
case DeepLink.AddInstance:
66-
await handleAddInstance(data, rawLink);
67-
break;
68-
}
69-
if (rawLink) {
70-
storeLink(rawLink);
71-
}
72-
},
73-
[handleAddInstance],
74-
);
75-
76-
// biome-ignore lint/correctness/useExhaustiveDependencies: only on mount
7717
useEffect(() => {
78-
if (!mounted.current) {
79-
mounted.current = true;
80-
81-
let unlisten: (() => void) | undefined;
82-
(async () => {
83-
const start = await getCurrent();
84-
if (start != null) {
85-
const lastLink = readStoreLink();
86-
// if the link is exact as last successfully executed link
87-
// this is only necessary bcs in dev mode window is hot reloaded causing the startup link to be handled multiple times over.
88-
if (lastLink != null && lastLink === start[0]) {
89-
return;
90-
}
91-
const payload = linkIntoPayload(new URL(start[0]));
92-
if (payload != null) {
93-
try {
94-
handleValidLink(payload, start[0]);
95-
} catch (e) {
96-
const detail = errorDetail(e);
97-
error(`Failed to handle startup deep link "${payload.link}": ${detail}`);
98-
}
99-
}
100-
}
101-
unlisten = await onOpenUrl((urls) => {
102-
if (urls?.length) {
103-
const link = urls[0];
104-
const payload = linkIntoPayload(new URL(link));
105-
if (payload != null) {
106-
try {
107-
handleValidLink(payload);
108-
} catch (e) {
109-
const detail = errorDetail(e);
110-
error(
111-
`Failed to handle valid deep link "${payload?.link}" action: ${detail}`,
112-
);
113-
}
114-
}
115-
}
116-
});
117-
})();
118-
return () => {
119-
unlisten?.();
120-
};
121-
}
122-
}, []);
18+
const unlisten = listen<AddInstancePayload>(TauriEventKey.ADD_INSTANCE, (event) => {
19+
handleAddInstance(event.payload).catch((e) => {
20+
error(`Failed to handle add-instance event: ${errorDetail(e)}`);
21+
});
22+
});
23+
24+
return () => {
25+
unlisten.then((fn) => fn());
26+
};
27+
}, [handleAddInstance]);
12328

12429
return <>{children}</>;
12530
};

0 commit comments

Comments
 (0)