ReviewOS

also looking at this

craft-native/craft

fix(android): hand the launch link to the page that subscribes for it

#232
Merged glennmichael123 wants to merge fix/android-deep-link-replay into main
17 files +776 -136
packages/android/src/index.test.tsmodified+32-0
Changes to packages/android/src/index.test.ts
@@ -327,6 +327,38 @@ describe('Craft Android builder', () => {
327327 expect(bridge).toContain('craft.ar.onPlaneDetected is unavailable because ARCore requires native Activity integration')
328328 })
329329
330 it('queues deep links until the bridge is injected, and marks only the launch link initial', async () => {
331 // #215: the launch link used to go only to getInitialURL, and a warm link
332 // replaced the launch link as getInitialURL's answer.
333 const output = mkdtempSync(join(tmpdir(), 'craft-android-deep-links-'))
334 await init({ name: 'WildLoop', packageName: 'org.wildloop.app', output })
335
336 const sourceRoot = join(output, 'app/src/main/java/org/wildloop/app')
337 const bridge = readFileSync(join(sourceRoot, 'CraftBridge.kt'), 'utf8')
338 const activity = readFileSync(join(sourceRoot, 'MainActivity.kt'), 'utf8')
339 const holder = readFileSync(join(output, 'app/src/main/java/com/craft/runtime/CraftNative.kt'), 'utf8')
340
341 expect(activity).not.toContain('dispatch = false')
342 expect(activity).toContain('craftBridge.receiveDeepLink(launchLink)')
343 expect(activity).toContain('Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY')
344 expect(activity).toContain('intent.data?.toString()?.let(craftBridge::receiveDeepLink)')
345 // A recreated activity restores what the bridge saved, not getIntent(),
346 // which onNewIntent may have replaced with a warm link.
347 expect(activity).toContain('craftBridge.saveDeepLinks(outState)')
348 expect(activity).toContain('craftBridge.restoreDeepLinks(savedInstanceState)')
349
350 // Flushed in the injection's completion, after the page's replay exists.
351 const injected = bridge.slice(bridge.indexOf('webView.evaluateJavascript(script) {'))
352 const flush = injected.slice(0, injected.indexOf('fun markBridgeLoading()'))
353 expect(flush.indexOf('bridgeReady = true')).toBeLessThan(flush.indexOf('links.forEach { (url, initial) -> dispatchDeepLink(url, initial) }'))
354
355 expect(bridge).toContain('val initial = !hasBeenReady && !initialAssigned')
356 expect(bridge).toContain('put("initial", initial)')
357 expect(bridge).not.toContain('if (initialURL == null)')
358 expect(bridge).toContain('pendingDeepLinks.clear()\n }')
359 expect(holder).toContain('private external fun nativeDispatchDeepLink(url: String, initial: Boolean): Boolean')
360 })
361
330362 it('routes external Activity results back to every pending media promise', async () => {
331363 const output = mkdtempSync(join(tmpdir(), 'craft-android-activity-results-'))
332364 await init({ name: 'WildLoop', packageName: 'org.wildloop.app', output })