From c75bd776d689349106259224f7365f5512b441c8 Mon Sep 17 00:00:00 2001 From: Adron Hall Date: Wed, 16 Sep 2026 14:07:41 -0700 Subject: [PATCH] feat(blog): reach the blog from the app via a themed Custom Tab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The blog at /blog is server-rendered and the API exposes no listing or post endpoint, so there is nothing to render in-app and scraping HTML is not an option. Reaching it is therefore a routing decision, not a data-layer one — no DTOs, no Room, no repository. - The Account hub's menu gains a Blog row that opens the blog in a Custom Tab coloured from the live MaterialTheme colour scheme, which InterlinedListTheme has already resolved from the user's System/Light/Dark setting, so the tab follows that setting with no extra plumbing. The row carries an "opens outside the app" affordance instead of the forward arrow the in-app rows use; AccountMenuRow gained an optional trailing icon for that, defaulted to the existing arrow so every other row is unchanged. onOpenBlog is defaulted on both ProfileRoute and ProfileScreen, so existing call sites and tests compile untouched. - Falls back to a plain ACTION_VIEW intent when no Custom Tabs provider is installed, and to a no-op (not a crash) when nothing can open a web URL. - Deep links: only the app's own interlinedlist://blog[/] scheme is claimed. The https://interlinedlist.com/blog URLs are deliberately NOT claimed — with no in-app renderer, intercepting a browser-bound link only to hand it straight back to a browser is worse than letting the browser have it, and the no-Custom-Tabs fallback would resolve back into this activity. The parser still normalises the web form so the claim can be switched on later. - androidx.browser added through gradle/libs.versions.toml. Tests: 20 new JVM unit tests covering the entry point URL, deep-link parsing (including the /blogroll prefix trap, look-alike hosts, traversal segments and garbage input), and all three launch outcomes; plus a case in the existing Account hub Compose test asserting the Blog row sits in the menu and routes out. Closes #88 --- app/build.gradle.kts | 4 + app/src/main/AndroidManifest.xml | 19 +++ .../interlinedlist/android/MainActivity.kt | 26 ++++ .../android/blog/BlogLauncher.kt | 91 ++++++++++++++ .../interlinedlist/android/blog/BlogLink.kt | 100 +++++++++++++++ .../navigation/InterlinedListNavHost.kt | 10 ++ .../android/blog/BlogLauncherTest.kt | 77 ++++++++++++ .../android/blog/BlogLinkTest.kt | 115 ++++++++++++++++++ .../feature/profile/ui/ProfileScreenTest.kt | 16 +++ .../profile/ui/profile/ProfileScreen.kt | 33 ++++- gradle/libs.versions.toml | 3 + 11 files changed, 491 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/com/interlinedlist/android/blog/BlogLauncher.kt create mode 100644 app/src/main/java/com/interlinedlist/android/blog/BlogLink.kt create mode 100644 app/src/test/java/com/interlinedlist/android/blog/BlogLauncherTest.kt create mode 100644 app/src/test/java/com/interlinedlist/android/blog/BlogLinkTest.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c48e04c..b97375b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -82,6 +82,9 @@ dependencies { implementation(libs.androidx.lifecycle.runtime.compose) implementation(libs.androidx.core.splashscreen) implementation(libs.kotlinx.coroutines.android) + // Custom Tabs: the blog is server-rendered with no public JSON endpoint, so it is + // read in a themed Custom Tab rather than in-app. + implementation(libs.androidx.browser) // DI implementation(libs.hilt.android) @@ -95,6 +98,7 @@ dependencies { // Test testImplementation(libs.junit) + testImplementation(libs.truth) androidTestImplementation(libs.androidx.test.ext.junit) androidTestImplementation(libs.androidx.test.espresso.core) androidTestImplementation(platform(libs.androidx.compose.bom)) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 03d93a3..0a6f3db 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -55,6 +55,25 @@ + + + + + + + +