ReviewOS

also looking at this

craft-native/craft

fix(mobile): preserve native editing, navigation, and durable media requests

#369
Merged chris wants to merge codex/native-vertical-scroll-tab-swipes into main
36 files +864 -185

Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.

packages/android/src/index.test.tsmodified+2-2
Changes to packages/android/src/index.test.ts
@@ -1482,9 +1482,9 @@ describe('Craft Android builder', () => {
14821482 const shell = readFileSync(join(output, 'app/src/main/java/dev/craft/media/CraftNativeShell.kt'), 'utf8')
14831483 const gradle = readFileSync(join(output, 'app/build.gradle.kts'), 'utf8')
14841484 expect(gradle).toContain('androidx.work:work-runtime-ktx')
1485 // A job per address and connection rule, joined rather than started twice, run when its network is there.
1485 // Serialize each address's requests under its connection rule; cached successors do not fetch again.
14861486 expect(shell).toContain('internal class CraftNativeMediaWorker(')
1487 expect(shell).toContain('work.enqueueUniqueWork(workName(address, wifiOnly), androidx.work.ExistingWorkPolicy.KEEP, request)')
1487 expect(shell).toContain('work.enqueueUniqueWork(workName(address, wifiOnly), androidx.work.ExistingWorkPolicy.APPEND_OR_REPLACE, request)')
14881488 expect(shell).toContain('if (wifiOnly) androidx.work.NetworkType.UNMETERED else androidx.work.NetworkType.CONNECTED')
14891489 // Resumed from the part file an earlier attempt left.
14901490 expect(shell).toContain('connection.setRequestProperty("Range", "bytes=$have-")')
packages/android/templates/CraftNativeShell.kt.templatemodified+15-5
Changes to packages/android/templates/CraftNativeShell.kt.template
@@ -1272,8 +1272,10 @@ internal object CraftNativeMedia {
12721272 .addTag(TAG)
12731273 .addTag("$TAG:${digest(address)}")
12741274 .build()
1275 // KEEP: a transfer already queued or running for this address is joined, not started again.
1276 work.enqueueUniqueWork(workName(address, wifiOnly), androidx.work.ExistingWorkPolicy.KEEP, request)
1275 // Serialize requests, including one arriving after a terminal answer but
1276 // before WorkManager marks that attempt complete. Each successor checks
1277 // the cache before fetching, so concurrent callers still share one copy.
1278 work.enqueueUniqueWork(workName(address, wifiOnly), androidx.work.ExistingWorkPolicy.APPEND_OR_REPLACE, request)
12771279 val reason = when {
12781280 !online(app) -> "offline"
12791281 wifiOnly && metered(app) -> "cellular"
@@ -1370,7 +1372,7 @@ internal object CraftNativeMedia {
13701372 capabilities?.hasCapability(android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET) == true
13711373 }.getOrDefault(true)
13721374
1373 /** Removes every copy whose address is not in `keep`, and stops their transfers; answers how many files went. */
1375 /** Removes idle copies outside `keep` and cancels their jobs; pending keeps retain ownership. */
13741376 fun prune(context: Context, keep: List<String>): Int {
13751377 val wanted = keep.map { digest(it) }.toSet()
13761378 var removed = 0
@@ -1389,8 +1391,16 @@ internal object CraftNativeMedia {
13891391 runCatching {
13901392 androidx.work.WorkManager.getInstance(app).getWorkInfosByTag(TAG).get()
13911393 .filter { !it.state.isFinished }
1392 .filter { info -> info.tags.none { it.startsWith("$TAG:") && it.removePrefix("$TAG:") in wanted } }
1393 .forEach { androidx.work.WorkManager.getInstance(app).cancelWorkById(it.id) }
1394 .forEach { info ->
1395 synchronized(lock) {
1396 // The query runs asynchronously: a new keep may now
1397 // own an address that was idle when prune was called.
1398 val active = waiting.keys.map { digest(it) }.toSet()
1399 if (info.tags.none { it.startsWith("$TAG:") && it.removePrefix("$TAG:") in wanted + active }) {
1400 androidx.work.WorkManager.getInstance(app).cancelWorkById(info.id)
1401 }
1402 }
1403 }
13941404 }
13951405 }
13961406 return removed