Compare commits

...
2 Commits
Author SHA1 Message Date
MythEclipse b4bd114eed fix(tauri): configure deep-link plugin for mobile custom scheme 2026-06-16 05:14:08 +07:00
MythEclipseandClaude 7423e4d83b fix(tauri): use raw __TAURI_INTERNALS__ invoke, fix dynamic import on Android
Root cause: dynamic import('@tauri-apps/plugin-opener') silently fails on
Android Tauri WebView because the module resolution path for @tauri-apps/api
(plugin dependency) differs from npm expectations in the bundled context.

Rewrote tauri.ts to use window.__TAURI_INTERNALS__.invoke() directly:
- openUrl() → invoke('plugin:opener|open_url', {url})
- setupDeepLinkHandler() → invoke('plugin:deep-link|get_current')
- Warm-start listener still uses import('@tauri-apps/api/event') for
  deep-link://new-url events (bundled as separate chunk by Vite)
- Added @tauri-apps/api as direct dependency

Also kept withGlobalTauri: true (needed for __TAURI_INTERNALS__ injection)
but reverted APK frontendDist back to bundled React app (../web/dist)
since redirect-to-live-web approach was unreliable.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-16 04:34:13 +07:00
4 changed files with 52 additions and 35 deletions
+12 -4
View File
@@ -4,10 +4,10 @@
"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": true, "withGlobalTauri": true,
@@ -26,5 +26,13 @@
"active": true, "active": true,
"targets": "all" "targets": "all"
}, },
"plugins": {} "plugins": {
"deep-link": {
"mobile": [
{
"scheme": ["zeavisedu"]
}
]
}
}
} }
+38 -31
View File
@@ -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);
}
} }
+1
View File
@@ -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",
}, },
+1
View File
@@ -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"
} }