Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4bd114eed | ||
|
|
7423e4d83b | ||
|
|
4b252a1d5d |
@@ -4,13 +4,13 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"identifier": "com.zeavis.edu",
|
"identifier": "com.zeavis.edu",
|
||||||
"build": {
|
"build": {
|
||||||
"frontendDist": "../web/dist-tauri",
|
"frontendDist": "../web/dist",
|
||||||
"devUrl": "http://localhost:5173",
|
"devUrl": "http://localhost:5173",
|
||||||
"beforeBuildCommand": "echo 'Nothing to build (APK loads live web content)'",
|
"beforeBuildCommand": "cd ../web && bun run build",
|
||||||
"beforeDevCommand": "echo 'Dev mode - connect to localhost:5173'"
|
"beforeDevCommand": "cd ../web && bun run dev"
|
||||||
},
|
},
|
||||||
"app": {
|
"app": {
|
||||||
"withGlobalTauri": false,
|
"withGlobalTauri": true,
|
||||||
"windows": [
|
"windows": [
|
||||||
{
|
{
|
||||||
"title": "ZeaVis Edu",
|
"title": "ZeaVis Edu",
|
||||||
@@ -26,5 +26,13 @@
|
|||||||
"active": true,
|
"active": true,
|
||||||
"targets": "all"
|
"targets": "all"
|
||||||
},
|
},
|
||||||
"plugins": {}
|
"plugins": {
|
||||||
|
"deep-link": {
|
||||||
|
"mobile": [
|
||||||
|
{
|
||||||
|
"scheme": ["zeavisedu"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-31
@@ -1,7 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Lightweight Tauri environment detection and utilities.
|
* Lightweight Tauri environment detection and utilities.
|
||||||
* Avoids importing @tauri-apps/api at module level so the web build
|
* Uses raw __TAURI_INTERNALS__ IPC to avoid bundling/import issues on Android.
|
||||||
* doesn't bundle Tauri internals.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let _isTauri: boolean | null = null;
|
let _isTauri: boolean | null = null;
|
||||||
@@ -14,24 +13,30 @@ export function isTauri(): boolean {
|
|||||||
return _isTauri;
|
return _isTauri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Get the Tauri IPC invoke function directly from the global internals. */
|
||||||
|
function tauriInvoke(): (cmd: string, args?: Record<string, unknown>) => Promise<unknown> {
|
||||||
|
const T = (window as any).__TAURI_INTERNALS__;
|
||||||
|
if (!T?.invoke) throw new Error('Tauri IPC not available');
|
||||||
|
return T.invoke.bind(T);
|
||||||
|
}
|
||||||
|
|
||||||
export async function openUrl(url: string): Promise<void> {
|
export async function openUrl(url: string): Promise<void> {
|
||||||
if (!isTauri()) {
|
if (!isTauri()) {
|
||||||
|
// Not in Tauri — normal browser navigation
|
||||||
window.location.href = url;
|
window.location.href = url;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { openUrl: tauriOpenUrl } = await import('@tauri-apps/plugin-opener');
|
try {
|
||||||
await tauriOpenUrl(url);
|
const invoke = tauriInvoke();
|
||||||
|
await invoke('plugin:opener|open_url', { url });
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Tauri openUrl failed, trying fallback:', err);
|
||||||
|
// Fallback: navigate the WebView (Google will block, but best effort)
|
||||||
|
window.location.href = url;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Listen for deep link URLs when the app is opened from an external link.
|
|
||||||
* On Android, after Google OAuth completes in the system browser, the
|
|
||||||
* callback page redirects to zeavisedu://... which triggers this listener.
|
|
||||||
* We extract the path + query and navigate the WebView there.
|
|
||||||
*/
|
|
||||||
function processDeepLinkUrl(url: string): void {
|
function processDeepLinkUrl(url: string): void {
|
||||||
// url looks like: zeavisedu://login/login?token=xxx
|
|
||||||
// We need to extract path+query and navigate the WebView there
|
|
||||||
try {
|
try {
|
||||||
const u = new URL(url);
|
const u = new URL(url);
|
||||||
const target = u.pathname + u.search + u.hash;
|
const target = u.pathname + u.search + u.hash;
|
||||||
@@ -39,32 +44,34 @@ function processDeepLinkUrl(url: string): void {
|
|||||||
window.location.href = target;
|
window.location.href = target;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch { /* try fallback below */ }
|
} catch { /* fall through */ }
|
||||||
|
|
||||||
// Fallback: handle both double-slash (://) and single-slash (:/) schemes
|
// Fallback: handle both :// and :/ custom schemes
|
||||||
let match = url.match(/^[^:]+:\/\/(?:[^/]+)?(\/.*)?$/);
|
let match = url.match(/^[^:]+:\/\/(?:[^/]+)?(\/.*)?$/);
|
||||||
if (!match) {
|
if (!match) match = url.match(/^[^:]+:\/(\/.*)?$/);
|
||||||
match = url.match(/^[^:]+:\/(\/.*)?$/);
|
if (match?.[1]) window.location.href = match[1];
|
||||||
}
|
|
||||||
if (match?.[1]) {
|
|
||||||
window.location.href = match[1];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setupDeepLinkHandler(): Promise<void> {
|
export async function setupDeepLinkHandler(): Promise<void> {
|
||||||
if (!isTauri()) return;
|
if (!isTauri()) return;
|
||||||
|
|
||||||
const { onOpenUrl, getCurrent } = await import('@tauri-apps/plugin-deep-link');
|
try {
|
||||||
|
const invoke = tauriInvoke();
|
||||||
|
|
||||||
// Handle cold-start deep links (app opened via intent)
|
// Cold-start: app just opened via intent:// or custom scheme
|
||||||
getCurrent().then((urls) => {
|
invoke('plugin:deep-link|get_current')
|
||||||
if (urls?.[0]) processDeepLinkUrl(urls[0]);
|
.then((urls: any) => {
|
||||||
}).catch(() => { /* ignore */ });
|
if (urls?.[0]) processDeepLinkUrl(urls[0]);
|
||||||
|
})
|
||||||
|
.catch(() => { /* plugin may not be registered yet */ });
|
||||||
|
|
||||||
// Handle warm-start deep links (app already running, new intent received)
|
// Warm-start: listen for new URLs while app is running
|
||||||
onOpenUrl((urls) => {
|
const { listen } = await import('@tauri-apps/api/event');
|
||||||
for (const url of urls) {
|
listen('deep-link://new-url', (event: any) => {
|
||||||
processDeepLinkUrl(url);
|
const urls = event.payload as string[];
|
||||||
}
|
for (const url of urls) processDeepLinkUrl(url);
|
||||||
});
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Tauri deep-link setup failed:', err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "zeavis-edu",
|
"name": "zeavis-edu",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@tauri-apps/api": "2.11.0",
|
||||||
"@tauri-apps/plugin-deep-link": "2.4.9",
|
"@tauri-apps/plugin-deep-link": "2.4.9",
|
||||||
"@tauri-apps/plugin-opener": "2.5.4",
|
"@tauri-apps/plugin-opener": "2.5.4",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
"packages/*"
|
"packages/*"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@tauri-apps/api": "2.11.0",
|
||||||
"@tauri-apps/plugin-deep-link": "2.4.9",
|
"@tauri-apps/plugin-deep-link": "2.4.9",
|
||||||
"@tauri-apps/plugin-opener": "2.5.4"
|
"@tauri-apps/plugin-opener": "2.5.4"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user