From 4689961e7179a9ecf00db1bf6426be93170bbf7b Mon Sep 17 00:00:00 2001 From: Adron Hall Date: Sun, 2 Aug 2026 12:29:03 -0700 Subject: [PATCH 1/5] feat: Strata branding + dark mode + messages explicit-null fix Brand refresh and dark mode: IL* design-system theme (Color/Theme/Logo), updated colors.xml/themes.xml, launcher + splash + monochrome icons, values-night/drawable-night dark resources, ThemeSettingsStore in :core:datastore, and the cross-platform brand-kit assets. Also the messages explicit-null fix (NetworkModule coerceInputValues + messages DTO/repository handling). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../interlinedlist/android/MainActivity.kt | 16 +- .../res/drawable-night/il_splash_icon.xml | 13 + .../main/res/drawable-nodpi/il_logo_mark.png | Bin 14348 -> 0 bytes .../res/drawable/ic_launcher_foreground.xml | 18 +- .../res/drawable/ic_launcher_monochrome.xml | 13 + app/src/main/res/drawable/il_splash_icon.xml | 13 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 4 +- .../mipmap-anydpi-v26/ic_launcher_round.xml | 4 +- app/src/main/res/values-night/colors.xml | 5 + app/src/main/res/values/colors.xml | 6 +- app/src/main/res/values/themes.xml | 9 +- brand-kit/HANDOFF.md | 225 ++++++++++++++++++ brand-kit/README.md | 74 ++++++ brand-kit/guidelines/brand-guidelines.html | 179 ++++++++++++++ brand-kit/icons/android/ic_launcher-144.png | Bin 0 -> 5882 bytes brand-kit/icons/android/ic_launcher-192.png | Bin 0 -> 9020 bytes brand-kit/icons/android/ic_launcher-48.png | Bin 0 -> 1419 bytes brand-kit/icons/android/ic_launcher-512.png | Bin 0 -> 33743 bytes brand-kit/icons/android/ic_launcher-72.png | Bin 0 -> 2414 bytes brand-kit/icons/android/ic_launcher-96.png | Bin 0 -> 3283 bytes .../android/ic_launcher_background-512.png | Bin 0 -> 7124 bytes .../android/ic_launcher_foreground-108.png | Bin 0 -> 1918 bytes .../android/ic_launcher_foreground-192.png | Bin 0 -> 4752 bytes .../android/ic_launcher_foreground-512.png | Bin 0 -> 20703 bytes brand-kit/icons/ios/AppIcon-1024.png | Bin 0 -> 78832 bytes brand-kit/icons/ios/AppIcon-120.png | Bin 0 -> 2749 bytes brand-kit/icons/ios/AppIcon-152.png | Bin 0 -> 3806 bytes brand-kit/icons/ios/AppIcon-167.png | Bin 0 -> 4316 bytes brand-kit/icons/ios/AppIcon-180.png | Bin 0 -> 5458 bytes brand-kit/icons/ios/AppIcon-40.png | Bin 0 -> 727 bytes brand-kit/icons/ios/AppIcon-60.png | Bin 0 -> 874 bytes brand-kit/icons/ios/AppIcon-80.png | Bin 0 -> 1510 bytes brand-kit/icons/ios/AppIcon-87.png | Bin 0 -> 1722 bytes brand-kit/icons/ios/AppIcon-dark-1024.png | Bin 0 -> 82846 bytes brand-kit/icons/linux/icon-128.png | Bin 0 -> 4795 bytes brand-kit/icons/linux/icon-256.png | Bin 0 -> 12778 bytes brand-kit/icons/linux/icon-48.png | Bin 0 -> 1347 bytes brand-kit/icons/linux/icon-512.png | Bin 0 -> 34488 bytes brand-kit/icons/linux/icon-64.png | Bin 0 -> 2198 bytes brand-kit/icons/macos/InterlinedList.icns | Bin 0 -> 113440 bytes brand-kit/icons/macos/icon-1024.png | Bin 0 -> 74508 bytes brand-kit/icons/macos/icon-128.png | Bin 0 -> 3299 bytes brand-kit/icons/macos/icon-16.png | Bin 0 -> 354 bytes brand-kit/icons/macos/icon-256.png | Bin 0 -> 8938 bytes brand-kit/icons/macos/icon-32.png | Bin 0 -> 549 bytes brand-kit/icons/macos/icon-512.png | Bin 0 -> 24891 bytes brand-kit/icons/macos/icon-64.png | Bin 0 -> 1199 bytes brand-kit/icons/macos/icon-dark-1024.png | Bin 0 -> 77266 bytes brand-kit/icons/web/apple-touch-icon-180.png | Bin 0 -> 5458 bytes brand-kit/icons/web/favicon-16.png | Bin 0 -> 246 bytes brand-kit/icons/web/favicon-32.png | Bin 0 -> 464 bytes brand-kit/icons/web/favicon-48.png | Bin 0 -> 786 bytes brand-kit/icons/web/favicon-dark-32.png | Bin 0 -> 465 bytes brand-kit/icons/web/icon-192.png | Bin 0 -> 5750 bytes brand-kit/icons/web/icon-512.png | Bin 0 -> 26592 bytes brand-kit/icons/web/maskable-512.png | Bin 0 -> 20105 bytes brand-kit/icons/windows/InterlinedList.ico | Bin 0 -> 11975 bytes brand-kit/icons/windows/icon-16.png | Bin 0 -> 233 bytes brand-kit/icons/windows/icon-256.png | Bin 0 -> 9221 bytes brand-kit/icons/windows/icon-32.png | Bin 0 -> 472 bytes brand-kit/icons/windows/icon-48.png | Bin 0 -> 818 bytes brand-kit/icons/windows/icon-64.png | Bin 0 -> 1145 bytes brand-kit/logo/logo-icon-master.png | Bin 0 -> 12088 bytes brand-kit/logo/logo-icon.svg | 5 + brand-kit/theme/android-Color.kt | 30 +++ brand-kit/theme/android-colors.xml | 34 +++ brand-kit/theme/interlinedlist-theme.css | 159 +++++++++++++ brand-kit/theme/ios-ILColor.swift | 54 +++++ brand-kit/theme/tokens.json | 42 ++++ .../core/datastore/ThemeSettingsStore.kt | 50 ++++ .../core/designsystem/component/Logo.kt | 25 +- .../android/core/designsystem/theme/Color.kt | 59 +++-- .../android/core/designsystem/theme/Theme.kt | 80 +++++-- .../main/res/drawable-nodpi/il_mark_dark.png | Bin 14245 -> 0 bytes .../main/res/drawable-nodpi/il_mark_light.png | Bin 14348 -> 0 bytes .../src/main/res/drawable/il_logo_mark.xml | 10 + .../main/res/drawable/il_logo_mark_dark.xml | 10 + .../android/core/network/di/NetworkModule.kt | 4 + .../data/DefaultMessagesRepository.kt | 4 +- .../messages/data/remote/MessagesApi.kt | 6 +- .../messages/data/remote/dto/MessageDto.kt | 44 ++-- .../data/remote/dto/MessagesResponse.kt | 11 + .../data/DefaultMessagesRepositoryTest.kt | 20 +- 83 files changed, 1130 insertions(+), 96 deletions(-) create mode 100644 app/src/main/res/drawable-night/il_splash_icon.xml delete mode 100644 app/src/main/res/drawable-nodpi/il_logo_mark.png create mode 100644 app/src/main/res/drawable/ic_launcher_monochrome.xml create mode 100644 app/src/main/res/drawable/il_splash_icon.xml create mode 100644 app/src/main/res/values-night/colors.xml create mode 100644 brand-kit/HANDOFF.md create mode 100644 brand-kit/README.md create mode 100644 brand-kit/guidelines/brand-guidelines.html create mode 100644 brand-kit/icons/android/ic_launcher-144.png create mode 100644 brand-kit/icons/android/ic_launcher-192.png create mode 100644 brand-kit/icons/android/ic_launcher-48.png create mode 100644 brand-kit/icons/android/ic_launcher-512.png create mode 100644 brand-kit/icons/android/ic_launcher-72.png create mode 100644 brand-kit/icons/android/ic_launcher-96.png create mode 100644 brand-kit/icons/android/ic_launcher_background-512.png create mode 100644 brand-kit/icons/android/ic_launcher_foreground-108.png create mode 100644 brand-kit/icons/android/ic_launcher_foreground-192.png create mode 100644 brand-kit/icons/android/ic_launcher_foreground-512.png create mode 100644 brand-kit/icons/ios/AppIcon-1024.png create mode 100644 brand-kit/icons/ios/AppIcon-120.png create mode 100644 brand-kit/icons/ios/AppIcon-152.png create mode 100644 brand-kit/icons/ios/AppIcon-167.png create mode 100644 brand-kit/icons/ios/AppIcon-180.png create mode 100644 brand-kit/icons/ios/AppIcon-40.png create mode 100644 brand-kit/icons/ios/AppIcon-60.png create mode 100644 brand-kit/icons/ios/AppIcon-80.png create mode 100644 brand-kit/icons/ios/AppIcon-87.png create mode 100644 brand-kit/icons/ios/AppIcon-dark-1024.png create mode 100644 brand-kit/icons/linux/icon-128.png create mode 100644 brand-kit/icons/linux/icon-256.png create mode 100644 brand-kit/icons/linux/icon-48.png create mode 100644 brand-kit/icons/linux/icon-512.png create mode 100644 brand-kit/icons/linux/icon-64.png create mode 100644 brand-kit/icons/macos/InterlinedList.icns create mode 100644 brand-kit/icons/macos/icon-1024.png create mode 100644 brand-kit/icons/macos/icon-128.png create mode 100644 brand-kit/icons/macos/icon-16.png create mode 100644 brand-kit/icons/macos/icon-256.png create mode 100644 brand-kit/icons/macos/icon-32.png create mode 100644 brand-kit/icons/macos/icon-512.png create mode 100644 brand-kit/icons/macos/icon-64.png create mode 100644 brand-kit/icons/macos/icon-dark-1024.png create mode 100644 brand-kit/icons/web/apple-touch-icon-180.png create mode 100644 brand-kit/icons/web/favicon-16.png create mode 100644 brand-kit/icons/web/favicon-32.png create mode 100644 brand-kit/icons/web/favicon-48.png create mode 100644 brand-kit/icons/web/favicon-dark-32.png create mode 100644 brand-kit/icons/web/icon-192.png create mode 100644 brand-kit/icons/web/icon-512.png create mode 100644 brand-kit/icons/web/maskable-512.png create mode 100644 brand-kit/icons/windows/InterlinedList.ico create mode 100644 brand-kit/icons/windows/icon-16.png create mode 100644 brand-kit/icons/windows/icon-256.png create mode 100644 brand-kit/icons/windows/icon-32.png create mode 100644 brand-kit/icons/windows/icon-48.png create mode 100644 brand-kit/icons/windows/icon-64.png create mode 100644 brand-kit/logo/logo-icon-master.png create mode 100644 brand-kit/logo/logo-icon.svg create mode 100644 brand-kit/theme/android-Color.kt create mode 100644 brand-kit/theme/android-colors.xml create mode 100644 brand-kit/theme/interlinedlist-theme.css create mode 100644 brand-kit/theme/ios-ILColor.swift create mode 100644 brand-kit/theme/tokens.json create mode 100644 core/datastore/src/main/kotlin/com/interlinedlist/android/core/datastore/ThemeSettingsStore.kt delete mode 100644 core/designsystem/src/main/res/drawable-nodpi/il_mark_dark.png delete mode 100644 core/designsystem/src/main/res/drawable-nodpi/il_mark_light.png create mode 100644 core/designsystem/src/main/res/drawable/il_logo_mark.xml create mode 100644 core/designsystem/src/main/res/drawable/il_logo_mark_dark.xml diff --git a/app/src/main/java/com/interlinedlist/android/MainActivity.kt b/app/src/main/java/com/interlinedlist/android/MainActivity.kt index 633b288..5a8d172 100644 --- a/app/src/main/java/com/interlinedlist/android/MainActivity.kt +++ b/app/src/main/java/com/interlinedlist/android/MainActivity.kt @@ -4,8 +4,13 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.runtime.getValue import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.interlinedlist.android.core.datastore.SessionStore +import com.interlinedlist.android.core.datastore.ThemeMode +import com.interlinedlist.android.core.datastore.ThemeSettingsStore import com.interlinedlist.android.core.designsystem.theme.InterlinedListTheme import com.interlinedlist.android.navigation.InterlinedListNavHost import dagger.hilt.android.AndroidEntryPoint @@ -18,6 +23,9 @@ class MainActivity : ComponentActivity() { @Inject lateinit var sessionStore: SessionStore + @Inject + lateinit var themeSettingsStore: ThemeSettingsStore + override fun onCreate(savedInstanceState: Bundle?) { installSplashScreen() super.onCreate(savedInstanceState) @@ -25,7 +33,13 @@ class MainActivity : ComponentActivity() { val startLoggedIn = sessionStore.isLoggedIn enableEdgeToEdge() setContent { - InterlinedListTheme { + val themeMode by themeSettingsStore.themeMode.collectAsStateWithLifecycle() + val darkTheme = when (themeMode) { + ThemeMode.LIGHT -> false + ThemeMode.DARK -> true + ThemeMode.SYSTEM -> isSystemInDarkTheme() + } + InterlinedListTheme(darkTheme = darkTheme) { InterlinedListNavHost(startLoggedIn = startLoggedIn) } } diff --git a/app/src/main/res/drawable-night/il_splash_icon.xml b/app/src/main/res/drawable-night/il_splash_icon.xml new file mode 100644 index 0000000..dd2b3c1 --- /dev/null +++ b/app/src/main/res/drawable-night/il_splash_icon.xml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/app/src/main/res/drawable-nodpi/il_logo_mark.png b/app/src/main/res/drawable-nodpi/il_logo_mark.png deleted file mode 100644 index 01ac3a536537247f5cc289dbd0b9a077f13e1522..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14348 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%0AP zPu3#5z2`;oyL@4*!DRBzv$;iuferKhuGpf&@*)0roPcJH1glR;j=s|E=fpcO%Y+>D z+T?#CcPUo;8Z1S0z){dNtMmi_=p7QDqcpkK_pq6Li~nf#M1$O4vYEez_falARE1Gt z(sC%LCwL?x56yoA&arozF;RiJcLyBr?eh>7hnTQAN>1W`l8Mc7$^ZTPOLGbEFaha< z*KCR0_J_+0hAeEkaa5AKy?c>y5*|I4K>6%U!woy6eBq9J}H z_~c6f=uGeL7i#yiM@c+8DhfBI#Yb{4R9nHoTWzT1Xuu~`%JenhEdo&)tzak2bmYm? zQ{edo%NKp^@u1MwRswEY{ykQQE#^X?IjRU`^6tGG9q(NO$1mUu9+Sx1UNq<->ZwKh sm-q$!ubgc8znlJ_1^&kZCUrFjbo8);%5@cS+`musw2ibX?%9R^KhRsB{Qv*} diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml index 90219af..67f4a0c 100644 --- a/app/src/main/res/drawable/ic_launcher_foreground.xml +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -1,5 +1,13 @@ - - - + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_monochrome.xml b/app/src/main/res/drawable/ic_launcher_monochrome.xml new file mode 100644 index 0000000..2d9710b --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_monochrome.xml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/app/src/main/res/drawable/il_splash_icon.xml b/app/src/main/res/drawable/il_splash_icon.xml new file mode 100644 index 0000000..67f4a0c --- /dev/null +++ b/app/src/main/res/drawable/il_splash_icon.xml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index d70df48..40eab51 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,6 +1,6 @@ - + - + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index d70df48..40eab51 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,6 +1,6 @@ - + - + diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml new file mode 100644 index 0000000..2f42062 --- /dev/null +++ b/app/src/main/res/values-night/colors.xml @@ -0,0 +1,5 @@ + + + + #121317 + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 76c7982..21411a9 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,5 +1,7 @@ - - #0F4C5F + + #F4EEE2 + + #F4EEE2 diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index bd04fac..45a2b2f 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,15 +1,16 @@ - + diff --git a/brand-kit/HANDOFF.md b/brand-kit/HANDOFF.md new file mode 100644 index 0000000..4219d0d --- /dev/null +++ b/brand-kit/HANDOFF.md @@ -0,0 +1,225 @@ +# InterlinedList — Handoff & Integration Guide + +How to route this kit to each team and get them productive fast in +**Claude-enabled projects** (Claude Code, or a Claude project with these files +attached as knowledge). The pattern is the same everywhere: + +> **The 3-step pattern** +> 1. Create the team's project and drop in the **subset of `brand-kit/` listed below**. +> 2. Add a short **`CLAUDE.md`** (project instructions) that points Claude at +> `theme/tokens.json` as the source of truth and `guidelines/brand-guidelines.html` +> as the visual reference — and says "use these tokens, do not invent colors, type, or spacing." +> 3. Paste the **starter prompt** given for that team. + +`theme/tokens.json` is the canonical, machine-readable source. Every platform +file (`interlinedlist-theme.css`, `android-colors.xml`, `android-Color.kt`, +`ios-ILColor.swift`) is derived from it — if a value ever conflicts, tokens.json wins. + +Brand one-liner to include in every project's CLAUDE.md: + +> InterlinedList uses the **Strata** system: teal `#184860` structure, green +> `#2FA877` actions, amber `#F0A830` for live/Dig, soft-sand light `#F4EEE2` and +> near-black dark `#121317`. Type: Space Grotesk (display) / Manrope (body) / +> JetBrains Mono (time & meta). Sharp corners (3–4px), 4pt spacing grid. +> Dark mode follows the OS. Never introduce colors, fonts, or radii outside the tokens. + +--- + +## 1 · React Web Site Team + +**Provide:** +- `theme/interlinedlist-theme.css` (variables + `prefers-color-scheme` + `.il-*` components) +- `theme/tokens.json` +- `logo/logo-icon.svg` (use inline/as ``; scales crisply) +- `icons/web/` (favicons 16/32/48, `apple-touch-icon-180`, `icon-192`, `icon-512`, `maskable-512`, dark favicon) +- `guidelines/brand-guidelines.html` + +**CLAUDE.md pointers:** "Import `interlinedlist-theme.css` once at the root. Style +everything with the `--il-*` custom properties; dark mode is automatic via +`prefers-color-scheme`, and `` forces it (wire this +to a Settings toggle). Load the 3 Google Fonts. Never hard-code hex values." + +**Starter prompt:** "Re-theme the site to Strata using `interlinedlist-theme.css`. +Start with the feed: masthead (teal), left nav, color-edged post cards +(teal/green/amber left border), right rail with the stream-clock widget and +trending tags. Add a Light/Dark/System switch in Settings. Wire the favicons and +web manifest from `icons/web/`." + +**Deliverables:** themed feed, thread, profile, compose, settings, auth; +`site.webmanifest` referencing the web icons; a working theme toggle. + +--- + +## 2 · iOS App Team + +**Provide:** +- `theme/ios-ILColor.swift` +- `theme/tokens.json` +- `icons/ios/` (`AppIcon-*.png` 1024→40 + `AppIcon-dark-1024.png`) +- `logo/logo-icon.svg` (in-app mark) + `guidelines/brand-guidelines.html` + +**CLAUDE.md pointers:** "Use `ILColor` for all colors — they adapt to light/dark +via `UITraitCollection` automatically. Register Space Grotesk, Manrope, JetBrains +Mono and reference them via `ILType`. Use `ILMetric` for radius/spacing. Add the +iOS PNGs to an `AppIcon` image set in the Asset Catalog (include the dark 1024 as +the dark appearance)." + +**Starter prompt:** "Build the SwiftUI feed with Strata: teal nav bar, color-edged +post rows, amber Dig button, bottom tab bar with a green compose FAB. Timestamps in +JetBrains Mono (military Zulu). Support Dynamic Type and dark mode." + +**Deliverables:** themed feed/thread/profile/compose, `AppIcon` set installed, +fonts bundled and registered in `Info.plist`. + +--- + +## 3 · macOS App Team + +**Provide:** +- `theme/ios-ILColor.swift` (shared SwiftUI tokens — works on macOS) +- `theme/tokens.json` +- `icons/macos/InterlinedList.icns` (ready to drop in) + the `icon-*.png` sources +- `logo/logo-icon.svg` + `guidelines/brand-guidelines.html` + +**CLAUDE.md pointers:** "Set `InterlinedList.icns` as the app icon in the target's +General tab. Use `ILColor` tokens; the desktop window uses the 10px radius +(`ILMetric.radiusLg`) with traffic-light chrome. Provide a teal icon sidebar." + +**Starter prompt:** "Build the macOS window: traffic-light title bar showing the +subtle stream clock, a teal icon sidebar, and the color-edged feed. Match the +macOS surface in the design doc." + +**Deliverables:** themed window + sidebar, `.icns` installed, light/dark support. + +--- + +## 4 · Windows App Team + +**Provide:** +- `icons/windows/InterlinedList.ico` (multi-size 16–256) + `icon-*.png` sources +- `theme/tokens.json` +- `logo/logo-icon.svg` + `guidelines/brand-guidelines.html` +- (If WinUI/XAML) generate a `Colors.xaml` from tokens.json — ask Claude to. + +**CLAUDE.md pointers:** "App icon is `InterlinedList.ico`. Build the color +resource dictionary from `tokens.json` (light + dark). Title bar uses the deep-teal +`#0C2C3A`; right-aligned min/max/close. Sharp 4px card corners." + +**Starter prompt:** "Theme the Windows (WinUI/Electron) app to Strata from +`tokens.json`: teal title bar, color-edged feed cards, green primary buttons, amber +live/Dig. Wire `InterlinedList.ico` as the executable and window icon. Respect the +OS light/dark setting." + +**Deliverables:** themed shell, `.ico` wired, generated color dictionary, +dark-mode following the system. + +--- + +## 5 · Linux App Team + +**Provide:** +- `icons/linux/` (`icon-*.png` 512→48, hicolor sizes) + `logo/logo-icon.svg` +- `theme/tokens.json` +- `theme/interlinedlist-theme.css` (if the app is web-tech / GTK-WebKit / Electron) +- `guidelines/brand-guidelines.html` + +**CLAUDE.md pointers:** "Install PNGs into `hicolor//apps/interlinedlist.png` +and the SVG into `hicolor/scalable/apps/interlinedlist.svg`; ship a `.desktop` +entry. For GTK/Qt, generate a theme/QSS or GTK CSS from `tokens.json`. Window +controls are left-aligned on GNOME; body is identical to other desktops." + +**Starter prompt:** "Package the Linux app with Strata branding: install the +hicolor icon set + scalable SVG, write the `.desktop` file, and generate the +GTK/Qt stylesheet from `tokens.json` with light/dark variants." + +**Deliverables:** hicolor icon install + `.desktop`, generated stylesheet, +light/dark parity. + +--- + +## 6 · Android App Team + +**Provide:** +- `theme/android-colors.xml` (View system) **and** `theme/android-Color.kt` (Compose) +- `theme/tokens.json` +- `icons/android/` (`ic_launcher-*.png` + `ic_launcher_foreground-*` + `ic_launcher_background-512`) +- `logo/logo-icon.svg` + `guidelines/brand-guidelines.html` + +**CLAUDE.md pointers:** "Split `android-colors.xml` into `values/colors.xml` + +`values-night/` overrides, or use `android-Color.kt` with a Compose +`lightColorScheme`/`darkColorScheme`. Build the adaptive launcher icon from +`ic_launcher_foreground` + `ic_launcher_background` (mipmap-anydpi-v26). Register +the three fonts in `res/font`." + +**Starter prompt:** "Theme the Android app to Strata with Material 3: map +`android-Color.kt` into light/dark color schemes, build the adaptive launcher icon +from the provided fg/bg layers, and lay out the feed with color-edged cards, a +green compose FAB, and JetBrains Mono timestamps." + +**Deliverables:** M3 theme (light + night), adaptive launcher icon, fonts, +themed feed/compose. + +--- + +## 7 · Documentation Team + +**Provide:** +- The **entire `brand-kit/`** (they document all of it) +- `guidelines/brand-guidelines.html` (canonical visual reference) +- `theme/tokens.json` + `README.md` + this `HANDOFF.md` +- The interactive design doc (`InterlinedList Redesign` — screenshots or the live file) + +**CLAUDE.md pointers:** "Treat `tokens.json` as the single source of truth. When +documenting a token, show its light and dark value and its role. Keep every code +sample in sync with the platform files in `theme/`." + +**Starter prompt:** "Produce developer docs for the Strata design system: a color +reference (roles, light/dark, contrast notes), the type scale, spacing/radius, +component specs (button, tag, post card, Dig button, input), and per-platform +'getting started' pages that mirror `HANDOFF.md`. Export the guidelines to PDF." + +**Deliverables:** hosted docs site or PDF, per-platform quick-starts, changelog +convention tied to `tokens.json` versions. + +--- + +## 8 · Marketing Team + +**Provide:** +- `logo/logo-icon.svg` (primary, scalable) + `logo/logo-icon-master.png` +- `theme/tokens.json` (brand colors + type) + `guidelines/brand-guidelines.html` +- `icons/` (for store listings, social avatars, favicons) + +**CLAUDE.md pointers:** "Marketing may use the brand colors expressively but must +keep the mark rules (clear space ≥25%, sand or near-black backgrounds only, never +recolor). Headlines in Space Grotesk, body in Manrope, timestamps/labels in +JetBrains Mono. The tagline is 'Ordering streams across the chaos.'" + +**Starter prompt:** "Using the Strata palette and fonts, design launch assets: +social avatars/banners from `logo-icon.svg`, an app-store screenshot set that +matches the in-app look, and a one-page brand overview. Keep to the token colors +and the mark usage rules." + +**Deliverables:** social kit (avatars/banners), store screenshots, launch +one-pager — all on-brand. + +--- + +### File-to-team quick map + +| File | Web | iOS | macOS | Win | Linux | Android | Docs | Mktg | +|---|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:| +| tokens.json | ● | ● | ● | ● | ● | ● | ● | ● | +| interlinedlist-theme.css | ● | | | ○ | ○ | | ● | | +| ios-ILColor.swift | | ● | ● | | | | ● | | +| android-colors.xml / .kt | | | | | | ● | ● | | +| logo-icon.svg | ● | ● | ● | ● | ● | ● | ● | ● | +| icons/web | ● | | | | | | ● | ● | +| icons/ios | | ● | | | | | ● | | +| InterlinedList.icns | | | ● | | | | ● | | +| InterlinedList.ico | | | | ● | | | ● | | +| icons/linux | | | | | ● | | ● | | +| icons/android | | | | | | ● | ● | | +| guidelines.html | ● | ● | ● | ● | ● | ● | ● | ● | + +● = provide · ○ = provide if the app is web-tech (Electron/WebView) diff --git a/brand-kit/README.md b/brand-kit/README.md new file mode 100644 index 0000000..fc1c475 --- /dev/null +++ b/brand-kit/README.md @@ -0,0 +1,74 @@ +# InterlinedList — Brand Kit ("Strata") + +Everything the web, iOS, macOS, Windows, Linux, and Android teams need to +re-brand InterlinedList on a shared visual system. + +> **Direction:** Strata — deep-teal structure, green as the action color, +> amber for live/Dig, on a soft-sand light theme and a near-black dark theme. +> Type: **Space Grotesk** (display) · **Manrope** (body) · **JetBrains Mono** (time/meta). + +--- + +## Contents + +``` +brand-kit/ +├─ README.md ← you are here +├─ HANDOFF.md ← what to give each team + Claude starter prompts +├─ logo/ +│ ├─ logo-icon.svg ← vector mark (crisp at any size) +│ └─ logo-icon-master.png ← 321px master raster (transparent) +├─ icons/ ← export-ready app icons + packaged bundles +│ ├─ ios/ AppIcon-*.png (1024→40) + dark 1024 +│ ├─ macos/ icon-*.png (1024→16) + InterlinedList.icns (ready) +│ ├─ android/ ic_launcher-*.png + adaptive fg/bg layers +│ ├─ windows/ icon-*.png (256→16) + InterlinedList.ico (ready) +│ ├─ linux/ icon-*.png (512→48) hicolor set +│ └─ web/ favicon 16/32/48, apple-touch-180, 192, 512, maskable, dark +├─ theme/ +│ ├─ tokens.json ← source of truth for all platforms +│ ├─ interlinedlist-theme.css ← web: CSS variables + prefers-color-scheme + components +│ ├─ android-colors.xml ← Android View system (values / values-night) +│ ├─ android-Color.kt ← Android Jetpack Compose +│ └─ ios-ILColor.swift ← iOS / macOS SwiftUI +└─ guidelines/ + └─ brand-guidelines.html ← printable one-doc reference (open in a browser → Print → PDF) +``` + +## Color roles + +| Token | Hex (light / dark) | Use | +|---------|--------------------------|-----| +| Green | `#2FA877` / `#3FBF8C` | Primary action — Post, Compose, Follow | +| Teal | `#184860` / `#0C2C3A` | Structure — masthead, rails, card edges | +| Amber | `#F0A830` | Live status, Digs, highlights | +| Base | `#F4EEE2` / `#121317` | App background | +| Surface | `#FBF7EF` / `#17191F` | Cards, panels | +| Text | `#16323C` / `#F3F1EA` | Primary text | + +## Per-team quick start + +- **Web** — link `theme/interlinedlist-theme.css`; it exposes `--il-*` variables and + optional `.il-*` component classes. Dark mode follows the OS via + `prefers-color-scheme`; force it with ``. + Load the three Google Fonts. Wire the favicons/manifest from `icons/web/`. +- **iOS / macOS** — add `theme/ios-ILColor.swift`; register the three fonts. + Use `icons/ios` in the Asset Catalog; drop in `icons/macos/InterlinedList.icns` for the app icon. +- **Android** — split `android-colors.xml` into `values/` + `values-night/`, + or use `android-Color.kt` with Compose. Import the adaptive layers from + `icons/android` (`ic_launcher_foreground` + `ic_launcher_background`). +- **Windows** — wire `icons/windows/InterlinedList.ico` (multi-size 16–256) as the app/exe icon. +- **Linux** — install `icons/linux` into the hicolor theme dirs + `logo/logo-icon.svg` as the scalable icon. + +> **New teammate?** See `HANDOFF.md` for exactly which files to give each team +> and copy-paste Claude starter prompts. + +## Rules of the mark + +- Keep clear space around the mark ≥ 25% of its width. +- App icons sit on the **soft-sand tile** so all three mark colors read; the + near-black tile ships for dark home screens. Don't place the mark directly + on saturated teal (the teal strokes disappear). +- Wordmark: "Interlined" in text color, "List" in `#4FD09C` (dark) / `#2FA877` (light). + +_See `guidelines/brand-guidelines.html` for the full visual reference._ diff --git a/brand-kit/guidelines/brand-guidelines.html b/brand-kit/guidelines/brand-guidelines.html new file mode 100644 index 0000000..974a952 --- /dev/null +++ b/brand-kit/guidelines/brand-guidelines.html @@ -0,0 +1,179 @@ + + + + + +InterlinedList — Brand Guidelines (Strata) + + + + + + +
+ + +
+

Brand Guidelines

+
+ InterlinedList +

InterlinedList

+
+

Ordering streams across the chaos

+

The Strata system: deep-teal structure, green as the action color, amber for live & Dig moments, over a soft-sand light theme and a near-black dark theme. This document is the visual reference; machine-readable tokens live in theme/tokens.json.

+
+ + +
+

01 · The mark

+

Logo

+

The mark braids three streams — teal, green, amber — into a single ordered form. Use the master at logo/logo-icon-master.png. Preserve clear space of at least 25% of the mark's width on all sides.

+
+
On sand · preferred
+
On near-black · dark
+
InterlinedList
+
+
+
Do — keep the mark on sand or near-black; pair the wordmark with the "List" accent color.
+
Don't — place the mark on saturated teal (the teal strokes vanish), recolor it, or stretch it.
+
+
+ + +
+

02 · Color

+

Brand palette

+

Three brand colors drawn from the mark, plus theme neutrals. Green leads action; teal builds structure; amber marks what's live.

+
+
Green#2FA877
Primary action
+
Teal#184860
Structure / masthead
+
Amber#F0A830
Live / Dig / highlight
+
+
+
Sand base#F4EEE2
Light background
+
Surface#FBF7EF
Light cards
+
Near-black#121317
Dark background
+
Deep teal#0C2C3A
Dark masthead
+
+ + + + + + +
RoleLightDark
Primary action#2FA877#3FBF8C
Text#16323C#F3F1EA
Link#184860#7FB8C4
Borderrgba(24,72,96,.12)rgba(255,255,255,.08)
+
+ + +
+

03 · Typography

+

Type system

+
+
Space Grotesk
Display · wordmark · headings · 700/600
+
Manrope
Body · UI · reading · 400/500/600
+
JetBrains Mono
Timestamps · counts · meta
+
+ + + + + + +
StyleFont / weightSize
DisplaySpace Grotesk 70028 / 44
TitleSpace Grotesk 70017
BodyManrope 40013
MetaJetBrains Mono 50010
+
+ + +
+

04 · System

+

Spacing, radius & components

+
+
+
Spacing · 4pt grid
+
+ +
+
4 · 8 · 12 · 16 · 20 · 24 · 32 · 40
+
Radius · sharp
+
+
3
+
4
+
10
+
+
+
+
Buttons
+
PostFollowDisabled
+
Tags & Dig
+
+ #dev + #sync + #meta + ✦ 12 Digs +
+
+
+
+ + +
+

05 · App icons

+

Platform icons

+

The mark on a soft-sand tile so all three colors read; near-black variants ship for dark home screens. Full export set in icons/.

+
+
iOS
+
macOS
+
Android
+
Windows
+
Linux
+
web
+
+
+ +
+

InterlinedList — Strata brand kit · tokens: theme/tokens.json · questions → design

+
+ +
+ + diff --git a/brand-kit/icons/android/ic_launcher-144.png b/brand-kit/icons/android/ic_launcher-144.png new file mode 100644 index 0000000000000000000000000000000000000000..4b91d54e96adc66073dd3b9a8a6d557e759ab409 GIT binary patch literal 5882 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 Q!vFvP07*qoM6N<$f~RFBb^rhX literal 0 HcmV?d00001 diff --git a/brand-kit/icons/android/ic_launcher-192.png b/brand-kit/icons/android/ic_launcher-192.png new file mode 100644 index 0000000000000000000000000000000000000000..1cb6ff92f0ac8642f36f3be86b85ca496c41ee42 GIT binary patch literal 9020 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 DfI3a` literal 0 HcmV?d00001 diff --git a/brand-kit/icons/android/ic_launcher-48.png b/brand-kit/icons/android/ic_launcher-48.png new file mode 100644 index 0000000000000000000000000000000000000000..cdd6aefaae3b1594166425c54b60d199ef2f0d7f GIT binary patch literal 1419 zcmV;61$6p}P)KE8buBCtx;Bp_)`QC6|9y%2#M9;i$%eId`KS}h!jc17V!^6 z(}#!^UzOUH)>`PY6^S(_5}UM1in^BFV0KG4YjS(OyL)dkyF0TpyOVSq=FI&&=R4nb z=iWP$wJ3nUWO8alDmmAaPR#C2C1($(5;Ie5DK?om)Tc0(^O@^GOaQ5=VgU<(O}3MrXMq>X3=k^GXB5>Hgc44wGDd`ke)x+g~u%tPsP0t9ZlcCpjXRVj3xhetpn8K^P z)`@F=;0n%YD*@uV`W`f{Cexgi0RAG}7Bu-C(XZ&C@7^=^_@RBlrjVpKTlozU=*bgJ z%+eTJZ|B2w^szVTR0&R(J|M?MNtLLr)EUVW1zq=?+!Du(V{B)#@dN z`re77%E#*s+$||+qt^iK_r_;m>#A!Rn>eFH!U+UXZaugMMM?X1I##mZsT15SlCs0e z!QQ{pIzK!>ZWiwwH{*!H)52cxV(|BD%pIpttLK0U9OHcxDheW0CD>al1mG8gKw-Nt z<_@*GKR;nysh$Q`%yL3KAZvgC-&mMUCp+=SoWYale=v0{Io-g1a5C-VwS<7QS7+@9 zGEUZgcbj&zm3{jTn6}$qFizeoX$XjW^Y!nQeefFbpw(9^r^44x@Z@rN=wlx=xSJlDX`9(%!enHZiEIl#sIX(04Fg-goVzb}o5(WS(y`IsZ8o2}$Q~=#*)Ip_xy_qMFDFWj68&?6D8^DBWK zYEp=pm8HcWJ^0#OoHj7}@9)fQ;&^sZ9iJhwWMXl=LWr9gXAc!{x5DAu&#`ZmEzoP* zp$}|$I5uq^)iQ!5wvl1XLW%gGMSO5UBg;wBF zRa0|T0-A2V^It(C;D4E_;^!I+uHXzOB>_ zI-0`m16&4vA~9xAS2WMj;MoMAS6Fff%38Ei+=>J=+_2$i5>{``31Ci&TLG~m0T^!# zx9ulEwewRWb5Wc?uE0tPfKOw%eNRn@P~@2lNR=>00r1UI)5QD6WIm|@0dJYSb@0U8}2Q`lg5 z+?YAMJ1~nR#1Zzv|Jkd5?Ld7RV>utI7cqzh2Cz7$qU$mM00960Q;wfG00006Nkl-@74yB|7kw#!F8c~q$66p?sff+zR1nH1gK)Sn!E|CuD5Gg6? z{tn)IfB$bS)&ka?+2`HQe)ita+3$Pazj!V~c!TB!004w?vQJe201EyI1@LgdU;FMO zrvN|#$US}Z${q4M8P|}meYEqc%q=qCQm#^$wFaw@iXh(BW4nQ^97ZPl80DYf@Aa8A z8YX-Do&W3;)IgoZXG+?mdo{hW+<9?z7qjAWl|6E`QyXqq!yjGUTz~a>Hq9g-xw_i* zL#0>{PzNPUrHzJOd(T2eOmEhxwf5T=7qO#RBQ_jY-Zo|@qmi}+dE94CHp{s(mYlsQ zMKijW>NaLSIwOCLsKxR`ysgOyjjWW-jT&LeQH#`woJn!k+H+2f9W2$(s@LnF<2Wkm zIjp#CBiC52Ajy!_wOyES+j63JE5)9&+?n-DNh*{6;op%&7#0KgGy1>E&?3+XT_4 zeR&`9go{KT(@j!DUcS`Oy8J9s(Pv*bMlJk$N@$A`t1!knL8NWQR8m$YSJ`h;D~XM% zP))k$MvLN)6DswXizn{-pRHp`qL-dRSxD|c3cdpf4!#IPI1I!!YfQaBQiEMUI3E0^IE?guLR ztyt=;yCir2+^@O~+Q5vh5bQ6sA2~CwH4Y(fVM|QF;4F91LKW0Nxx>5<*$!#3&6IV4 zZiO94#QvY&`(+g|W>$K(p6@B$6_YNMGY7mQ_aBLVatLOI)+!$kBQwtAk3MbHRU$Pn zAIxOc+AYZ%QWA8QUE?j-DbojC= zq)V37#==)`!#LSAjLbc&L^950pXnYUjT4>~$T0~(=_?98VtBRSMNty^1a~&!J5Ax zz7JdHii;Vu;b?jh;GY^^-pxNIBV=DjK*@NgqmI=QZRtJD$Lqb zF2J>Z2ri{3oaU0}y{wS2KH&ZJn`i_=0B^O#wU)utgJ8WRyEB6!aqvx&O*qO4s%OOy ztoHa>Imo9y4>+I#CE29(Ldgz zH9(FD4@v;|qT0UV+E$oFHIZbKYDC8b_MI8Xw}E0cT3T|`&45l zHR>NNBr3^!pqz>Ou>6dwa2z%m`jlsxy_aIx>GXgL)2J3bmKqU5%}yrk4t3<7s#-58 z=yY$O+ko4V63QZ=5^Ar3fhf4sm#1kSCMU2p61&_2ZmuJbP4l0@YsYs5`(Dd3vBCDK z#!d9X-bx%E&;`A}V;v3i2e1G%hNBi%Y0D|DFV?i8YwKC!95k-5(H!UjiC*nI)F4`{%^#=2)*TXh3got(GLDxH%m?9EaQWRBT`}eNq;Do1V6CS^%Xt*8iDs>&r1(Wx1xH5t zA-2`|fYoHes{O~9$|j|v0`8_-TZI#viE7p082QEP0xJQ{^w?Sx&0hJE5;bU*z{83U z!2!BU+0zXJb#1CE%5w60bO!h)el5fHq2?Qp)P96%aWbMJ2U4~UG`|$qAw~biUuz_Zli{VlIG?6>g(dR<@VB= zVg&ai=(&2=Xd}@9@rME->tvX08Y8rIUw70=y?@5L$I)qse02(e10eV0eiR(Un3>D7 z^UE-bkm04XUi&Od2Tebi9(9U0U{p*>zGb6C4LnVBPpZn|rH^>Nq<0~fnk}YA#tyE! zjg_zBV#9BHy(YQ(^$RC<*lAuXkS6t*qqL#2)XNf-2T^?PN{T70@5w>*|B%FIu2iNh zRX7&1hiR3w+yt?I{5cNhau6tEnb-64vQa|jo2b%X%RgC}R;ndr`rk?%MU5+Jpn*MFmk zfIg^D9%y`YhPNn74=z>3)S$QfLbYi3UhKl)VlBf@H(lMqZ%w$9lrb+gkajw*uV`u+fXUz$nv?8fE;OU@qi1NESj?nj_oj=wt@ z)F%!j3EuzovC}Pd13T`~T?_#k6NGH_9qN#}!>lO}8Fa|naMQ#!i45-*4-SBiluvUM zOw>r$)`%%V+&Sqbm$k{22bW_9v(_y{zCYyH{YrBHPX@411JoU%{s{>#&&W!o3`5?^ zN7FF>vlf4O!;nLc$*MPuIYq2&7dH@(T5`GKgM^O? z9T!C#TJi&U0qRLHqXsAZ)YMLpv)>}Bq3a+ZEP{|T>rKycVtLC4QPUzg0KD37v%;!u zJ-6qou;~x>6hkD6?S&c0wWsV%$gsmllav;z(W}2$0xz4jngJ#e{rkLndNqH#P9X(z z6P3u9-(o&PK{d;vj5xcq8C7m4|CS1l1A+|QEC%~@qF%9nC3+X0A*99%65SCx$!;;1 zfqWI2NHhKmJ1>dC8ap;j5}nYO$2P7W+rY*C!)4GenI{qe5io?6hYdB#pXFmLi(her z9XyEj0+=k!jBlbAj1bYm9D)Wyv>CCbZ9YI^(^aA zCD~mt70{8vk85gs{&-p!T@YLJ$X=Hx1ho4x=FQw!q;4tIO#@ev;Q$Egfvr(Ng;{TI z38<{LCyIS;syBn38obouDdJ3IbE8r=l~TqsGoO^K!f|A#pmg6orXv(#Z%DnNvlGQa~=fEwC-R}?SLNg2_Y_nNjaxVVqYgT^_0^mz^;?I=vM*ZGPpc47wW}=zuHPj??ungsq#+PSZ z$La!?<@nE-ae&VQ_+MmoX{^9c9KakFv*S&bm|Io&lxa{1#`aK;61^gf#=|V z1>4pJ%iMRsp7|7(SM4?B+6_4`x7k~kBz>${0$wGjh&cOxC^-_-_By~+J3EFN@RuJ@ zzNih3XWy(^f$QoaAApybBO(_pd(djb=hkNobCr25$;U$2s^!@`RM2!iTv z&%lUi?7NkpJ#&QK*EdGN?mWe~S{dXaZT)FvIZD9#K0V_SlgCTid1W7DtO<6aYepMF z4_j@Jb{LAtd_(tdMgR%4b27J-%i8^l%ok4`OiU1Ddj#T4f76ftm064{xT?uVF!8)R z?m8l&ErLph-Y5_b0Pg=Dwcp&>D5AJX_Dr#Q-xQcfnd+&Xgn}B{FE{`3-yo1kLnkS% zQt6~4IGBSWi10e?FMWyDQ2l(vz4$mE44w|C@CV&*vu(CB?swhBSB(0H;52Pwn47jiEc-4kQ=8b_h{VG9amQ zw&gy^?&aaHL578rbeF&RHd#_v>7*w7zgfVL45{sPK>pry(#?vIAuzW+RGt9v_(5^% z3x9v-M6F5xm@o0#x5Z@C;lgBGXI%<K+a|9&E>|{Vj)JCdge=rwHk-l0cd9l7kT#QOl>h_bnyz)xT{e9N|bl(0G}1 z*eLagJ2WJLgB}1yTdCeyl;d)fs_Bjj=Rcf2CoOgxYBWG1&ev{9gsO-E*>k0*@J9`) z|0Q|OGs7lVMv^0S8TsaDLICX2+YzhV(X!}F-M{&bK4wdkOas$7<#>$WaWc~scmbkP zL3kz;(JJ44O$P+@Jp$p2V$4wvGSFkr((1tm4q}$gyCtrs?gU0h{)^lP6jCSAPL{ji z$*52!6jczDcX-g^E%541zVcsEh3mpbgtNnfg!As?DEGjCgTx*gZ9cDF1C}th9=2;> zVcfcon53Gg2MAJk$@5N?Cl`I53*IMBM{73 zk6LF}NIw^+mgEO-(rH$XxD`LPNP;1biQAvo}HYKNfalL4fe{`!$}|I%H*e> z*Azp+j1>^b52x{~Z%C8XBRPR$o;{7`*MC*~^&-*sWPF>FY)SkkoLm-A6etGH4EdP9 z3j7fH2c|z{B#cXYf~1vv+KW+lK>J`{$GP|MmO$l`e}a_&8yU~;m&4g0mjtqZ)LWL? zEb(?1KX`-nFYCx(q^38r^Cw6vTejc=9r04|LT9<8f0ic8L<1X|LMjVP25Zv7>4C0> z_{4YLxc~3t1la3}v$Wz$Y9%8$E$~XC@S)09yDp`YruB!vF+jn@@Un?j3`R;)-t74k z0KY+vG+n$UConM*_fL>&@UnSTQg1Tkec@ugjfFZ@?4Kcf8ya?xv+{2r2PvxuzrJEb zQ272B**ze;=$`m+<>m)6EZ&OiBnIV>D|9_-MEIShdHDwWE8s<$_Zmsj`1$S0@Ck;$ zK7#u783Gw~n%&sbHdX0=15otn&@0}&y^9lZdL1DEo*@F+Rgz>}rh2l^#ij^yt>RP{ zBPo6wG;!x&3$Vu+$+|ubY!#tHGcpk794!sll&AS`hnHCYa%t5MbffY5)RU8>F7N33F`FtyX{I^hmgX%A-yJDYF)Qy65KykH}7Im-B{Z98(obwHZ;-nudMX_KfIn+n;# zIJJeNLKBgVePK6BzJZrutls)CBl!PoH%w?#rtlwIQ;R*Z)i(f3;W+wx8dodr6t_A5 zAu9sjKF+z zHCQR*8M|5pqm!>IG=P8Bq1kG9_TN|Kpz9kWE6++p5oVpwfNkgaH5_43yu6av6vD()>e@aKuf#p#Hs+#=8Qe8szth0pKj4@#asVelXhjbKvwsq+kZO!q z0IL57@+&sJiWzq4}Zk(f`HrHZAkN^bol{kVmsfi8dm{;v^tmVwmghcPydrA>|tKNu>mjm#@Q zTfX!E8Sx;yYk3qSQT3FGofCqZ9=C74HiCf#|L4RpJ4txJA{SEfL0ZdaieP!*MOEfG zd+~h%0X40^0s=${9Nrwur}l^VetHQ9+D@)mZd4Zk*EoVyp^vi7m^mdMEJ6@xv>)aD z_>!Of$MG0=bmiIiApRhCz_9f!JxCj|@E?a(V7_X+G>s3wKoH2OB08lTWA?HCId+qs zFJ~rDn&8P(EP%FJz&Jq_oZS2^E-WyJxxDmbpfop-J)`Ryf43>@L7CQn^-(1eb^78U zGvW(L34qZYNIegYo{d)cTe=ZY_#>v9tjuc)%w&H_Sz(hz6+D>#-={gx5y+g=5n1~n zS-{ZHM~bXRh3&t4-a-#lghne9dIB%BYOK?ytH7busbm)HU-qF+UmX-INRu7{OfwmJ zEb?C!{&~UzJ>^hhQtZg8IveAAZ$(ai2mqCXK$?=+)j!%5bjklY6SPqSD&8Ju?N6|Dp%f|pMU(L5~3wy=1t<|H8`r#-D1=$1FO6cel#seU??m#DsQ z!9!b|U2=;yasB0wSAhjQq1i{}Y3+w%*2$wF`R+VIAPY}tUzi2b0qzmr>e=$elAus_ z$8$G&!A0bw)T+*%uOB8X6ti$ik-DJaq~CEQ3zE0@w)~fQbah>b@a7uV*DU6YF*Fi+%?H2f;#ZTq z^I%Mj6-j}ER}*hjl?|EhYPXS!;h{$ayDGxuyMj*?^lY{>w? zWl1Y0wi6V>eXccmnGR#H>+O1iL>*{>(w^xd?D0YKZEQdYoIXB0i%_{9SGcIGp9tMhA4zeVM~kEVzG^A%^}^%XvX6X--9l3&ffwFXqdQf%$_MfQY*3?Xq-Z z`n2!E@tcdsuY^aNWAp5~Kj!6(ZbXoxCd)Jg`@&;z z>Z_Vuxpo&Q-ejdl83BN|EK@0rb6NgW`NM4Mrh(*!Q{gP^___B77<7-b*M9km;Sfn! z@`)HOeaQ5A|0cWPbBn$1W)u6HfWRxWBo-w(5~eFrP_sPs$0VFuPT#+>sV_b!cPGQ_ zx&^)%9}1e1^dTe8KPOWo>vEp^?m{Lj3B6x(H+_a3ccwZWzz9iWq0(qgutCLv!Q;;P z;cwuxM<%H)ELD8NLA!^mijzTSpXjps7#Tfo_gx2}xM3dqt zs5CibTJ>t|69i1k5-y_tf;Duep-Z z85rP8Q=2>6zEOGmx@@neV(1($eWLP~Sx2qTc#{yU`?or>XyX=}%@|EM{7|q-r&ofH z@Tw<~PA}GR=mB>KA>eE2{|;;vr|iY~g*U=@uL$rD>h}4aXMIB3I0t`QG_%vjE`Cxo zFj6v6GUiaWkxMgxmhjBC3FA@#U3`A}CybOj4}OTjSs2kuJWB3`ziGWHELgSc(G5jX zq#Obs3A>lI*zPu)cdTt} z?3YfqA3@XvgE9ZiT70+^G%$86cw6xyRotc*L1BUef8q3jsV@J)-K3IM?%(3ZH>7_N z1WXNSHU$P1o32)2rReO!C1WFq0sx~&^0yrjOhm7`td9mEd1SAE`FDc=li>n4m~I17 z=(lQlyZe8_UQP`S_$)9fm{t$jA7@ino6PO)P8&(J&mD@?#dsZ0I1=GWQMWf0zXsG~b)?0$7F zTwLp_+GM76{S3|rZ9ZL1g90W~GPqxH6hPv^9veD2Vd-m%6E7P%`mg6dn4zXh6uv2y z?EO$*a*I$S*b7dPJDMjyWs(`2N#2Z}2;&2a-e13!)U!pKrlot5{CJg{b?K3~~u zK0pK4B9>8EbJ@sj+1_w+`_RvI~#NBv;3t`X4PD343H_eFJ zNLhz>_bg-m zgpmh^Nba>k%$M_iekL(++6tqAClDidu%v=3uX4-+{gDl#L{$i0Ce; z!FAPd_)#N;dkeq@fF{eXB)lx!f8vmZ9@{anwVvfWw_j`)@j8A^wd#1rdlRioDDXXE z3bL~0%!K1_RK8H52JYA)U0?#4vSdL5Xpt!1#>tg3tQTBq0H$(?m=JO611L6Nvhz)j z+d4&FSlnAx6XE$#UBLsq-!C~}D{f&VFMSTDh$vXEFD1_wAecN;MA2N{W-wj=u5bNpE&b0}-fGcLv zpL2CMHaew0Z?zdc>6kz74t!)MQ@)IIHOI9SYhbJWv4W?sS>rX~^q(*kbKILi z5IXShFqs4gYL{;i#G~Z$k(JuS{*$gtSnoFq>WI2I-WIF`{->$w0UtXZOpXY$z3krQ z`geQ6FE;`kesp+|1@WU%Uan1`pz7l~B5TK$`4tyLwczF%Q`mS`$9xN zmJgPMgoX+pyY}M99B6^_SwnG%2GCds+7mJaijz6wWSLl?sbFtERX3k|+wW*T-!-&G z6`@8INfeQ5{ZsOi1^(iYp^`c2G8Unx%5zJt;A3bN%~u$yE8}}pwecewH>#u zEMg=SM)nR-Fz$z~W;L+7Gg_sJ6WYP>F0-rr^r4MOu*nM#wnGjYT%=AK`Lq2%-`Me2 zY0VqIZ@rHa(h7t{MxsKoyd>+1mrj4ntqaBVMb-|UIhF`?yoW``V1_2;t*vv>NzsoI zJbtp0G1TY_*%|*eX(qd>f>NS|!k?5o6)>zF!8LRxK&tVWXtM zEKi+D#W;}mHP@PvBl_Oo4aOjt^lQ_FL%p!hI+;s?Z8{giS;<-}kG)l^-9InK7oN!} z)15dyw4aDMj?3^QKELfFT-O>`h(w~->OdcEpQvS zMKgJJtM`I)+6pji9-;(J3fdGw&AtK)*+egICQQ$&oBrVGiVz?WlCI+pi4br>2z6wb zi`j7p2nP1@paiJl_Fo<=S>dn0dmmMs4?d%UXc{zvAYiBxK{NwEG|BKHG4tQhvSqrD z#w@vJ2PXa5;Lypj<5bw<2gDtV;W*xHK_Hy8dO-hovaEkL7pTD};6H007=F^PiaUU ztH#)m7g_7fBdJ|K8TYzh=e=8_9=d9!5VK*x+stKfkBFmzjsKOt!P*73S!1QR^Qz>P zdpC_&qjiJwI8kz4f0LHlLRnsGqb&GWO-Z1n0bF@G*M?V9^<5@y<(K?Y23jkfk_z~n zii?yE?;{<72W8v}P{5pL-3l$}Qn(qBl3$-gG$@-#V^xyhC{{S!6F`%1W&1RA9DgeQ zdv-P5ODLS(%e_u{`*ZbN3Ui;$_G~)K1C$ zs53<`=A1Zp_jKm?4LeeNW7a~;=7aXSTB=Y-ZYLpeQ_u$EDQ}_YJhtv0X0Q6xpnoYK z(4a-Hzpum3&gXqxqo%Luvf7)SYGaRY@*%6$Nc>wN=PtL3AKAjEA8ac<6`R?#S|Xel zzv`=2cI)@SYDzs4+xm1R#Gtb)Dw(4j$_9@Xb6Vnu@BbR+@p)5^1MU%^$J?h>d1J2d?q24z zWvdd?12F^bMq6g}!XiA50upNaKj(dvaYe1ZYF1u0ZmR=GNw>GujoP(CSyqAHXI4^q zw11T;ZGMnG^&0%caO!pYp_Jal*G`$eTT2(B_&bMjbBi&`O)Yzj-XGa#FK!Yy9B^!T zo*?HMPx_X47jm1V-fhpG0N#I&R(8UKn=t2sI`b4+G<{F1Yb&TdMz=DQOxi9K)^)4u zOK?(cH0gHtYsz=qJ2H=Jjo<3BkM1tH;O`!;*SH&v#<^SnxU%1_7v9O^O>GsY;1W+? za-@Q@e94~ms+|J>AyA-tzohtnmHC;R{>< z6B6zJG~JV=IFtPHa)x+ev*=g)*T&DCuCLpK(Xr~pxf|;*^1c`!=!17n~+gv>93NwA7qk3aCi>WA{Dxgwebg?ETb*ZKT|D4je@Cg*I z(^sUt4el#|vpfp&vg;x2`Q}0YPPcF7j(pZ*y7x+mBO_G>{Mi!^hUbkZ+wl$Aj|&Hw zg=vbNd$mC!PXCCjShp+UCF*;O@Sg)rvcrB|epCMOdT!B+aXYzNvJj}&W@E#L90@2u z^`JokUYNm+o_tNqi#!*kNzhS=eN;-*_}LM^(QSNgXL^O}#W2<7#njy=S6r4tVl)t0 zw@f1x%P`L>_f+kDEjO9A-;>Incc58goK6W+L@_KtP! z)o|nE3>B4nl5DFtZ81J?@vhwW6!PcDW;fb+^Tv#okb>4H_+ztZ{&T4}2-`4A51%?2 zwg;yy0|S#Gf)$Bp+^wNGkJGa3j>v)dWL0e3&EJWbNhP&6)~-VSQ&pz6p_I@92I9;8 z^Ns4GOyUw}Ju}Ub+#`?O_OBEZ)2qKl{l4U*?`|DV;f~%ZtL3PFB(E6~>U8_mKCMyh z{`XNuk^?fmzLdrWuxCyUlg%iuGcrIu`m3kzTD!wk<#kElys_*UE|Y5CPE8H1$r}sI zpE_T6Ikw#66nL97qfM0%@gXw*{GNvwbL&;5-=P1!$13*i{5Wgs+BdYHM|qO$EKL=B zf444NbT9Vc`q>(If{$a2@VIk<$K#d&9&n>*8JE1e)r9=T<5l9-P_4ZrJM!xn&Nuxg zs%1fnkL%{O!!MJP!;);J?qtllxv}uIbDg;VVwvTY>sYc?fF^@GJm*5HwJ?C^&8Jcu zd`guROyN@S{ky{A%c2=KP&hR+d(HK%16AVQ)$S774b57G<7cT|4Od5FJctkD8pfoLnq$v+@;&5Jmcoy{u;l?<}3fOBE%5_^mw88&~ z@c#NP{q3l$<3)$V9+>a4`2I^DM@#eNm*WR;#1gv!Xzr>cC@Pp1@40<rp#FxENT7T`9EI0-<~^hJ3q>cA%pERVq071x6{ux2DrF< zXJi?ny!tKVj9b0j=we=MSCqmm1{0+l)I!|2M{8m~lC9_Pvv`-|jfB#cAA*u`E>lCe z^3#OOGh6v*(8^Gs^Hl*|r(Y!#Ii^&>fA+$oM1eJ1;gCU++Z3NO0{Tve(t5Wp*)Pv0 zDJLceM)C#)3;K?mM0riRV=EeB;ij!S`A|Cb{O#LQ`n7`_?dG(xvcP~3Noj++BZ@4GMPw|BqwJ$m%K zl9!Mn{==Q_jih~}RwEVf>XBgqNBi6I0R(?Chd%q=e?1Z2rq~*2*wSSb|Kef&c)6c- zqNW%bxeUcol=LZ8UcLi!fbqz{oe+}e&&a|FzkPq405i2YnwIJ#rbYwyh!hM zqw#A?^3|=SDr}&FR)c_C;2pR}fii$n+v1;XiqOR|+$VR_wdZDN{G)rm(X(g2P2Ar? zKPOI6mEv>9+vR&n_0~6Lhl8BJrQ9gu_84|XUq(hBChw7y!iQ@l1}VSXGE(J!SSd@9 zjVHOn3PmsQbbWrkJYN3Mc{)_oFG|#O@_n~@t?qrC?d2Byc=H|b7X9nLSpfTz8!}^x zR}9-t&r!_V$H8ZffYBtzrS~oS;?;KvqVZG*mO`<$7w0?iTE|<>4;+Rh>x{1~$nR_W zVpa7nyD^G&TU*$)Gut8k=cJApVhf6X-!|TEAr*0&)*rw<_OK@4Gx55_qm%S94jXFa zceG#lh&#`&`cXE1e~BY-sgxsdaY!z3|C9IKDc%%!lE$iGwD<54J3|noqSyrcl1vn_ zUr&SAdPKG5O1i^ndAh^F{*rxbA^W0mU25=L-$Sgb?;`Lxg=CX}Z9HZl2}AzY+vtp3 z^@*(wq_hn_U|sFHv%JFyhbdyDn*l%ifM15=gMKC3&zT+C_De(@!rqG@Q#nM;$DETq zG53yVjXdWUTg^|0+=!+}r4kxbNM1vk>~riFo#J@W)kryAsY7ICFI{l4aM$>d)S?y z{${p6d&j;ww)e4<(3}BXMl?x_ZFG(!gES#exti0c-jmuk)PMUz-H zeu<}{>}yHi3)3t@%n>*JxCD4v^tiTrwj8`jbM zq+_Aji2VHPlfdT~$TJf!{zIBq1Tl3wmwbwS^xPICuHR^aUQ&OeGS!l;iqRV%B;R=g z^~Re>_G%a{nUTjzy|spF7KO7gp&_=0`}5XxH5)|Lf7-C@_ujIf*!KJP-kht0HpU0e zU7YcJl9im_&fisK`yDu$x51S87Sf&?0=UO=J9Ce>V(Ff{LnTh6aBb*~ar)_pyQL5!Lk zN7B-F%x=4?!_evsAD={CZ{-%(vrliU?a%e8B2#`K$;aiGp5w7((kzMPPci^hA)8%4 z!4pAD^)OwttAMkHVWS4O*QwotX9OJM97yNe(;IX4`5_SrPjFu^2Fm9xKN5JW=+0TE8Tn^Rzsi0o4z}qFw1{DoAHFN#*iiC{DiCZ zA@~rCN5VISyf_3f9r1Vq&mvgD|G+}-w_xzyb&^<>x96HEn;}YTv7xF*NkNHCPIUWL z{;|qgsN6%XH+y3=bUv;0$M$mA+TsmDFP2lKT#1ud9$^3t4@zo_SbUkVA6Rviv6vY% zd<|KqM$ktgkPf_mw{u?*(EoE5b^<=Z=eX4ONt2MSR^LcGD!sy*uI%i2^<@Y%-&;k8 zi4^>7n>UzcpSQ5>Zw7uHwo*(x64Z-QZ+|PUMqTDUN?7iYY-L~cU*x!J??y=AEPj2$ zw+&8Ud33Ua4Xp8{-MVqH(ThQf+Y#Q7S^q9^%l{Q#$sD0gg+Ni0QcnlV-SJ;Rg)PW;7ccH@TC(xDRg*! zQ#Se6vx7T1z2zGdpR0HC)9vT?yY>Dg7rr4Ww%)`!Uwc=z+acULI8b`Qjviiayn4yZ zZZzIvoVBPG2Kyqht*5tZ1SR!7REEKeyx| zt51ePpheR@kLio)M0cMBnsR6Fmk2&z%~+6-kua}opC@pbnSYR(MIkuJUu3QBz=3AA zR@@G^gdRuJuX62vyJb;CZI9~wdGRzkA7PZj8c{{PxlC@jchW^NBN1(QELHPFsdHmx z6I2n?zlOxfg6>1W2|x^KJ=yyCaLeAz#VpNu9I?_pjz0~O?nT|=`|8};UW6X8MN>hH zS8GQ3(~E?;_wYfWp2j2U`t9%m)B>sP$3;2t&7TZPz!vx8v?CU^ig3$5w+SC||AnsS z`sWFo&Jw;3|LHF{5Hnigh4QO9T8e|uz4%(UZ!n?3!y<^11R-_z%dC6)>7VT)@EqcI8n~%E32MI3e{&zbLx8tUFE*?TKbnl2&po{nn^hRt z?zzcFCMC7};`c+_o6e?bG7wy9e2s!;m#b4^b-UU1G@-n3gaAC_sm`Zqe+f~+aaT-@f{_-zb252GM z^Af1(`zXnYKtK`;r&Zik9l*xoGS=RhpB15fec_3)YS?yz|5od7uJ{Ajbm{fP`{CJX ze7^XNna?D!M50o(JKN5(BQLeDJ!-qZIrh}PVRqs@8#w%FAeW8dSwG{MvLbv=;b*}P z2VaH~HsSj{L{19s>an>A{HDL>%g+38w+4;7wO)fs7)Sr&N#>itbc8Kv?~3HF+len% z@oe4q_J=_NP3CgME6n_lefGw*AMS2txjTKoAzv|Ng2?g&IH>F2q{p@<9 z$X*)rz@DtQ^$6vBals?SKZI}Ck%Ath=k;Uvp_IZ-GW%4^AFtLA4W*F4o+v;p>%yU? zENSbB3{&PI^dGI1C1ALl{573I?9_R}YKz-N-#V)D#^es>Q{IQ)NRnmpg{jDtpL+KO z=*5e%4Xcm^@jz&AX}6bnzMdv}@Jk@x%OSUhzfIEFE?zFKMf_s?4fR{@`XBsI5;isD z(S?R2yv{XKHk!-(WX@!W$#8=V4rO>(k+~s@lEf1!aK`iY9(p9gL{LOHW=?Y}U)xXW z@<<{-Cx7enjR)5|)yfED(=-(E=x3raJ~$$#hgmQA3OA7SWoCVOJ6SYP+H3RX$F8FQ zB|V`03vc3dwCQ8Z=kGSeLqhe~)~C?bZJBo&+?KJe4Zk6p6?&=gfjDd>dO_HDva777 zE$-?Qw4IkIaX&awQ%d%J!1T&VD;4(mcDTGtrug0Bwt26=r#`+sM+7t-pfMj|TEt~U z7CaQi8j{Q^HgaesF_n4kW109#3YE`R4c+a24i{^IM%&%w^QGly;dhAG>EXvqPDRF- zwc3GXnxSCiV9=9T^h|GG7fD+wc&rlf4IZ!VkUuwz?XKfDo>@q+_3TKVq_|XO#v=1s z^oYdwAhfUglu_BxiO_&-1WVw-7e%Iu1l75se&A)b94V@!6Fk-d5!g(Tt2sTELzLQj$`C?qLNn{AD9eCheX<}?h{?|2FaC40!MbYmOCA2y&@6T2d)~@?nRMZD z-Y!}b!-BW)H@n-_q^s<8u-TG1G&^{NUT|Lm(j>Nlr^uiO)@+2(h>*Zy7;nAby0m!s zBDf=R{c7dG#jTKJ`Y&|qAmQT)0dkY$7Ldy=IUN(^Y&e-vPGwd_4~}z;@d9g zkNyaI&`np_jW_3};J)g^-p{!_tWPt?3ZAESy7dry_^hCIs+zQVVgJef*VTJf!5dg; zgL_)^#A9<_qlMzGUr3I>xTCe~${hWZG$ub*bLmSlOLiuhG=2gM)JQM&Wrsy2_ps(exSBD0MpXuECAwU7oT8 z6ss)RjeYdsz(iZ`1k z@lEYh!Pqf*yS@D>>k3J8Hif5ZJ4j^z)S{bb)29yX5HWb=r7hawNT)s>HD$Jd$ywUD zFd;%uvGR)v!R{Y<*l=?i1XeXBFh1qE#flz)XMBMjT*OX0(1r(h48JA|VuLh%_o?`; zz#T&h&^nGC3|Gf~s(MSYWpGJd4~d>*v)zkBTqM}!HS@^E@i{ZP2OkEEXJBin$*-7d ziWr5_J51OP8-{d6)*EIW#OUla*PFfhvpR2DT%nZ$68{Bs2%m7EjGKAj2Dc2Z4?_R*yu!>`Iw#mdSBl&iIZv_lPa6@`PmF| z+{8@n38DI!h8^vn?b^|+gxOo}6~DQ-ZL<#y(sT-Yr+~aiNllkS230l}MZvA#=?S+7 zuojdUM|^hmCwrU3UW383}AJpwIoUr$3^FHYHXwJlSus#*~JRR&Q7I+mA|Jh9)wc2wz?f z=^U@*ZO;!@Nn%Xwe1fj(uQ>FNcgh&nVw4+O5)EWmSk^-OSN12iia!3zd3a%4-SfPB z|3C%#&Q`qoP^&?=U9cxc`^*yafcmT7_>|Mr<|5Q-1p?VW-)i^c-lxSkPVz>?KZrfB zXU(GgVic=F$q$o!;miSIwG_n_jSz(~HDfE2@fE6scXPi5opxML#yMj24GviKwRbJz z_P^vyHT*=r@o?<+bhmF#tUlGpc>B?=D}Q?^W9G;#e>k!>ppHl%7FfhVIA6> z@VVbMn|s|?vs4qM%_bW@TB{~QN0&NEWWfH*uj5KvbTvm!K1GSi)_i2;bWQz*cfYwD zCPWA(a+X;1j@?tDH3{zo((t2Z)yGEF7C*Bx&M);XH&7Tgh9TsyoGgGej!ce$<5~E_ zKZoqc_Opw#`1T42n07rwd8N;aRN4*tcWpfBU6GjaOZGUZ_K@vdM8{17 ziduj`Jux(q7A4%65Z3Yam!!OdsxnkUIY)hpd_))!Go1|A0niVz(v&ZS?bfnyP1oDY z+b?5rZ&q6Wu58t;&QE!GO8&VB$M(&bIUqI~LyB^yWYk&@)9Z94q|+EvpHIvcq}NJv zV-8=;gVg`xYKLc*p6sa?H2*57SoBr-^mwPz%~5NLb7PY{ECg!Zi)a4`2QcMpxH1Qr zs;K5IZoIG@H)g9Vx00qB?+6>gg%qT*uD1q-GAQ_d-4qB70j~w{g3WB4m}70VXkXFa zdQK1Zcx}lTC3b6g2n8Pz6lh+8V@_b)_g(NSLjjSu0e3K@Kpn-7kT}%3u8eLJKSSk_ z?Y*BZ65(GYdPW322iIq2C8#+fUhjS~YbM#r=+v`OFu^RV<{9?rSXluW2Fc|xtSg`t zC!=?#fZ@(%)dOLB=qb`$nce)>8ztmrKY7;!K$|bz@sh?>#BpBv)zL%xfUOkD)mj}} zdS_w=YieS6xnwUy`cn5+>K=~NC7yeQ8wkOoGSyyW2xh{_FFdh5u&|{>=Rj=C)%gJ{ zxZKU31SX9pWN?GF*pCw*%PAJml@iLfrxObl z=1Y?$qz2->)ybOtI^UY>_|B%x>3B<@vk7(mo5GdmNfQxntLBpJ_A})J>JL@Vi>p zbn0w8nbjZh0S9=P?hj+Xg1OQ^xG5!a_=3vhoYK%%89JkRlH^lWpAzHIf1eFFv=GGr zflnIx)cDlq_6t$1JWjpBM(wA4A8jE7JXArxx+OWPQ(}=XW^~6n0zV*0<8?KCnFF>* zG)Qd~Y@mHKfXLa_3rjx*>WyJ;+LiDBud1(rit78qy)z6Y(%mE7-8m={qJngXfOI1u zFfbz`NQX2Cs0av3mo!KtAgFXBNP~30JL>QM-dpP~mkTcEp4eyaefHV=`_d!m2jppK zS4H|WMUultwJbKktYWzgP;PHGM;v5}#{PBBJhW|u-~AUo+|etdNgC5T=fQ*}8yCbv%PD?VG- z(f|JJsL3qilZxFawRIS^)#sdZfi*vV>f`GOYBn={K5MJ|r)1|3myM3U45(V$LwZG2-{iqTy?ifrT&{(0;NgX4ncu-D;LZ_W zLUu;9@a?)G3(AcK@zJapi;bY?v)%_$i5@5Wi;EIwA_r=wVkbhXs|(z!b6v8`^@pXu-S^CATQ|?lQ2};lPlK5}rOo9Qc&2JrXr*f1*+h<;o(+WEud)5e16eho<1@m$ zh@!rcsr>>K?OcOvO<+2f$3}g2k0vUFldv7XKDwhY;C>6Xi1AKHyHAH^%*1p`kpKSO z^M#j2$3F`j^Jg=k2yIDi7buK0&MFcGH}Bg_v4Zn56EqWFyIOqf)LHZ3I z;_SRjbt@*4I4fq4D3wlk;)$|^OtACh^O@$YOzPwE_y+*F;cn&wgpY@%?M2f2OoHMk zj5{*H0){e_U}gd9%(=g?<64%PNffOFEK&8SlGW^^J28_fG*=vDvAUvPQsrw#T(`@!4Z1$8wPaHiuLM5BfS1wfMEp?bJSIrvfMTRE|iCBl{ zG+97_iYnF+FEF0DLq0^{Wrwwf5>8)YhMxI-fV9j_qbsLcc#e60x}c4&1Wb#varaf9 zpC_%p%x7iD7MMf@yb2~LlF;>@Ae(yVs?QNKV{Lc6civ$1DQnj-8;KkQtKxgP)%%c= zUb;V-adA$Ls)FsSac(kH(9D2qJ}%7IrO@K}{M3BXM_-)WOOWG(oga;6Um7ev$9>iD z+S5++qDZXjL2$rA0WG5p1+;2zw*SlARuY6qi1wO3@@tIX9yoGoD^qe0ik3npqDw$k_ zLcnta3#{6(^QNA%vJMx194Z!3V1ZTn^g2-aH97M|OU~*i8e=-GXUPrR@;3Hb6_!Xw zf!aN0!9PO zi1q{%e8Q<9tWC4OE32Oe;izg@gGFulYKW|69LRmW8U`GIpM#v{%B)2kUig9)3QU-5shL&E`zL;sy@H|cO=Du(bKxait1|aaj z`m*g56JTbpd1q3OXcUv;2+wI>q=W50csqEnGgYDHsH)6%Xr`s~A}^es8>OBV+)Cls zWr3%8IwwGxZrph6*CR8v+rNJNXuLNr3(Q)T3arWUm{3TpuWY9Tr=go%DA_mBRxWvO zdsEq5wHmDJ&;-XYLJF`NTm@oG@ z6f7$Fo*B@)QOh9Dxz6WOFXH*%0Fz#I%FuW?B5fKIge%91oft!6je==jZ5oJK>^Zg9 zkf@p1u)SDLB#B%{*XzMq{2>XPExdq!+MP$SXhMhDM)eEE5J7$2?xZXFV`nSB%&YKL zY^kd_`LgyQxo2dvl||jO;5j)C_U)*zBlN4Uk0%9O&5!RsnCc0xDw5tZ=VyINVq#^0 zzWaJ7;UI$c2eXG%T=Gi(N2#-^veK?>T+M-6fdLDGK@*)2dgdn(3w|TgWZtXHeu&gM zO5VMDU@<;^d6*+r4gMO&)qsn>W^HplM?L^~mMIEOnh^QUG}K)R2LDLgNfdJP<8{9< zM!bN}H&A@O@u}k1Q~kK*ns|eY`rhSbWO$5cGj-PR_O>sak4y{%*+ruaYyWd!bdE4XE#$i(FtRmiE`5>p-idlie&Mh?QmXM1R+dMH+e<>9$}`p9#i z!i6M5TlL$lV48^HgKA+p3o?;T%k%Ku+O?%y!mjT6DTlRSIXH(^e_viG`aT<2BC(Ex++x!xTml%d0Z0waP6o%QjEqf(hob=YbKfp zy_&gR9isMEi$8sCsdB7f3d5aWbE1i|w+R57J;y(BI2J_C7i7%^v3^5^@kPyWx&jUV z)>8p&d2`E|HJ~A#i?RdfLi{7LwE2(6~{l)8Wr+CCM{&8SX0AQIE zW}(0i)+>6GxUY5peA;kJS$W0(r=r_E%FCT86Tjero0b0ieM-5Kg#%X+iA0@+0h&Z+ zH0*I;m91f`B3c5|BWE#DU`O*s9UF-Vg!R^OCry_pc##yti>viu`(zL-XYo}y!AfKFK1&K$Vv+hU3{N6 z_UPXXwMZp9g#A1_Y7}qwFQPU$Kfn(Zb9*rM{35Jd{wkXx7=C}o>6*yMbffr}Kv3?+ zNXDkN_T~wIKpC3ioi8V`pvogV@{DgZXUO> z2I>w34Xb@_jj7N?;4Ck2ifjbWDAnJXo?fMFuBKisx*|MUXjbE*NV9xrU|oj>FDQA@ z_nW{K1NhdBBb-2~ormX8Uv)|mK)#tx47@+pYXt2nF?C5hd$3>2%J~JAXI$azUh~?^ zcK1sq(YMu({Gw@5H4iecDoRu2i7MvhYpE~rKmMQ_FK>)0c4tAClIOIT<2&yiOhSRx z?Bqk^OicA61G$jrbWu+kO6a8b^c_`$u${DP;L;O5^F{&ZYk>-N|HT3XPiVy}@kU<- zu??h&H%7jAS4Fk;xTMv5*W0A227BxnV%m3!Z?I@gGdYqxcm?+U0qfBCD0siKG-XOB zPB=z_|CR(|9c5Ug{)mW!K35eGC;W1}ZAaj-(mdM&=Je$#YAqp>1oWbWQx$^5f}R|H zmGW5rEHKGe*XvsnOm29>H#Gh=z{JY!nB>QARm<=E)+9rrbOvpzs09CiU%D_?U@Dgu6xUQYq$k4N{nKQbhh8-fRq>{(Qsw)wvShvsaWrSP2dQ1_Ms_=1-gQo7c zuZtfpS1`W)hxPI=H4g*O_p&uecBD*`vY|eHGqQ7uUxL9AA<2ZY0xzloxLr5R#gmp^ z1t(8dP$Lh)_9te$T~Il^KDu}}{0kf;YFC<>=bBPx+ItTu8Od&?*PBui&05QRRQO4< zAyK_ve-WL}v=woL`Ywp|MX#~ujV6o!mrx@CCB#XvWz=dTUkygh8#Wj~lQx@X3@lB6 z94DMdxdUn7aM%MBS7ZSp>(U_9XKzL;dptevmv z<6)&#V-Xqwr+VAY$?HeUnyB&>bSDseyEKTu_&4=@dNAkI<8v16?WMX<*iBB$PbbF$ z$VitB>|9ADO#NUMK8;b?&AK+i*REzop*71NT5lgIME7k6Fl+}^kW>*yzgGG+g4*}a zY?!@kiu`?J%*rsxU`6ogWZGl}=wqf_VP2B$Sdu^&9>bO=W#g)k2i1zq@TmPv;QR8) z@5$_a-C&>2js+kn-E=`Pxp!aP(kU_eD8;#Lgjujdhgq=o!fmi)cJ>q}IAD+dd_Hf` zy(?JpFk>rVk=H_+f6~N%qdioiPBU^t`QF|S-^aDd%D}W{r2@ovf=kcpTn?-D9nNui z1C?99Nm$ryb}T(p00HOBZ}_9jB`Q<*uGyWn6|b_~_jEHt7q4eQKP1zmv{fx4hxGA!7FgRi#yIIwLTu05~if&l4 zF*b_d;zpyZ@Nhyq?1HMJF1*@VVuPK!bxGEuhe((Y&%lRt1AgBD?ctSJTTC+}#mH#b*`@I~1;gJGAGgxpwYniGp)Sn$1g+7t8Z@$e)$Y zZmV{|$ZwoSp7B`)t0%Dv+c*&)zNscH$H8lNP(jeq|{dAxRo`hhd+Z-2!$@zt_r z1okbdedhSa2Bl`J`M&QMnWbNI9qr-DXzS(iwXv(S-NXGC#lar(8qwg5FaiCx0(O8oO9T{aM|o{WSl8+dwMZ0-hxZrc5x}KX5x-JMOb9n&?Z>P z7++dd=(i1%n+^<20pNrVvxnwMCfZ2|7aiWqv!8buj)H^R6)m37Cz&|uP~JxP(2sl{ z_cbF>Q%<>w^RDmH$7dt0MGZ5t-`o-)cK6iH=5hNy!NdG>+2S6uJQ1kNr{gt zX(&~dR}w+j_r?d~tJgeI-@)BFl#R?JsFBcVB<9ObTDy)9xaDH6MZ)jD(9G$v-WfZa z?M=E28Pol8f2M~?T)VLE1;;vC6Q?tkspH_rDN~x&0FC5Bu8#_vc&0%c3M}Kv-@M8< zI8rwrws`v=2p+%ShR`aN^_f2l=(tsC%rKMiq7afRHt-2aQec0yI~lRRkrI_w;lhha zg~TQ3-Cd!cL1^&KPAu95VrsB_y4U1tl*iM^Ip@H{v({YM*pR-pdmz%5@dpM!)ogqUW_-id zNlAC^YFLAajLt58@jJq@@2n9YDhiwM_V+h~PTAMjo0Q#6)<_J?%INxj=(Ui)ELm*|>TX1JnD!}g@MOF3mhkZO`zJ@q7bO8xbvrLJfq-xL zDA}&y8_O!$2p#rf{=L_;0wz(np!&qKt_MTI)d3W151!LW${lbt9z6&0-eSRyzMR651@esorn z#gSE14;-_dr8TUe?YKX0SpbP`cnoPXUZ2fPtJ+C{LVWyohE-BEcI)dt zJd7^_1mtnaBg!qMk1i%NDyHli8zeWf+)zE)uW4P3W?kV32PAdk4mN)WXWJ`J%8eTVkhFHlB97t%cn-v1OUiGL zLnXJ&P_p&#jAbZ8^C)1srjiasCH#6f*M)Oal=1BT^o>xyn5e?!b^usdX$*n_ibhBt zSK5$PEU&Vw$``fAzl>_v7e;nQ77uSfr_5rWcKE_XJV>PYOnyGlatwkPu;1YgL2i`~ zNj^$xk^Rl(_mk!t=pwU;p#P-wTh7HLr1m?wsxc`cBa5oAfPSI7R zh`VsL%2z@lLS-p`n<)hZ$T%SJN_EQ2sYj+=@5UEhh!Rf_jAjK%1$k`=*KYhm3ftN= zZD@`-HL5w6e(Pft^=7$4?|>I1A_-sBa;kw zPeH}k;R_%01O9q`$*JVN8iszw##6U_*rndx#$mzkFRiZLhRK9*8c#ioB#T@o&r51t`oWvx#plb5PeXh=vcLqu zupoblR}F_O=Zn3O?xV!bAsGTvSfiPbp#q90T&98jH>6hLF5HKML_NOso4Frv$({`t zXL0s?c*a;Z_>wW#Ej>fQRZQ$2y-5UPMA)raS>`+1v?HC-zk77+8uJ=CG|pNu?Be#g{tNEP$G z?@8ET8~ehqq^m|n8RA$oFH)vY3(n_T4c0r0Z;smV1MYWhkw-juDVq}(Wp7jL;_|hg zCMctvooM$R@zWYhx`tavs_WrzzKSf%+E1@`*=Kq3%gJ8R9qwDS-u-m_QCK7!LJIfM z#;QG)ZvZ$@YbTok0x@DudO2YIHt|IT5v>^9ew_hIhwm$b0`45D-?=(Hj&7zbd80{qy3 z0@wHJ0z@ecP4$|wPWLC673F;dO}&S_86>S44zmm+%xtfs==H0Hw8W^fgoI!R>NLDnzN48PStTF zbyp95#c?GvMI|y~+TH8UYKNfvEg~A1Ee>FhCwPiHfIDcpQppU@H8@icI&0j*S+=wf zay3lo<#9}zoHU)>8fu?y(DK$p2C$XgcA-m6LSGJ}#0bFA6R85mjZC@{KfI~KrXZ^d z@}8n2$bQigGm*;JAXC2Arw&2_FTcBX4zCf`2G3+j9eoM#ID6__R@!8o`4V4?n$>o? z)YfnE2cVS5OJ2QBVmlYK$#Fj%j7X(kGF1vp3}f-XA{fq%nzkPds5D4T8kr zp<4i7nPIc79QI=sz?kmqL+ImFB}vkjx?d4+~`{?^8g(RR}^aPIeTqfXPK$V2Ikxl@(0yqp(?m3r-sWTC4`$ zwhJ^^&O8DU;t`1!mA3rQ6P)(vpLiNrSh1SVejAxybX3&Z%{vDgS5fhMSm7sOkY$Im ziMK_8ZYEtBle^m|>^411n9)d~1M9HkID4dOHS6;ViTbKPhHd7!&e1)uSrYMBTc@kO zS#pS#RJ<)Qw8E%hTm$U!_C0-1Z%@ycVhXdrmkl$XMEaG4o?Zu*W|WHdrMC}_Gcbo~ zC`%m2R=t`EL}bbVlC*7FpuQDbQZOBLjTZk`Ci7sQg#n-H?RF|vo=10bbYH#-y>89$ z-HshB!n|R*^HKO6B{C3fvhr2gZp@#<-c42^9yh$-!{rS4MjSQ3vfc+DUCD_g39;|i zN(VCEPM!oS!9P(Db$66*JViv5-@QYgvx_9f`|Nu}5BNC?VxgM2yy(H30ZZ>0t*$|c zu9&X%K=MJs;XdsUZh^W@B$BoLh@iSSRN-X%ZQlHNC{31Rzq;NJ5T44OlO%k*dZO=8 zrjK6C*aEMzj^N#((|@smQRox~%a8iyUO}aUYKc}p0bPro$%=G!%k4K!}SLfP-nPiN|so`Y%*9}u?AQ6 z9Hv{3LkyGQb=x^*e-ngQr%9gjf5rgzT#$_+yJ1UjrJFw?E5NLoxcSE=E=QC4l?25O z1s%Lj)DMf=!zdry*gU&_wJqa!w^Gk80ACe#?dUdKE1S^}m~#OEw5eVGNa04R4vNgf zb8Q1!Je-#*GsCj8AIzMi(aKfwD>7iF$Pg_kA>c7p-X!qEQArr5&NRlh6a!ag z3EdxuU;{>>i@lbM1YlY{8wnYN_2@S-vp80=aLmuZMi1^+{8u>99wJ&Ftx2={KD@9? z4uV~5Li3+f+Sn}SkdkNV+ci?KAau^)a7OP%>9kNVO(s>cD!UKngi z_u7~N?o@!EB`)8$+?iq(D7O;aU|?vmYm+kJ!>MOZ{^wPNhI5_DP9Z!)-Dd3I%qxy$ zy%K_+SKfWm*yD0p~ z(th@_o4re$XrE%|ELAY-6U`J+DyzYzF9^Is(7yBZHC9BvRiM84HAaY75_r*KX;xoC zq~_NGLEWi{exkl|7hKm|(r%ABkJhF34j0*ADuj4}gKG=H=`9%$w&zaRY@>m6>$@X~ z51-AvEbt@u1~mrTVtq2P2^vm`eH7n5d@?a5i?&31Ye7(%If92XVl^04gIkuP8-ZYP z)lDJ2^JEuim(C6G*#!d&xyUQMkD;n<*P5~0;|htH6!ax^|u}`C>+{4@7r$-jj8cMg6 z;E+BAf0|&!V4p6SaJN z1{d%5!!wm??ei~k(QzL|F~t5oMnzVf!s)bYT`qeeCR;YI@jf*Lc zH&=xQrmcL3BqzR+JZ^^7ewOdCKlLv+jYn@x=)cAT!5L|);2vW`$ETf0dF;EUY6+2% zwc;ApZ!c7v9tf70$5N+&@JB&sJVCNYlvCo{_onK1%+8;Db)Kp@MxN^pTd-X?%sWpt z&bdqp+`pHOL@_GP^_E2hSre!&%!iM|{CX|H7Ug|o$E9VY8m3c89j7NCV6!z_<=T2(ve$f@Gc*dYvV z`arw3CYm&9A>o(rlG)+_SmvpXDg^OJDG>*_hu;#)lYnR=>GrJi*yvW5vYj#q>tb)D zDd6BN{tjDQ0MR#?1HV0pwJDFSR(=JT{7ORT)N5{4A>8O0uz}9IBr9%;BN6_N^pc@GzOF&)R&Cnz=f1(OONw6L< zy!!FSrSQYAjSkgOx&JwDB<8t-kX;+E!uw(GvET;Nli8ZtP%fXkU*~V`D5lZ9;cq0} z@o$U~>}13R#Spm%NFbj|VW$K@c?ARB+o&h+*`uB2mC~TH?nA^KR_t zWc;+PLn-H(ay}d%Q@!zUT8M&KU#4oq?2Ch;xu=$W&0{T|@*ut!?0H0&eUdgwA zpes+75$|z<3|&t9TFkTCxd}7nW7geL=avgMq`da8FV&8`8RE%+qrb6{yeIx~*&$rl zP@RqUYTXdv1@CNN%9b{d1O{jt=>@X<;6yai>{}{W`|q2@eDUtn*H8=0cu*ZO%3TZW z(L~fIhn}*xKpRO?C$~ZZ#;1aP)F6lqzdggr%q#E~l0_ZfS6&)mg)fj^S~Eo@g%c2) zN;aE6et`{u4`}UyT@~#hhs$s4)m@O6?gpP%K&(eDWmbqyV;P?jm7b%Sb z+~8wr*8- z#euWo=z+&(BL!2fW41f?4CXS7)c0g4d^fxl%%y*?)CO;RxnKS=6XIdzeg31&0plE& zewtqCeQ2LZX?(C*MYuC}C@ug>`DZM&mFM$q6Cs9K+D2a2OdpD^$#rMBhO1CNLzQcn z4Od4VM?uu{O-9s<+2xlx?~sBm+rEmFqjwcxb}3s?W?lsG%Re;&7pyGay(X%x<3zik z8m}_>OU$w5SA7=(+@Ud#)!;a=scPJisOb4*B}hZ}%L8xqIgNmarIKT8_^|u%%q9tE zNK4BNb`3U>`F0d~TQ&JFCgiUqK+>BtM?oLyauk@qZbbm-LSm_}hA|ub4+tX&zK8#8 zvg^4x&`oc8DrZUclo=c;=D|Y)MLwLCRMjVATPFjf?R3ELH=#qtK(EVxz?a}F#Av+Z zAEmg!7auT@?aO>wsy66+gO7GBz`v!EQ#W1X(?{eYz5zj4&DT0&o%4%T8U6=EsfNHW z(A1lK-#35PiWtz9rhg7WJ+=G`iHMgD@Ora@fW>t206f3c=A-bjrU7{^=$HNhYsSzt z9OSL(gcOq7kB7s6iQ3H$q>SC)zXj3L@Jns%pz-^8qy&H`PEwP=5x>HQGhPa~{r+|j z%9^8~i*&He3lXF&0}y(oKd;_;VO+33<9-J8IdhUYFw=n;Uqz++9>TUw{~=9-pGT9Y z&TiepDj~J$3C986N1`7K{trhs2)`783n9mzrQHSK^`(Z)IDA+bF0Q{1nuN>gC=31a zu_?y_1f?k&Qjh%KT%yhKgzB>pF9RRxLQxy%3H4T`xzl0;f4d9*drnGXvAUObSsf}Z z2x5Ni!zFTSGN7_~-d^B;^ZAWKs0M@AdK1KJ ztT=I#)DxYgl#(J^JjQ4T+Q3LOn*Ypq39-hcv38E0@QurU&iskKChevT10I|KLK(MB`5i> zIQPE%v4Y?z-{PSu#J#-g2P5lFJV*fws+gi`OV=oALZ1JGgOn|&=|0jNmVswjN$K8$ zN8QiEck)JT_r-srr>+TSCp>>UJ=zhv>p>12+m9V5{clKYan7)X{;m$aKO+*Skpr;| z|15e!I--}Bv+2vq;zIWhh#UMe@@G}hjXj({zAEMwI4z!7N>^V($k;Zas%qido{_SsE zGV)KmAUwg?xKjhu=MRFBlVJ9uK3Bq+b)Y2`|6a9=1!K*A0nI=4b$lbINduXhea|D2 zaukBn?qw8Pa{xb*BY^Ri!EdlhpkEHn%qeAWM|fE;DgatR)v}v*A|)7lsJ}y8NJHGN zAB?bLM1Epd2I4Yg8=##an2qp1^l6Z|j6%yKFlo?^9aV@=3UGC0K3xcd>~Z~1N^99H znOyetJPJ*T^T;>=tuLF)ATL_K%70U&#s^jZ@Zp#vJN6Eom7E9YW|RneV>_P0Nn(NF zc)oVHNY8dVE?Sm6UbOWd6c~>v@I1-$~!cV{_72i~PpP z{u&9s!1W6q`oil;Pt%MS~A!6My-z2Ld^=@cNwnY)`@El24c@*PmSx&yv%mg1rBBNma*H8wOmv z3nHjP=`DVV{QU?>ub{>v|GDcp@LoddOECy)i+Hh0Fu9KZf1P-ih??HAZsQhxR}SLG zcMH%RgqHOmJ-qWW`fn$kS>yN!)=a*2u|>)|s%m2cj#@rJJr_93=Kpslv_K>EV3zEC zre9IEH)iSu_`cS_nu_``2Vn3iV~L~KY?|ECNoY$QP+_wLa&W4R4@3W70n5bP8?xVD z>qKU~B?Nx&sq!DSHNm%z07{IFMzgWU{d!bL{J7~Fp~Qv&FS+$t8(5oc!T zDIE30^WSU+p zbS48u9FM*fwt2T2&WE@C%`XW6@4;n?o!5@Y?tnF{}?`UNz_oECPuS=%p7r|KE>+@|HJ}rXXqG;j2@=yKt`_pr(|EE9KJW1 zpPjRb3vpG@0F{fq{5)x!2NU^~e-eN}K_L-CAAih2HXI)8ez4uIv<8M=Jf4{wUV*gN zVE?tjAXxhHRJDS__=cw|F&4^o!^Zv>Db_mf90-|!(bHVeUE?ve?2ky<kVs(-8V42Z?5uiPp{>?g2f#j-?=;znadYu$5^f zOoMG=$pFS!Y7M41f?g^oWHYi#lPJGmH@*XCnSYgRzem-?*L_1yAM|9^SfIjU9Y+b- z!zK?4lx|9bXqeBY^956d=A1%WiT`vW4He&HaAvu)mK_Ffr&o$oJB< zA7Tj;)gmBZL(DJ{G4WA9IouaxM}Lv-(QnyF*xP4D74*)2a8;#yN@nwZ7KF-9SQR8Ql4_#@S#5m}ObC`Y z<{Zn!FjEOa6RSDAL*^Z(#V2UiP2w9)QrA(aLybb&W1m=UbGj7&=PiOccu5ngjuzwM z8BXymAQ-i_%|-VYUWNnoPw}(1)S8QbBuZ>}XrUr*x6$+?K2}J?ArK5jRW&a zsY20h6xbjhg7CHt#%^_5Zxhj5JYxxa{AaKNcxoWaSo)!hM=o*FmzmL+YqRZrr-nY#EIv9mO5f1)P&^Y@D^i>!}ffE=`dTg2g zg;C7W{JzyH1mr@~5IVpBVW42I)e&c0n?;p_5s&Jf`)EOavw_-1;T+%(s zoyrgi&_#-CgZWFEow03ZRK~6~Qq+8=2VdQ-Rz3Y{XF31Npqy@6mfrgEtLB5h$|)SA z6;i{z%`!tnkH-tXYt=!^X!YoG`02*egl!{%Ol)fjm3+NwFo!s<7v?`E@E;T1H)~nZ zg;(ID*3RB;w=iY;F$?e67BMvr?TX$g6i2TmpW55HIVm;%(Lb|S0+E_*h-e)MdUiu zMqSb$B!3bX$f`+Y?AIm^W%XhvPO#Coc*^n41PB8GN!*efD`!^<1?6VhiVe1`&m|3fL zyE)tsI?`liSKkg(v99;4-oUrU6eYZ5$w5Bf+-1l?h6|IYYxf>gh$KT03ELY}uM@_{ zL)p)|?OP}xI)?mJS3~q@oK4=!!ES!K_R-iQ6>jz4v;bRA%*Nm!+Qac5M4q0CLl#D^ zpk-21g5nU{V@MV4*6T7G!0xhbH*H!O{m5DP?&+CLOt0GAsri=<2CZ57|8X?hS-4e* z*=emhfx9KZSV%FU|L>XVEff071g^bB4)SU6URp*!tk^~(ruyI>cwk?O{bEncS%bZ` zR?k)g#_rfCAtCkqAfF_>aO1VG5-BL%6d#v(O%0_!8b81M2 zqh8I5qIL3h7z*UA3;NQ>(xL2LUe`1I{~3?#W}42LJJD8pxbf>}US)l9duQlo`kGE{osb7*^nf?He?` z9Z7v;c>%QC!CXnjsloK=q{W(;%yU)RJL=eA zN3^X3!^Tyt--$WdC1zQOXou0?4ov$a5MNof-eEXZ{RP&fX&c{-YnOv+B#tjY1jncQ z`*YPW`kz6ITv)U1PxmlJ6~TtzFX{Oo4{LZ!7>iB_xa+=+4U*PqGfO4hcPMAjvZI}A z50~u5QGN5lQPKZp6}7YI`ZnBF&CK{oN2cn9x#0ac-pm!um{FfQ{LBaOJ@?i{m)^$-&m4B zeX5q%$Y@th#GqZSEYnQyc}+uuuP>8@kwq1I!I}9%7Fb+lZ5sPne)(W*)C=CguSEw& z2*JgkD>f8)3i}f;Wn93gGFE{s8w2O;H$jeJZoKX?=a1j5O=3Ode=L6ISxYj;%-4_0 zm;k4WYh6h{DUzo+4Ae#%N{3ODr=#oR-0S zIurI|wJfhImC4D!f3+;X%TkIymk)zqfdhPf`=y!%tfN@$5#T~9)Z{D$3`@rjg_T@) z7j+IKEmPr^ssp$Do31z}4gb6U8tJi<;s+ri!A<2+l8^dwx}ygkvI(U_?)X*=iS!yS zvk05GQ936}bCT_-B4VRC4-CIrtHd(OKmUDA6**TTw5JCDirMh{@@f}n>^bK5xeEXQ zi%nfwN!OyecR;Yt;^V?VoY=9{kql8J6+eL&)nkpm21K|T~C?F{hifhZeiHO zwej&??%l)m#H%^K^81qzl`GAU8?HUvT=KeGE05Zom|5CCH9vQmp%MbMFFPmt(R5c6 zk{_f{Z``Kg;%mGe`(jBfO+Oy74NA@az;hFA6iO=OM`r7r88SO}>w%nv5L*Sr_6c-( zrE^XZI7Z|m)P@|)CKbdKNB_siJ_V|w9G_4lmk13#OjjNcj<`0%K68)x4@!cz^$@dZ zZPfgp<$u2$NB)zKR^YNx*WW_Bk?sEOZNyQ}*{4`wp}Y70BnX}+3DOEQ-OTL0Oelwl5_X=(NM+Vr_L~48gfN;UuduCa3NhBF z5Qoe?Qy#wj+iYZl0RHTCLNzl?&wOV`ev{yJK&b7CniU>=J?m1?# zm3@^_anYYlQj(1vu|1KoeOvs}=r!HFMs@fDj6!-Te_K8=0Tbh2%WNz2Dkq4v=mnie z&0)d?lEFc4K6%q&G4)&J1b3!77a4Q!j~x0FSM#mwTBk)qyrSPuelEPkE@iGlQx6^L zb!r0rN#}VY7X0dr9##DZO41kUZ!W_VC{i-3)lc^AKQ_Z!E~6#xKN lS2wF2Si%3`VD<|Z;7p4fvYmA03;qO9zkOf%<4wz_{|ADGxHkX* literal 0 HcmV?d00001 diff --git a/brand-kit/icons/android/ic_launcher-72.png b/brand-kit/icons/android/ic_launcher-72.png new file mode 100644 index 0000000000000000000000000000000000000000..59a4d66db3c5cbd203d23ec3a726ff939ca1ab82 GIT binary patch literal 2414 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 literal 0 HcmV?d00001 diff --git a/brand-kit/icons/android/ic_launcher-96.png b/brand-kit/icons/android/ic_launcher-96.png new file mode 100644 index 0000000000000000000000000000000000000000..02d864c68b34863b5e09bd9c2c3fbb3fb8d1858a GIT binary patch literal 3283 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 literal 0 HcmV?d00001 diff --git a/brand-kit/icons/android/ic_launcher_background-512.png b/brand-kit/icons/android/ic_launcher_background-512.png new file mode 100644 index 0000000000000000000000000000000000000000..df0ab8a2051e313fc7750092639c24d7c9d8aec9 GIT binary patch literal 7124 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 literal 0 HcmV?d00001 diff --git a/brand-kit/icons/android/ic_launcher_foreground-108.png b/brand-kit/icons/android/ic_launcher_foreground-108.png new file mode 100644 index 0000000000000000000000000000000000000000..51b681a3b107b79ba3a3f6a9c9c1751472357bcb GIT binary patch literal 1918 zcmcgtX*?5*AKz>kC33XoNK$TD=8Q0r968FBj7=#=K91=@fS|N43H`~I%a_xs}a;`dFpx3v%wloSL2078~m&8{Ej`@aLi zcZeBxUHSk3euSkN(lH$PEnnEi$w|74?F96!qW$7`u;vvEj!*b5urHs|V-_A?CK3}Qzw~USI}`I;!Br)v_yiLpuJBeop5j`JbU4EE z5}x{61l%bql;$P^OR`i;It?fK*pJ3jlx)0|!EtZR5{gfNuV6&2eJWZ-N3sYzjP+E#%?t$IDa!ac_S1e)j>h5a0du*{75+N|=^^or^+x`kDa`(EkxCqRJ z&4ldCGIO*SbLqrLG?tUKca6q>mDgb;ttU8q=1+DjOut%x=sjs8BA}7~3eN6`w(8oc z$hN7j2{}$ebV3eVbpzOS28PpGFovSFYf+t@(9tA3t z923yhg5Pvq_7j}N0gLLzW-GIXVXbWH(aWRJ~XaO!6z*YoR6MaLR9LhjFr4%cAu5% z7A2mpub^p+&k~PBxQZYs6x_|!9~_iwRi=&`l#z3{)Yvh}(m|H>ZnAu2f?EEQAhv4p z*<}J4#h9J_`cxP^_;~IWN~GYv%kVrS)Syh$$ii==NG34Fu=24oy8N1eWKip5-i<&y z$1Xj_qi{V@K{#Y$PBK?1tzbH;Wio4#`LW!FBrc5YIzS zR`PlGW|exw11~-pvu^YYrZaM_$zQfbHIsoW7a9ZCh6u`RaFj*Fo0n>cPZx~!hb~}ENtHJVv|$)cz3@`I!}A>Fe}=0nl&$W*lSPFS zXxchDP3+X_IM7#WX;xR?gDkuyHp{H^p_9j*OpumwNcqbUcYH#WOpv-RH{~RH)@;X& z>>Ge2zL|FOneZi3Cz^{LB@-I8H3qal=6OF0B~M|JAE1_+clB$EqBC*ORYL}HXRsfi zdq)L!sm~FX{mto#*T}UOb&Tf<*5UC2pSdYfI(_2sdY3)=l!36C09B8rpwOftWCXPw ze4FlXYFR}8{K$#q9K+tfRFmBfvasK2C{|SSXKlz{IVL+j{{U!_;0{9YDj8$L+cYzd z8hm#{a$xxPN<^x5ZCB(b=OL!BR%KmNDu2D`OQOc`s;3%b@i&PE01?KhqpB}@KbX}O zZVTHLT9egow$-pp+jGgQU7`TGOzXoF9Y<@|axWCybsJcH&C{)vF=fpn(l;Ukp97tD zt*X@(eTzkQ-OEIb?mQJ4uBoXUd>Uu!bF~)k$FQe!AsFro#Iic41vM>XD+`tX=H^}B z+Mg!#%ceq6I+jnAXU8!W)Ft560()pKo*mB8tea`?CPWrS{<&nn%Q|Q7g+*m=qb{d! z_u_T$6ihdH7=EmG=H4B-Nay3t|*@4Zfs;Z3D3Rg9^`Gvpt_aq{OHb)rhbRNag zCvgO+7432#@^T~Y8v^vr;aaVh%ZxJ>GkrsW-H|(6+mJOM4x>YJg{8akSF;8(ISykW zPns3}K6kZwq^}}uz(k)&Kek07cJ}?zG9GmNebk34XyfKH&UQB0?nB9|qMAC-oZGK~ z$6|toj~bRn=v#|{8eMZd53dS@tfW$6X2#n7D9?Xt4C|EZlwH)NKyWPm`r(*Fz*R-o z*b_oZSI>dMvySh~9T|8bW^fcw@p1Q3;)}ZgPACxvUs({nd;x;v;j@;5;Lb^k*Pn(9 z5?lq5XtkjiWxR0?xgFObJhxN%%+(>dwzEW25ty}>k{=TNzt7z(4|v&)m&-GVrD!!c z&Ll!?JY%#6@c&U$4Yv6yhA|D0HbGBpuaMOsw0q9}*fZdg^bDikb>TzvJUB=-U7;No de824z1laKTYs+moiuZ5=ur#+dt2RZ){|l@NohtwU literal 0 HcmV?d00001 diff --git a/brand-kit/icons/android/ic_launcher_foreground-192.png b/brand-kit/icons/android/ic_launcher_foreground-192.png new file mode 100644 index 0000000000000000000000000000000000000000..a747b62fb46cd0eb81d15936d469b32ba062ef0d GIT binary patch literal 4752 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 literal 0 HcmV?d00001 diff --git a/brand-kit/icons/android/ic_launcher_foreground-512.png b/brand-kit/icons/android/ic_launcher_foreground-512.png new file mode 100644 index 0000000000000000000000000000000000000000..18432daf0781bf31503f8ebfb293bbe9444da9f3 GIT binary patch literal 20703 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 C8Q<^# literal 0 HcmV?d00001 diff --git a/brand-kit/icons/ios/AppIcon-1024.png b/brand-kit/icons/ios/AppIcon-1024.png new file mode 100644 index 0000000000000000000000000000000000000000..1721481b083dd594c2f8910cb4f7d87807c1dd6a GIT binary patch literal 78832 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�@&=Djb ziDR^Fij;nkc+7ZdXySiLXn;8GE+={(5?lSND?zqea^ literal 0 HcmV?d00001 diff --git a/brand-kit/icons/ios/AppIcon-167.png b/brand-kit/icons/ios/AppIcon-167.png new file mode 100644 index 0000000000000000000000000000000000000000..058fc2e0f860a639507b0ebdb01b40c733539670 GIT binary patch literal 4316 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 literal 0 HcmV?d00001 diff --git a/brand-kit/icons/ios/AppIcon-180.png b/brand-kit/icons/ios/AppIcon-180.png new file mode 100644 index 0000000000000000000000000000000000000000..abcd3ce1b9ef142af2a619feabc5683dbb502133 GIT binary patch literal 5458 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 AUjP6A literal 0 HcmV?d00001 diff --git a/brand-kit/icons/ios/AppIcon-80.png b/brand-kit/icons/ios/AppIcon-80.png new file mode 100644 index 0000000000000000000000000000000000000000..0c3344a65d0246480551d7e9f59248f8ee698d4c GIT binary patch literal 1510 zcmaKsdpOez9L9gMHJ2u1Dx}k>G^B(%E@7BVIcsid?w4A+n9Gz3vn9nrMwl5BPFjdcq#QlxdCni_InVjyec$JKpXdEP-@iV|1iYi7f`$SB0E$jH ztm}Fben<|w?l+VdDgj^v+X-vyei0|II(UDXPv*e}DMl?0nA`n?~*9AL>PykI7Az8uG ztT(-bJXI)iXa8q+b>q=zEqCI`*1YX}U>DIbYY~^Pho}xbQ_oo&bGMrHGj&eGaiV>F zBH%TdzxjTUd*!WO*l>tASX^c~oTZA28`Hpdv_-Soh}pen5HeFEG$)y-lZ?2#s_MqsGG2<$^DnX&6g3q zttaP+1E#269JH=7)V#xa^vF2qW8|50x_rL#^h~Z(&n>IT08>`Uw-JBO)4F^-g(gv5 zJXTF7kR~T@mF^ZKSKD-*pEx%$JA5HRI@hp0@~8;ihpAfq@&yAnvG$fhAHAPYmA{v3 za7n*3y#}4_J@M(8QJH+ZfvA_r-X9goTnHNV@&oRl$Z72nN~HD<2Q{jGDA1BRJt= z6;#?`n!u&Us!UHuF?Yd^)4Y{ErEXSeJ9ew)Ng|PQ^hs=V)y>%ZPc#9~%OkAP7+na4 z-RcIF@f|)+!P8XyyoX5hh@e4kA3o`JZW@)_fAN*Rc*uL$Hk6uoo%4|*4|*{!Q7=;Q z3!@m+Y=QJA)}EGTQQJt3)l)!sG0%h6Qi~x42Y;x*bEWS*+pdT^H_aIN&D=%`o;Rq5 zIat03zh~aOoN54-6er%QfMg*-SXJo`T$wv`n);xmd(YFmW(|jUSHWGR0nZm0x^k|4yC7hT z#N2S(4*kRpT~O3zU68homOMcQtAe@K1Iq$3APlimEL3~BJKkCc6eN#)GpW1x-w^vR zV9j;Huz2D~xIDbuP3G@AYhVCaTZ5YD>|Uoe^arNFKvO=nsog=(Z@mGW9Pn7KUBH#U E0JurEX8-^I literal 0 HcmV?d00001 diff --git a/brand-kit/icons/ios/AppIcon-87.png b/brand-kit/icons/ios/AppIcon-87.png new file mode 100644 index 0000000000000000000000000000000000000000..52831d89c9afc585855027c224ce51100d55c766 GIT binary patch literal 1722 zcmZ{lYdjN*AI9glnR}b9%)M~jk6X=@6w~CEPa88P6Dbbc^mml&WD~hXDEE?+OPHwD ztXw86w;Tt#bX-<{o6ZJ|^^1ywKc{39Q001dx(Wi0OrB=#lJ~&@>-l&*!@!_sIgA^OpUjeKeLm(tC{EED0 zChWXXxmO5Z&Ac)z&P@DfJfd4tTVQYEb;%ZG^V-R@4*))J!#`i;D7*`pqbsxFB7Ju* zdUP)S+PzB9(C7Jq>bAI;()4AD8NiiV7z&Fl3p06 zvT-BHk?4r9rVGc$_34e53I)5u(*<{apuW;gtSL$f4Lt5i=rR|kpee3?T%2uwTjb$0w zu*ZXwX=tRHJny0iBg3j387|{Ami7V75EhY5;J~`|-R!Csq?$eLQ#zfEyH>zm6|YWG zo;Jhif-(nG)UU04mFbn|eDBQ&9S!2HA8ma;f_g5@9(@z&f_78$RH%3Z(^2ofI2KZ! zS_VG=G-k~O*4!6_NH(FrW9n+h2y@Fdwcbh@_25FFFN0=f+!H@ee>lCQ#heU0(*flO zhul*%ELRg{+_BKb&wTXu=JIUYhDXDr zCAzA?7XG&Nodh;qo&V}f2EOg*GRey8!< z`Wrt<6hUmWilB#%cPfDRMv=YC3Av5DQ~OT#u(_&s#x%plhY)x>sFss6H=H${W6IuY z-cv&a%-Um`{JZWWQ@Lcmb%EA-wsJI*KiYq+w(PD-cPFxe+a560%Pc)kj+2DQ{=~el z`k+pue|P|%ub+>9cAj7CX&;M|a$8|V-mtaE5bzE8RO)B8J%Cj^QAAH!3Ge%X&x`Lq9 zq&+{&m6|MU;e#(9qp3^F$2}uUzPl~-=}eHkM>ld|aw`hblly$7#z;Vl$mE z`YPeez~TpiConZ1{Bcp74=91aWyg&&s3E#AZ3Liz3)MS8Jpr{(Q6lf%UR+M=S~z6) z`0?s zz2PmUS!U5REUIcsK&#R-CpyV=x5hVu7VAGnrL_q}G8e&HPGE_Vm$n-8(Ghc^tU6S! zUk8+Tw1HO5zyJ0grf(6aZ)itMKlGg@o%W^3#`3W?jOv;$j1Gn{kYtSg5qIFq%iDqvO)OQ+|1{=9>XMz!M|klr6qSZLslL; zG+0apgVkzv2ul$1a-6#bAJSR=|6q4!?@0jwdwW;4My>Z72>4I+$N*T(as39|5zBo7 NIP2<-W;&D7{s!q}GQR)- literal 0 HcmV?d00001 diff --git a/brand-kit/icons/ios/AppIcon-dark-1024.png b/brand-kit/icons/ios/AppIcon-dark-1024.png new file mode 100644 index 0000000000000000000000000000000000000000..bb67c9c06d143aed951689f98a8bdbd6f740070d GIT binary patch literal 82846 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 HRkHX0YPwr+ literal 0 HcmV?d00001 diff --git a/brand-kit/icons/linux/icon-128.png b/brand-kit/icons/linux/icon-128.png new file mode 100644 index 0000000000000000000000000000000000000000..9414a9bec639cb4b33757db1d1b9a3c2d378d145 GIT binary patch literal 4795 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 literal 0 HcmV?d00001 diff --git a/brand-kit/icons/linux/icon-48.png b/brand-kit/icons/linux/icon-48.png new file mode 100644 index 0000000000000000000000000000000000000000..8a899bf26bd05f1836c120cd72025a1b4cf61c27 GIT binary patch literal 1347 zcmV-J1-$x+P)B^5)~1lVue(fjn!;K(S_nhsHq8} zsB2xR)s2QK+C_08Bu!F3h^14>1WhOAdVY824U@^t`Bx+*B?tjw`|M zLYu!Vc4OL#eNL=>Scx4{>s5;`>W3Js`fN-SLo66zMUGj^%mSD>X3-Q1%VOmXO*pL9 z=`{=nSYU#k6)2N{mD1vAX3ouG)=UZhV1iAK1lLW&sRXQEUpUO;HdbEQ6ukota*&Ih zY2XtDusQ99`Ch@cvs5%Z(=|*^awkQ_3SfhYZq6i-9UIr&jzdg>wgOyWb(6+~@4}FB za9rA?YAL`yIt^?Sv-{o3#c^tpd5ZwIdGyh>@cncqH;ubUOeIjC(2 zZzs>y^@^i-{@9E1*&`=q=IE0`ozCOu-=*+n!*CBm>3Pr>A~*-~VVyATMqB>-ug2!K z>u?W@wMVib-8yz!vRE@V%PY>B{o~qj53G^9pq|0r9k?#Zy6;>JVJq0;@VnU{loS@6EDR)kx0iURqkxvB!6Cv}D{X|uc zj=$`n$v=zp z^WT1u#ohTLVAa)9-k8leYldMNZP@B2CN6FRGu9lR&|XEra)MLG!z>?c@bQU`F%klo z12JYk!Kvfne>be#F90k*^)rQEQ||gAlS^4p*@$r#Vc|3M#DoOKxc3JAET<&HFRd9X zm-IzuE~E{>Z{gge6v9w)xj=G(nwI%oPQrXndcwR!e}yn~#FyT${uahkGIu>A5`=AS zedLoUJXL9pxe!$3#y6?FYfkwFi|5p|T$nl|m)|=h^Jia@oA>T>p7^** z?Z&Zr|Bn}?^1z2}&5Egcrz_wd6pME(sQav%m+PaEryUN2T}5wE{i|ayXv^C*3wNDQ z1vK)p-o0JXaR_k?;HZzf7prl00_;C)GhRR&Kp@;`4MSe45oaf$|CT%dG-BTNg1ieL zaD_9eoB%2}Ooe||Q0;na9 zaW@H|*97)i%5Jn)+_nhl-FNT@F~L~Z2~bXsTNAM@0vPWr-ZmpfPEqd)Q!b8E3o%&(fV$lPP61Bx8{w# z5(N;==IPr%bT4Nx?VU7okc*sY@}v?F0rl&9qGHx>=T#o{`6|H#n;cm%GYJ5=s2{(3 z_Ya=5Yik3d8%+DQv$STx01M2bVk=&zE3<$IoexuNFdjEE$Gd}B6l100006NklFJ1pHu(<002ovPDHLk FV1hxGi>?3w literal 0 HcmV?d00001 diff --git a/brand-kit/icons/linux/icon-512.png b/brand-kit/icons/linux/icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..3ee9d273f23bbcdca7279b1a33cc759be621b135 GIT binary patch literal 34488 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 Y0OLQNj<{VerT_o{07*qoM6N<$f>93`5C8xG literal 0 HcmV?d00001 diff --git a/brand-kit/icons/macos/InterlinedList.icns b/brand-kit/icons/macos/InterlinedList.icns new file mode 100644 index 0000000000000000000000000000000000000000..9ba7b06f8b88d0b65340173503f9c035491ad153 GIT binary patch literal 113440 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

*pvey}F@1JPP{?`Ub<>)9dfNE{6IK^( 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 b3mL>CN>!(m{_Y@n0x4fozgTeI{PzC=>8{XW literal 0 HcmV?d00001 diff --git a/brand-kit/icons/web/apple-touch-icon-180.png b/brand-kit/icons/web/apple-touch-icon-180.png new file mode 100644 index 0000000000000000000000000000000000000000..abcd3ce1b9ef142af2a619feabc5683dbb502133 GIT binary patch literal 5458 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 QEC2ui07*qoM6N<$f|2xAz5oCK literal 0 HcmV?d00001 diff --git a/brand-kit/icons/web/favicon-dark-32.png b/brand-kit/icons/web/favicon-dark-32.png new file mode 100644 index 0000000000000000000000000000000000000000..557c063950999e71b68e8563297d0eaa97a7cebe GIT binary patch literal 465 zcmV;?0WSWDP)x zMJjDXuu$gUp5@tF@&^(E_b|76^Lw}R^Z!?vD$o2569pRb6$Ataf&-)Lz}(>~arho5 zUJlew+ooTyeL(;w5QCT(UIHF>FVn_oXhk5;3su0+!U{>~mgiHRH-rlkcuh7}1&nuY zNJ78yobnJ@ER_f@xR7ni=c)i+DCJQU86lt5CF%A+oGPH+Y*B4}gAh5EzZT8rFGtfR z!X-GdwzoxDv<`MmjcZT^oINCT_0%Io4%cRPlYPqKdZOzpH@qqUPbR^v#4uK*?ajLR zAS?9k{0>m+f-1nZ$ntR`q5b<0Gkx#enwWP`1>n6(uuX8UT8RmN#oO^LxnS$1s0xtm z2WK}zo}8ah+!qj)kQP`D2M*8q-137&F9A4kusNCn$SsFF54;4R0UC0E^MSVj zpJvjUARssp9Qbz!q+gG7e*gdg|NqUHX}kac00v1!K~w_(i{JU7Zb0GF00000NkvXX Hu0mjfVXMY# literal 0 HcmV?d00001 diff --git a/brand-kit/icons/web/icon-192.png b/brand-kit/icons/web/icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..10bb18f8aa309de141f614751c5665bf61fd1251 GIT binary patch literal 5750 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 literal 0 HcmV?d00001 diff --git a/brand-kit/icons/web/icon-512.png b/brand-kit/icons/web/icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..cc0f720605068755d7069ab75f7df0c1e0107414 GIT binary patch literal 26592 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 F{SO8-d945d literal 0 HcmV?d00001 diff --git a/brand-kit/icons/web/maskable-512.png b/brand-kit/icons/web/maskable-512.png new file mode 100644 index 0000000000000000000000000000000000000000..dbd64ec4844967202f1a41d541c4330cd0c2cec8 GIT binary patch literal 20105 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 z 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 Ck@Y12 literal 0 HcmV?d00001 diff --git a/brand-kit/icons/windows/InterlinedList.ico b/brand-kit/icons/windows/InterlinedList.ico new file mode 100644 index 0000000000000000000000000000000000000000..e806c07cee4ab79b94d8f2b15eeb75cac93239ca GIT binary patch literal 11975 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 literal 0 HcmV?d00001 diff --git a/brand-kit/icons/windows/icon-16.png b/brand-kit/icons/windows/icon-16.png new file mode 100644 index 0000000000000000000000000000000000000000..fa4abd80468ce435fc8438b0137311728b2c3171 GIT binary patch literal 233 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 literal 0 HcmV?d00001 diff --git a/brand-kit/icons/windows/icon-64.png b/brand-kit/icons/windows/icon-64.png new file mode 100644 index 0000000000000000000000000000000000000000..0f45ac2362e5f2a64e6a014388cc5c2c5d7f5256 GIT binary patch literal 1145 zcmV-<1cv*GP)(x&(V9^-L87qiCOB-!Z&l~%=7!^%NAc?Rt|FXB1!T%D>`uLJams_i4(zezX^6f3olzqGm?nmJ2+X9)};}u5%;nUAZ zzIWCtD5|R%cyT`B43TQ0jlfjU4Xudnp!xKmJQgy zyG4Gs)q^v>G&g`dYHU`zJi8l1jh{(?>Wl!S>77N6LTj1>O zaM-iTB(DucNXmH%(XZ~yz7~5sgil|rh%of>ifa& zi9w2pni>MJGfi-0+Co?}Vlwq-Ntgv-nS9vbfl!_rM?;iVRzp>RjclF?R{?ax=R#}f z9hsl8a2@zP-v;lyM0i$OF4yrgxC#(bp#ystv;I{%otC`~2KpGGG$n@0cqfp-DPa;| z^Z87=$#K!W0r@z<0gF4{|73r-iJ2S`euI3eEG778QUlAQ6se11-kXz2fTGuLdK`|h@Ebub+&X(yWD>xmncN!xz6>2ygGUGr zolgDkV|!M3O#ocDBQ%Td0Es;Ms?twk#tmK-pbZRkiArqoI7v3I+^YgO?>v$>cDxIW zPd4%5PV+DcfCSWnX6s7K@z?*@d1G5cXKly3fP_FXRHCzE-%=(41`lxk?^e5nwvLY- zY&(72X!<5VyVc-F_W{}-L2vM=xqD+K0m`n$5CII|-rziOenJHGD>xnD?50oKg+Jz# z5cn^l3JbDa#_+VjB!H)rscGNtu&ZhI*V=Fuz<frA6#yCG^VT#r08JN6@f%IgUl%8b0(SwBQ*3~l$f6h$Qs;+b z6?b?=0PZY%nDD<7pa-ssRYCwoSv2Gn5ul;XR-`H-Kv5PAIYk6$XtNcmiU?4YMMF*z z0UFwDMXDkK6lKwnQ$)bOzS*qn{{;X5|NovFt-$~Q00v1!K~w_(Xnnt5j8L(Y00000 LNkvXXu0mjf->d>y literal 0 HcmV?d00001 diff --git a/brand-kit/logo/logo-icon-master.png b/brand-kit/logo/logo-icon-master.png new file mode 100644 index 0000000000000000000000000000000000000000..83b4250de8ac4750e18c5645111d3e3d417e6004 GIT binary patch literal 12088 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 y*=Gw&*>ARzSXF1zGxOi=#A?EJi*A_ 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 literal 0 HcmV?d00001 diff --git a/brand-kit/logo/logo-icon.svg b/brand-kit/logo/logo-icon.svg new file mode 100644 index 0000000..49d4817 --- /dev/null +++ b/brand-kit/logo/logo-icon.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/brand-kit/theme/android-Color.kt b/brand-kit/theme/android-Color.kt new file mode 100644 index 0000000..f0e44e1 --- /dev/null +++ b/brand-kit/theme/android-Color.kt @@ -0,0 +1,30 @@ +// InterlinedList — Strata · Jetpack Compose color tokens +package com.interlinedlist.theme + +import androidx.compose.ui.graphics.Color + +// Brand (constant) +val ILGreen = Color(0xFF2FA877) +val ILGreenHover = Color(0xFF28936A) +val ILGreenDark = Color(0xFF3FBF8C) +val ILTeal = Color(0xFF184860) +val ILTealDeep = Color(0xFF0C2C3A) +val ILTealAccent = Color(0xFF7FB8C4) +val ILTealBright = Color(0xFF4FD09C) +val ILAmber = Color(0xFFF0A830) + +// Light +val ILBgLight = Color(0xFFF4EEE2) +val ILSurfaceLight = Color(0xFFFBF7EF) +val ILSurface2Light = Color(0xFFF6F1E7) +val ILTextLight = Color(0xFF16323C) +val ILTextBodyLight = Color(0xFF22383E) +val ILMastheadLight = Color(0xFF184860) + +// Dark +val ILBgDark = Color(0xFF121317) +val ILSurfaceDark = Color(0xFF17191F) +val ILSurface2Dark = Color(0xFF1B1D23) +val ILTextDark = Color(0xFFF3F1EA) +val ILTextBodyDark = Color(0xFFE4E1D9) +val ILMastheadDark = Color(0xFF0C2C3A) diff --git a/brand-kit/theme/android-colors.xml b/brand-kit/theme/android-colors.xml new file mode 100644 index 0000000..227c307 --- /dev/null +++ b/brand-kit/theme/android-colors.xml @@ -0,0 +1,34 @@ + + + + + #FF2FA877 + #FF28936A + #FF3FBF8C + #FF184860 + #FF0C2C3A + #FF7FB8C4 + #FF4FD09C + #FFF0A830 + #FFDB9720 + + + #FFF4EEE2 + #FFFBF7EF + #FFF6F1E7 + #FF16323C + #FF22383E + #8C184860 + #FF184860 + + + #FF121317 + #FF17191F + #FF1B1D23 + #FFF3F1EA + #D9F3F1EA + #73F3F1EA + #FF0C2C3A + diff --git a/brand-kit/theme/interlinedlist-theme.css b/brand-kit/theme/interlinedlist-theme.css new file mode 100644 index 0000000..088376f --- /dev/null +++ b/brand-kit/theme/interlinedlist-theme.css @@ -0,0 +1,159 @@ +/* ============================================================ + InterlinedList — "Strata" theme + Drop-in CSS custom properties for the web rebrand. + Light = soft sand · Dark = near-black. Follows the OS via + prefers-color-scheme, with a manual [data-theme] override. + ============================================================ */ + +:root { + /* ---- Brand (constant across themes) ---- */ + --il-green: #2FA877; /* primary action */ + --il-green-hover: #28936A; + --il-teal: #184860; /* structure / masthead */ + --il-teal-deep: #0C2C3A; /* dark masthead */ + --il-teal-accent: #7FB8C4; /* links / borders in dark */ + --il-teal-bright: #4FD09C; /* wordmark accent */ + --il-amber: #F0A830; /* live / Dig / highlight */ + --il-amber-hover: #DB9720; + + /* ---- Typography ---- */ + --il-font-display: 'Space Grotesk', system-ui, sans-serif; + --il-font-body: 'Manrope', system-ui, sans-serif; + --il-font-mono: 'JetBrains Mono', ui-monospace, monospace; + --il-fs-display: 28px; --il-fs-title: 17px; --il-fs-subtitle: 15px; + --il-fs-body: 13px; --il-fs-small: 12px; --il-fs-meta: 10px; --il-fs-micro: 9px; + + /* ---- Spacing · 4pt grid ---- */ + --il-space-1: 4px; --il-space-2: 8px; --il-space-3: 12px; --il-space-4: 16px; + --il-space-5: 20px; --il-space-6: 24px; --il-space-8: 32px; --il-space-10: 40px; + + /* ---- Radius · sharp language ---- */ + --il-radius-sm: 3px; /* controls, tags */ + --il-radius-md: 4px; /* cards, panels */ + --il-radius-lg: 10px; /* desktop windows */ + --il-radius-pill: 999px; + + /* ---- Elevation ---- */ + --il-shadow-card: 0 1px 3px rgba(24,72,96,.06); + --il-shadow-float: 0 10px 34px rgba(24,72,96,.14); +} + +/* ---- LIGHT (default) ---- */ +:root, [data-theme="light"] { + --il-bg: #F4EEE2; + --il-surface: #FBF7EF; + --il-surface-2: #F6F1E7; + --il-surface-3: #F1EADD; + --il-border: rgba(24,72,96,.12); + --il-border-strong: rgba(24,72,96,.20); + --il-text: #16323C; + --il-text-body: #22383E; + --il-text-muted: rgba(24,72,96,.55); + --il-masthead: #184860; + --il-on-masthead:#FFFFFF; + --il-primary: var(--il-green); + --il-link: var(--il-teal); +} + +/* ---- DARK (follows OS) ---- */ +@media (prefers-color-scheme: dark) { + :root { + --il-bg: #121317; + --il-surface: #17191F; + --il-surface-2: #1B1D23; + --il-surface-3: #22252D; + --il-border: rgba(255,255,255,.08); + --il-border-strong: rgba(255,255,255,.16); + --il-text: #F3F1EA; + --il-text-body: rgba(243,241,234,.85); + --il-text-muted: rgba(243,241,234,.45); + --il-masthead: #0C2C3A; + --il-on-masthead:#F3F1EA; + --il-primary: var(--il-teal-bright); + --il-link: var(--il-teal-accent); + --il-shadow-card: 0 1px 3px rgba(0,0,0,.4); + --il-shadow-float: 0 10px 34px rgba(0,0,0,.5); + } +} + +/* ---- DARK (manual override, wins over OS) ---- */ +[data-theme="dark"] { + --il-bg: #121317; + --il-surface: #17191F; + --il-surface-2: #1B1D23; + --il-surface-3: #22252D; + --il-border: rgba(255,255,255,.08); + --il-border-strong: rgba(255,255,255,.16); + --il-text: #F3F1EA; + --il-text-body: rgba(243,241,234,.85); + --il-text-muted: rgba(243,241,234,.45); + --il-masthead: #0C2C3A; + --il-on-masthead:#F3F1EA; + --il-primary: var(--il-teal-bright); + --il-link: var(--il-teal-accent); + --il-shadow-card: 0 1px 3px rgba(0,0,0,.4); + --il-shadow-float: 0 10px 34px rgba(0,0,0,.5); +} + +/* ============================================================ + Component primitives — optional starting classes + ============================================================ */ + +body { + background: var(--il-bg); + color: var(--il-text-body); + font-family: var(--il-font-body); + -webkit-font-smoothing: antialiased; +} + +.il-wordmark { font-family: var(--il-font-display); font-weight: 700; letter-spacing: -.01em; color: var(--il-text); } +.il-wordmark span { color: var(--il-teal-bright); } /* the "List" accent */ + +.il-masthead { + display: flex; align-items: center; gap: var(--il-space-4); + padding: var(--il-space-3) var(--il-space-5); + background: var(--il-masthead); color: var(--il-on-masthead); +} + +.il-card { + background: var(--il-surface); + border: 1px solid var(--il-border); + border-radius: var(--il-radius-md); + box-shadow: var(--il-shadow-card); +} + +/* Post — color-edged card is the Strata signature */ +.il-post { border-left: 3px solid var(--il-teal); padding: var(--il-space-4) var(--il-space-5); } +.il-post--reply { border-left-color: var(--il-green); } +.il-post--release { border-left-color: var(--il-amber); } + +.il-btn { + font-family: var(--il-font-display); font-weight: 600; font-size: var(--il-fs-small); + padding: 8px 16px; border-radius: var(--il-radius-sm); border: 0; cursor: pointer; +} +.il-btn--primary { background: var(--il-primary); color: #fff; } +.il-btn--primary:hover { background: var(--il-green-hover); } +.il-btn--ghost { background: transparent; color: var(--il-primary); border: 1px solid var(--il-primary); } +.il-btn:disabled { background: var(--il-border); color: var(--il-text-muted); cursor: not-allowed; } + +.il-tag { + font-family: var(--il-font-mono); font-weight: 500; font-size: var(--il-fs-micro); + padding: 4px 8px; border-radius: var(--il-radius-sm); + color: var(--il-teal); background: rgba(24,72,96,.09); +} +.il-tag--green { color: var(--il-green); background: rgba(47,168,119,.12); } +.il-tag--amber { color: var(--il-amber); background: rgba(240,168,48,.14); } + +.il-meta { font-family: var(--il-font-mono); font-size: var(--il-fs-meta); color: var(--il-text-muted); } + +/* Dig button */ +.il-dig { font-family: var(--il-font-mono); font-size: var(--il-fs-meta); color: var(--il-text-muted); + border: 1px solid var(--il-border-strong); padding: 6px 11px; border-radius: var(--il-radius-sm); background: none; cursor: pointer; } +.il-dig--active { color: #fff; background: var(--il-amber); border-color: var(--il-amber); } + +/* Live status dot */ +.il-live { display: inline-flex; align-items: center; gap: 6px; font-family: var(--il-font-mono); + font-size: var(--il-fs-meta); font-weight: 600; color: var(--il-amber); } +.il-live::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--il-amber); + animation: il-pulse 2s infinite; } +@keyframes il-pulse { 0%,100% { opacity: 1 } 50% { opacity: .35 } } diff --git a/brand-kit/theme/ios-ILColor.swift b/brand-kit/theme/ios-ILColor.swift new file mode 100644 index 0000000..7927b9b --- /dev/null +++ b/brand-kit/theme/ios-ILColor.swift @@ -0,0 +1,54 @@ +// InterlinedList — Strata · iOS / macOS color tokens +// Colors adapt automatically to light / dark via UITraitCollection. +// For SwiftUI. Fonts: register Space Grotesk, Manrope, JetBrains Mono. + +import SwiftUI + +public enum ILColor { + // MARK: Brand (constant) + public static let green = Color(hex: 0x2FA877) // primary action + public static let greenHover = Color(hex: 0x28936A) + public static let greenDark = Color(hex: 0x3FBF8C) // primary in dark + public static let teal = Color(hex: 0x184860) // structure / masthead + public static let tealDeep = Color(hex: 0x0C2C3A) // dark masthead + public static let tealAccent = Color(hex: 0x7FB8C4) // links in dark + public static let tealBright = Color(hex: 0x4FD09C) // wordmark accent + public static let amber = Color(hex: 0xF0A830) // live / Dig + + // MARK: Theme-adaptive (light / dark) + public static let background = dynamic(light: 0xF4EEE2, dark: 0x121317) + public static let surface = dynamic(light: 0xFBF7EF, dark: 0x17191F) + public static let surface2 = dynamic(light: 0xF6F1E7, dark: 0x1B1D23) + public static let text = dynamic(light: 0x16323C, dark: 0xF3F1EA) + public static let textBody = dynamic(light: 0x22383E, dark: 0xE4E1D9) + public static let masthead = dynamic(light: 0x184860, dark: 0x0C2C3A) + public static let primary = dynamic(light: 0x2FA877, dark: 0x3FBF8C) + public static let link = dynamic(light: 0x184860, dark: 0x7FB8C4) + + private static func dynamic(light: UInt, dark: UInt) -> Color { + Color(UIColor { $0.userInterfaceStyle == .dark + ? UIColor(rgb: dark) : UIColor(rgb: light) }) + } +} + +public enum ILType { + public static let display = "SpaceGrotesk-Bold" + public static let title = "SpaceGrotesk-Bold" + public static let body = "Manrope-Regular" + public static let mono = "JetBrainsMono-Medium" +} + +public enum ILMetric { + public static let radiusSm: CGFloat = 3, radiusMd: CGFloat = 4, radiusLg: CGFloat = 10 + public static let space: [CGFloat] = [4, 8, 12, 16, 20, 24, 32, 40, 48] +} + +// Helpers +extension Color { init(hex: UInt) { + self.init(.sRGB, red: Double((hex >> 16) & 0xFF)/255, + green: Double((hex >> 8) & 0xFF)/255, + blue: Double(hex & 0xFF)/255) } } +extension UIColor { convenience init(rgb: UInt) { + self.init(red: CGFloat((rgb >> 16) & 0xFF)/255, + green: CGFloat((rgb >> 8) & 0xFF)/255, + blue: CGFloat(rgb & 0xFF)/255, alpha: 1) } } diff --git a/brand-kit/theme/tokens.json b/brand-kit/theme/tokens.json new file mode 100644 index 0000000..f9ead82 --- /dev/null +++ b/brand-kit/theme/tokens.json @@ -0,0 +1,42 @@ +{ + "$name": "InterlinedList — Strata", + "$description": "Cross-platform design tokens. Brand colors derive from the mark; light = soft sand, dark = near-black.", + "brand": { + "green": { "value": "#2FA877", "role": "primary action" }, + "greenHover": { "value": "#28936A" }, + "greenDark": { "value": "#3FBF8C", "role": "primary in dark mode" }, + "teal": { "value": "#184860", "role": "structure / masthead" }, + "tealDeep": { "value": "#0C2C3A", "role": "dark masthead" }, + "tealAccent": { "value": "#7FB8C4", "role": "links / borders in dark" }, + "tealBright": { "value": "#4FD09C", "role": "wordmark accent" }, + "amber": { "value": "#F0A830", "role": "live / Dig / highlight" }, + "amberHover": { "value": "#DB9720" } + }, + "theme": { + "light": { + "bg": "#F4EEE2", "surface": "#FBF7EF", "surface2": "#F6F1E7", "surface3": "#F1EADD", + "border": "rgba(24,72,96,0.12)", "borderStrong": "rgba(24,72,96,0.20)", + "text": "#16323C", "textBody": "#22383E", "textMuted": "rgba(24,72,96,0.55)", + "masthead": "#184860", "onMasthead": "#FFFFFF", "primary": "#2FA877", "link": "#184860" + }, + "dark": { + "bg": "#121317", "surface": "#17191F", "surface2": "#1B1D23", "surface3": "#22252D", + "border": "rgba(255,255,255,0.08)", "borderStrong": "rgba(255,255,255,0.16)", + "text": "#F3F1EA", "textBody": "rgba(243,241,234,0.85)", "textMuted": "rgba(243,241,234,0.45)", + "masthead": "#0C2C3A", "onMasthead": "#F3F1EA", "primary": "#3FBF8C", "link": "#7FB8C4" + } + }, + "typography": { + "fontDisplay": "Space Grotesk", + "fontBody": "Manrope", + "fontMono": "JetBrains Mono", + "scale": { "display": 28, "title": 17, "subtitle": 15, "body": 13, "small": 12, "meta": 10, "micro": 9 }, + "weights": [400, 500, 600, 700] + }, + "spacing": { "unit": 4, "scale": [4, 8, 12, 16, 20, 24, 32, 40, 48] }, + "radius": { "sm": 3, "md": 4, "lg": 10, "pill": 999 }, + "elevation": { + "card": "0 1px 3px rgba(24,72,96,0.06)", + "float": "0 10px 34px rgba(24,72,96,0.14)" + } +} diff --git a/core/datastore/src/main/kotlin/com/interlinedlist/android/core/datastore/ThemeSettingsStore.kt b/core/datastore/src/main/kotlin/com/interlinedlist/android/core/datastore/ThemeSettingsStore.kt new file mode 100644 index 0000000..32f4e9a --- /dev/null +++ b/core/datastore/src/main/kotlin/com/interlinedlist/android/core/datastore/ThemeSettingsStore.kt @@ -0,0 +1,50 @@ +package com.interlinedlist.android.core.datastore + +import android.content.Context +import android.content.SharedPreferences +import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import javax.inject.Inject +import javax.inject.Singleton + +/** The user's chosen app appearance. [SYSTEM] follows the OS light/dark setting. */ +enum class ThemeMode { + SYSTEM, + LIGHT, + DARK, +} + +/** + * Persists the user's appearance preference (System / Light / Dark) in plain + * SharedPreferences — it carries no secrets, so it does not need the encrypted + * session store. Exposes the current value as a [StateFlow] so the theme + * recomposes the moment the setting changes. + */ +@Singleton +class ThemeSettingsStore @Inject constructor( + @ApplicationContext context: Context, +) { + private val prefs: SharedPreferences = + context.getSharedPreferences(PREFS_FILE, Context.MODE_PRIVATE) + + private val _themeMode = MutableStateFlow(readThemeMode()) + val themeMode: StateFlow = _themeMode.asStateFlow() + + /** Persists [mode] and pushes it to observers immediately. */ + fun setThemeMode(mode: ThemeMode) { + prefs.edit().putString(KEY_THEME_MODE, mode.name).apply() + _themeMode.value = mode + } + + private fun readThemeMode(): ThemeMode { + val stored = prefs.getString(KEY_THEME_MODE, null) ?: return ThemeMode.SYSTEM + return runCatching { ThemeMode.valueOf(stored) }.getOrDefault(ThemeMode.SYSTEM) + } + + companion object { + private const val PREFS_FILE = "il_settings.prefs" + private const val KEY_THEME_MODE = "theme_mode" + } +} diff --git a/core/designsystem/src/main/kotlin/com/interlinedlist/android/core/designsystem/component/Logo.kt b/core/designsystem/src/main/kotlin/com/interlinedlist/android/core/designsystem/component/Logo.kt index 890daba..9a7d87f 100644 --- a/core/designsystem/src/main/kotlin/com/interlinedlist/android/core/designsystem/component/Logo.kt +++ b/core/designsystem/src/main/kotlin/com/interlinedlist/android/core/designsystem/component/Logo.kt @@ -13,21 +13,24 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.sp import com.interlinedlist.android.core.designsystem.R -import com.interlinedlist.android.core.designsystem.theme.EmeraldGreen -import com.interlinedlist.android.core.designsystem.theme.OceanBlue +import com.interlinedlist.android.core.designsystem.theme.ILGreen +import com.interlinedlist.android.core.designsystem.theme.ILGreenDark +import com.interlinedlist.android.core.designsystem.theme.ILTeal +import com.interlinedlist.android.core.designsystem.theme.ILTealAccent import com.interlinedlist.android.core.designsystem.theme.PlayFontFamily /** - * The InterlinedList icon mark. Picks the light or dark asset variant to suit - * the current theme, per the brand rule (light mark on dark surfaces and vice - * versa). The provided files are used as-is — never recoloured or distorted. + * The InterlinedList "Strata" icon mark (the interlinked list ladder). The tri-colour + * mark reads on either surface, but on dark the deep teal is lifted to the accent so + * the structure stays legible. The vector is the same art shipped on interlinedlist.com; + * never recoloured beyond this light/dark pairing or distorted. */ @Composable fun InterlinedListLogoMark( modifier: Modifier = Modifier, darkTheme: Boolean = isSystemInDarkTheme(), ) { - val markRes = if (darkTheme) R.drawable.il_mark_light else R.drawable.il_mark_dark + val markRes = if (darkTheme) R.drawable.il_logo_mark_dark else R.drawable.il_logo_mark Image( painter = painterResource(markRes), contentDescription = "InterlinedList", @@ -36,9 +39,8 @@ fun InterlinedListLogoMark( } /** - * The "InterlinedList" wordmark rendered in the brand typeface: Ocean Blue - * "Interlined" (switching to the theme's on-background colour in dark mode for - * legibility) + Emerald "List", with the optional tagline. + * The "InterlinedList" wordmark in the brand typeface: teal "Interlined" (lifted to the + * teal accent on dark surfaces for legibility) + green "List", with an optional tagline. */ @Composable fun InterlinedListWordmark( @@ -46,7 +48,8 @@ fun InterlinedListWordmark( showTagline: Boolean = true, darkTheme: Boolean = isSystemInDarkTheme(), ) { - val interlinedColor = if (darkTheme) MaterialTheme.colorScheme.onBackground else OceanBlue + val interlinedColor = if (darkTheme) ILTealAccent else ILTeal + val listColor = if (darkTheme) ILGreenDark else ILGreen Column(modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally) { Row { Text( @@ -61,7 +64,7 @@ fun InterlinedListWordmark( fontFamily = PlayFontFamily, fontWeight = FontWeight.Bold, fontSize = 28.sp, - color = EmeraldGreen, + color = listColor, ) } if (showTagline) { diff --git a/core/designsystem/src/main/kotlin/com/interlinedlist/android/core/designsystem/theme/Color.kt b/core/designsystem/src/main/kotlin/com/interlinedlist/android/core/designsystem/theme/Color.kt index 0951401..be492b3 100644 --- a/core/designsystem/src/main/kotlin/com/interlinedlist/android/core/designsystem/theme/Color.kt +++ b/core/designsystem/src/main/kotlin/com/interlinedlist/android/core/designsystem/theme/Color.kt @@ -2,19 +2,50 @@ package com.interlinedlist.android.core.designsystem.theme import androidx.compose.ui.graphics.Color -// Core brand palette — from the InterlinedList Branding & Style Guide. -val OceanBlue = Color(0xFF0F4C5F) // primary: headers, CTAs -val EmeraldGreen = Color(0xFF34A56D) // active states, success -val AmberGold = Color(0xFFF9AF36) // highlights, badges -val NearBlack = Color(0xFF1A1A1A) // dark backgrounds, body text -val BrandWhite = Color(0xFFFFFFFF) +// InterlinedList — "Strata" design tokens. Canonical source: brand-kit/theme/tokens.json +// (identical to the palette on interlinedlist.com). Light = soft sand, dark = near-black. -// Deep ocean tint used for elevated surfaces in dark theme. -val DeepOcean = Color(0xFF15303A) +// ---- Brand (constant across themes) ---- +val ILGreen = Color(0xFF2FA877) // primary action +val ILGreenHover = Color(0xFF28936A) +val ILGreenDark = Color(0xFF3FBF8C) // primary in dark mode +val ILTeal = Color(0xFF184860) // structure / masthead +val ILTealDeep = Color(0xFF0C2C3A) // dark masthead +val ILTealAccent = Color(0xFF7FB8C4) // links / borders in dark +val ILTealBright = Color(0xFF4FD09C) // wordmark accent +val ILAmber = Color(0xFFF0A830) // live / highlight +val ILAmberHover = Color(0xFFDB9720) +val ILError = Color(0xFFED321F) +val ILWhite = Color(0xFFFFFFFF) -// Extended palette. -val Violet = Color(0xFF7E67FE) -val ElectricBlue = Color(0xFF1A80F8) -val TealCyan = Color(0xFF1AB0F8) -val VividGreen = Color(0xFF21D760) -val AlertRed = Color(0xFFED321F) +// ---- Light theme surfaces & text ---- +val ILBgLight = Color(0xFFF4EEE2) // soft sand background +val ILSurfaceLight = Color(0xFFFBF7EF) +val ILSurface2Light = Color(0xFFF6F1E7) +val ILSurface3Light = Color(0xFFF1EADD) +val ILTextLight = Color(0xFF16323C) +val ILTextBodyLight = Color(0xFF22383E) +val ILTextMutedLight = Color(0xFF6B7C83) // ≈ rgba(24,72,96,0.55) over sand +val ILBorderLight = Color(0xFFDDD6C8) +// Tonal containers (FAB, tonal buttons, nav indicator) in brand hues. +val ILGreenContainerLight = Color(0xFFBDE8D4) +val ILOnGreenContainerLight = Color(0xFF0B4A34) +val ILTealContainerLight = Color(0xFFCFE0E4) +val ILAmberContainerLight = Color(0xFFFBE3BC) +val ILOnAmberContainerLight = Color(0xFF4A3305) + +// ---- Dark theme surfaces & text ---- +val ILBgDark = Color(0xFF121317) // near-black background +val ILSurfaceDark = Color(0xFF17191F) +val ILSurface2Dark = Color(0xFF1B1D23) +val ILSurface3Dark = Color(0xFF22252D) +val ILTextDark = Color(0xFFF3F1EA) +val ILTextBodyDark = Color(0xFFE4E1D9) +val ILTextMutedDark = Color(0xFF9AA1A6) // ≈ rgba(243,241,234,0.45) over near-black +val ILBorderDark = Color(0xFF2C2F37) +// Tonal containers for dark surfaces. +val ILGreenContainerDark = Color(0xFF1E5641) +val ILOnGreenContainerDark = Color(0xFFB6ECD6) +val ILTealContainerDark = Color(0xFF20343C) +val ILAmberContainerDark = Color(0xFF4E3A12) +val ILOnAmberContainerDark = Color(0xFFF7DDB0) diff --git a/core/designsystem/src/main/kotlin/com/interlinedlist/android/core/designsystem/theme/Theme.kt b/core/designsystem/src/main/kotlin/com/interlinedlist/android/core/designsystem/theme/Theme.kt index bc8b4e6..24d19a9 100644 --- a/core/designsystem/src/main/kotlin/com/interlinedlist/android/core/designsystem/theme/Theme.kt +++ b/core/designsystem/src/main/kotlin/com/interlinedlist/android/core/designsystem/theme/Theme.kt @@ -6,38 +6,66 @@ import androidx.compose.material3.darkColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable +// Light = soft sand surfaces, teal structure, green primary actions, amber highlights. private val LightColors = lightColorScheme( - primary = OceanBlue, - onPrimary = BrandWhite, - secondary = EmeraldGreen, - onSecondary = BrandWhite, - tertiary = AmberGold, - onTertiary = NearBlack, - background = BrandWhite, - onBackground = NearBlack, - surface = BrandWhite, - onSurface = NearBlack, - error = AlertRed, - onError = BrandWhite, + primary = ILGreen, + onPrimary = ILWhite, + primaryContainer = ILGreenContainerLight, + onPrimaryContainer = ILOnGreenContainerLight, + secondary = ILTeal, + onSecondary = ILWhite, + secondaryContainer = ILTealContainerLight, + onSecondaryContainer = ILTeal, + tertiary = ILAmber, + onTertiary = ILTextLight, + tertiaryContainer = ILAmberContainerLight, + onTertiaryContainer = ILOnAmberContainerLight, + background = ILBgLight, + onBackground = ILTextLight, + surface = ILSurfaceLight, + onSurface = ILTextLight, + surfaceVariant = ILSurface2Light, + onSurfaceVariant = ILTextMutedLight, + surfaceContainer = ILSurface2Light, + outline = ILBorderLight, + outlineVariant = ILBorderLight, + error = ILError, + onError = ILWhite, ) +// Dark = near-black surfaces; the deep teal lifts to accent and green brightens so +// both stay legible per the brand's "light mark on dark" rule. private val DarkColors = darkColorScheme( - // Emerald reads better than Ocean Blue as the accent on dark surfaces. - primary = EmeraldGreen, - onPrimary = NearBlack, - secondary = AmberGold, - onSecondary = NearBlack, - tertiary = TealCyan, - onTertiary = NearBlack, - background = NearBlack, - onBackground = BrandWhite, - surface = DeepOcean, - onSurface = BrandWhite, - error = AlertRed, - onError = NearBlack, + primary = ILGreenDark, + onPrimary = ILBgDark, + primaryContainer = ILGreenContainerDark, + onPrimaryContainer = ILOnGreenContainerDark, + secondary = ILTealAccent, + onSecondary = ILBgDark, + secondaryContainer = ILTealContainerDark, + onSecondaryContainer = ILTextDark, + tertiary = ILAmber, + onTertiary = ILBgDark, + tertiaryContainer = ILAmberContainerDark, + onTertiaryContainer = ILOnAmberContainerDark, + background = ILBgDark, + onBackground = ILTextDark, + surface = ILSurfaceDark, + onSurface = ILTextDark, + surfaceVariant = ILSurface2Dark, + onSurfaceVariant = ILTextMutedDark, + surfaceContainer = ILSurface2Dark, + outline = ILBorderDark, + outlineVariant = ILBorderDark, + error = ILError, + onError = ILBgDark, ) -/** App-wide Material 3 theme carrying the InterlinedList brand colours and type. */ +/** + * App-wide Material 3 theme carrying the InterlinedList "Strata" brand colours and + * type. [darkTheme] is resolved by the caller from the user's appearance setting + * (System / Light / Dark); it defaults to following the OS. + */ @Composable fun InterlinedListTheme( darkTheme: Boolean = isSystemInDarkTheme(), diff --git a/core/designsystem/src/main/res/drawable-nodpi/il_mark_dark.png b/core/designsystem/src/main/res/drawable-nodpi/il_mark_dark.png deleted file mode 100644 index b8ade2dfcc1e4168bdfba4251727407784535c15..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14245 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 DBrus` diff --git a/core/designsystem/src/main/res/drawable-nodpi/il_mark_light.png b/core/designsystem/src/main/res/drawable-nodpi/il_mark_light.png deleted file mode 100644 index 01ac3a536537247f5cc289dbd0b9a077f13e1522..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14348 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%0AP zPu3#5z2`;oyL@4*!DRBzv$;iuferKhuGpf&@*)0roPcJH1glR;j=s|E=fpcO%Y+>D z+T?#CcPUo;8Z1S0z){dNtMmi_=p7QDqcpkK_pq6Li~nf#M1$O4vYEez_falARE1Gt z(sC%LCwL?x56yoA&arozF;RiJcLyBr?eh>7hnTQAN>1W`l8Mc7$^ZTPOLGbEFaha< z*KCR0_J_+0hAeEkaa5AKy?c>y5*|I4K>6%U!woy6eBq9J}H z_~c6f=uGeL7i#yiM@c+8DhfBI#Yb{4R9nHoTWzT1Xuu~`%JenhEdo&)tzak2bmYm? zQ{edo%NKp^@u1MwRswEY{ykQQE#^X?IjRU`^6tGG9q(NO$1mUu9+Sx1UNq<->ZwKh sm-q$!ubgc8znlJ_1^&kZCUrFjbo8);%5@cS+`musw2ibX?%9R^KhRsB{Qv*} diff --git a/core/designsystem/src/main/res/drawable/il_logo_mark.xml b/core/designsystem/src/main/res/drawable/il_logo_mark.xml new file mode 100644 index 0000000..6c610cd --- /dev/null +++ b/core/designsystem/src/main/res/drawable/il_logo_mark.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/core/designsystem/src/main/res/drawable/il_logo_mark_dark.xml b/core/designsystem/src/main/res/drawable/il_logo_mark_dark.xml new file mode 100644 index 0000000..5fd68da --- /dev/null +++ b/core/designsystem/src/main/res/drawable/il_logo_mark_dark.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/core/network/src/main/kotlin/com/interlinedlist/android/core/network/di/NetworkModule.kt b/core/network/src/main/kotlin/com/interlinedlist/android/core/network/di/NetworkModule.kt index 83265f5..be00cb5 100644 --- a/core/network/src/main/kotlin/com/interlinedlist/android/core/network/di/NetworkModule.kt +++ b/core/network/src/main/kotlin/com/interlinedlist/android/core/network/di/NetworkModule.kt @@ -24,6 +24,10 @@ object NetworkModule { fun provideJson(): Json = Json { ignoreUnknownKeys = true explicitNulls = false + // The API sends explicit `null` for many optional fields (e.g. imageUrls, + // videoUrls on a freshly created message). Coerce those to the property's + // default instead of failing to deserialize a non-nullable type. + coerceInputValues = true } @Provides diff --git a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/DefaultMessagesRepository.kt b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/DefaultMessagesRepository.kt index a08ef26..9b8b35d 100644 --- a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/DefaultMessagesRepository.kt +++ b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/DefaultMessagesRepository.kt @@ -90,7 +90,7 @@ class DefaultMessagesRepository @Inject constructor( ) when (val result = safeCall { api.createMessage(request) }) { is ApiResult.Success -> { - val message = result.data.message.toDomain(currentUserId()) + val message = result.data.data.toDomain(currentUserId()) if (message.scheduledAt != null) { // Scheduled messages are cached in the scheduled view, not the feed. messageDao.upsert(message.toEntity(feedOrder = 0L)) @@ -153,7 +153,7 @@ class DefaultMessagesRepository @Inject constructor( api.createMessage(CreateMessageRequest(content = content, parentId = parentId)) }) { is ApiResult.Success -> { - val reply = result.data.message.toDomain(currentUserId()).copy(parentId = parentId) + val reply = result.data.data.toDomain(currentUserId()).copy(parentId = parentId) val base = (messageDao.maxFeedOrder() ?: 0L) + 1L messageDao.upsert(reply.toEntity(feedOrder = base)) // Reflect the new reply count on the parent if it is cached. diff --git a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/MessagesApi.kt b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/MessagesApi.kt index 106bd1a..5415994 100644 --- a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/MessagesApi.kt +++ b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/MessagesApi.kt @@ -1,6 +1,7 @@ package com.interlinedlist.android.feature.messages.data.remote import com.interlinedlist.android.feature.messages.data.remote.dto.CreateMessageRequest +import com.interlinedlist.android.feature.messages.data.remote.dto.CreateMessageResponse import com.interlinedlist.android.feature.messages.data.remote.dto.MediaUploadResponse import com.interlinedlist.android.feature.messages.data.remote.dto.MessageResponse import com.interlinedlist.android.feature.messages.data.remote.dto.MessagesResponse @@ -31,9 +32,10 @@ interface MessagesApi { @Query("offset") offset: Int, ): MessagesResponse - /** Creates a new message (or a reply when `parentId` is set). */ + /** Creates a new message (or a reply when `parentId` is set). The created + * message is returned under `data` (see [CreateMessageResponse]). */ @POST("api/messages") - suspend fun createMessage(@Body body: CreateMessageRequest): MessageResponse + suspend fun createMessage(@Body body: CreateMessageRequest): CreateMessageResponse /** A single message by id (for the detail screen). */ @GET("api/messages/{id}") diff --git a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/dto/MessageDto.kt b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/dto/MessageDto.kt index 1ea8366..15079ab 100644 --- a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/dto/MessageDto.kt +++ b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/dto/MessageDto.kt @@ -17,6 +17,9 @@ data class MessageDto( val id: String, val content: String = "", val author: MessageAuthorDto? = null, + /** The create/detail endpoints key the author sub-object as `user` rather than + * `author`; [toDomain] falls back to whichever the payload used. */ + val user: MessageAuthorDto? = null, val createdAt: String? = null, val digCount: Int = 0, val replyCount: Int = 0, @@ -59,24 +62,29 @@ data class LinkMetadataDto( * Maps the wire model into the domain [Message]. [currentUserId] lets us flag * the caller's own messages (for delete) even when the API omits `isOwn`. */ -fun MessageDto.toDomain(currentUserId: String?): Message = Message( - id = id, - content = content, - authorId = author?.id.orEmpty(), - authorUsername = author?.username.orEmpty(), - authorDisplayName = author?.displayName, - authorAvatarUrl = author?.avatar, - createdAt = createdAt, - digCount = digCount, - replyCount = replyCount, - dugByMe = dugByCurrentUser, - parentId = parentId, - mine = isOwn || (currentUserId != null && author?.id == currentUserId), - imageUrls = imageUrls, - videoUrls = videoUrls, - linkPreview = linkMetadata?.toDomain(), - scheduledAt = scheduledAt, -) +fun MessageDto.toDomain(currentUserId: String?): Message { + // The API is inconsistent about the author key: the feed uses `author`, the + // create/detail endpoints use `user`. Prefer whichever the payload populated. + val person = author ?: user + return Message( + id = id, + content = content, + authorId = person?.id.orEmpty(), + authorUsername = person?.username.orEmpty(), + authorDisplayName = person?.displayName, + authorAvatarUrl = person?.avatar, + createdAt = createdAt, + digCount = digCount, + replyCount = replyCount, + dugByMe = dugByCurrentUser, + parentId = parentId, + mine = isOwn || (currentUserId != null && person?.id == currentUserId), + imageUrls = imageUrls, + videoUrls = videoUrls, + linkPreview = linkMetadata?.toDomain(), + scheduledAt = scheduledAt, + ) +} /** Maps link-preview metadata into the domain, dropping empty previews. */ fun LinkMetadataDto.toDomain(): LinkPreview? { diff --git a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/dto/MessagesResponse.kt b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/dto/MessagesResponse.kt index 918b392..c62f929 100644 --- a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/dto/MessagesResponse.kt +++ b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/dto/MessagesResponse.kt @@ -31,6 +31,17 @@ data class MessageResponse( val message: MessageDto, ) +/** + * Response from creating a message or posting a reply. Unlike [MessageResponse], + * here `message` is a human-readable status string ("Message created + * successfully") and the created message is under [data]: + * `{ message: "…", data: { …message… }, crossPosts: [...] }`. + */ +@Serializable +data class CreateMessageResponse( + val data: MessageDto, +) + /** * Request body for creating a message or posting a reply. * diff --git a/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/data/DefaultMessagesRepositoryTest.kt b/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/data/DefaultMessagesRepositoryTest.kt index 73cbd0a..17aa29d 100644 --- a/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/data/DefaultMessagesRepositoryTest.kt +++ b/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/data/DefaultMessagesRepositoryTest.kt @@ -130,10 +130,13 @@ class DefaultMessagesRepositoryTest { """{ "data": [ { "id": "old", "content": "old" } ], "pagination": { "hasMore": false } }""", ) + // The create endpoint returns the new message under `data` (and keys the + // author sub-object as `user`); `message` is a status string. enqueueJson( 201, - """{ "message": { "id": "new", "content": "brand new", - "author": { "id": "me", "username": "me" } } }""", + """{ "message": "Message created successfully", + "data": { "id": "new", "content": "brand new", + "user": { "id": "me", "username": "me" } } }""", ) val repo = repository() repo.refreshFeed() @@ -210,7 +213,8 @@ class DefaultMessagesRepositoryTest { ) enqueueJson( 201, - """{ "message": { "id": "r", "content": "a reply", "author": { "id": "me" } } }""", + """{ "message": "Message created successfully", + "data": { "id": "r", "content": "a reply", "user": { "id": "me" } } }""", ) val repo = repository() repo.refreshFeed() @@ -274,8 +278,9 @@ class DefaultMessagesRepositoryTest { fun `createMessage with media sends the attached urls`() = runTest(dispatcher) { enqueueJson( 201, - """{ "message": { "id": "m1", "content": "with media", - "imageUrls": ["https://cdn/a.png"] } }""", + """{ "message": "Message created successfully", + "data": { "id": "m1", "content": "with media", + "imageUrls": ["https://cdn/a.png"] } }""", ) val repo = repository() @@ -294,8 +299,9 @@ class DefaultMessagesRepositoryTest { fun `createMessage scheduled is cached in the scheduled view not the feed`() = runTest(dispatcher) { enqueueJson( 201, - """{ "message": { "id": "sch1", "content": "later", - "scheduledAt": "2026-07-19T09:00:00Z" } }""", + """{ "message": "Message created successfully", + "data": { "id": "sch1", "content": "later", + "scheduledAt": "2026-07-19T09:00:00Z" } }""", ) val repo = repository() From 6b9d6743131de860bdc8cfd314e282f6cf546dce Mon Sep 17 00:00:00 2001 From: Adron Hall Date: Sun, 2 Aug 2026 12:47:24 -0700 Subject: [PATCH 2/5] feat(messages): edit own message + author moderation menu (Milestone I + D remainder) Edit own messages via PATCH /api/messages/{id} (optimistic + 'edited' marker from updatedAt); author overflow menu on others' messages with Block/Mute/Report user (POST /api/users/{username}/block|mute|report), block/mute hide the author locally. Self-contained in :feature:messages (no :feature:profile dep). 82 unit tests green; :app:assembleDebug green. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../ui/feed/MessagesFeedScreenTest.kt | 80 ++++++++++- .../data/DefaultMessagesRepository.kt | 70 +++++++++ .../messages/data/MessagesRepository.kt | 22 +++ .../feature/messages/data/local/MessageDao.kt | 7 + .../messages/data/local/MessageEntity.kt | 4 + .../messages/data/remote/MessagesApi.kt | 25 ++++ .../messages/data/remote/dto/MessageDto.kt | 4 + .../data/remote/dto/MessagesResponse.kt | 20 +++ .../feature/messages/domain/Message.kt | 8 ++ .../ui/components/EditMessageSheet.kt | 96 +++++++++++++ .../messages/ui/components/MessageCard.kt | 76 +++++++++- .../ui/components/ModerationDialog.kt | 118 ++++++++++++++++ .../messages/ui/detail/MessageDetailScreen.kt | 57 ++++++++ .../ui/detail/MessageDetailViewModel.kt | 80 +++++++++++ .../messages/ui/feed/MessagesFeedScreen.kt | 60 ++++++++ .../messages/ui/feed/MessagesFeedViewModel.kt | 104 ++++++++++++++ .../data/DefaultMessagesRepositoryTest.kt | 133 ++++++++++++++++++ .../feature/messages/data/FakeMessageDao.kt | 4 + .../data/remote/dto/MessageDtoMapperTest.kt | 37 +++++ .../messages/ui/FakeMessagesRepository.kt | 51 ++++++- .../ui/detail/MessageDetailViewModelTest.kt | 87 ++++++++++++ .../ui/feed/MessagesFeedViewModelTest.kt | 127 +++++++++++++++++ 22 files changed, 1262 insertions(+), 8 deletions(-) create mode 100644 feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/components/EditMessageSheet.kt create mode 100644 feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/components/ModerationDialog.kt diff --git a/feature/messages/src/androidTest/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedScreenTest.kt b/feature/messages/src/androidTest/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedScreenTest.kt index 7edf96a..584aa41 100644 --- a/feature/messages/src/androidTest/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedScreenTest.kt +++ b/feature/messages/src/androidTest/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedScreenTest.kt @@ -11,8 +11,10 @@ import androidx.compose.ui.test.performClick import androidx.test.ext.junit.runners.AndroidJUnit4 import com.interlinedlist.android.core.designsystem.theme.InterlinedListTheme import com.interlinedlist.android.feature.messages.domain.Message +import com.interlinedlist.android.feature.messages.ui.components.EditMessageSheetTags import com.interlinedlist.android.feature.messages.ui.components.MessageCardTags import com.interlinedlist.android.feature.messages.ui.components.MessageMediaTags +import com.interlinedlist.android.feature.messages.ui.components.ModerationDialogTags import com.interlinedlist.android.feature.messages.ui.components.ReportDialogTags import org.junit.Rule import org.junit.Test @@ -28,11 +30,13 @@ class MessagesFeedScreenTest { id: String, body: String, imageUrls: List = emptyList(), + mine: Boolean = false, + editedAt: String? = null, ) = Message( id = id, content = body, authorId = "u1", authorUsername = "adron", authorDisplayName = "Adron", authorAvatarUrl = null, createdAt = null, - digCount = 0, replyCount = 0, dugByMe = false, parentId = null, mine = false, - imageUrls = imageUrls, + digCount = 0, replyCount = 0, dugByMe = false, parentId = null, mine = mine, + imageUrls = imageUrls, editedAt = editedAt, ) /** Hosts the stateless feed with a tiny in-memory state holder. */ @@ -40,6 +44,10 @@ class MessagesFeedScreenTest { initial: MessagesFeedUiState, onOpenMessage: (String) -> Unit = {}, onReport: (Message) -> Unit = {}, + onEdit: (Message) -> Unit = {}, + onBlockUser: (Message) -> Unit = {}, + onMuteUser: (Message) -> Unit = {}, + onReportUser: (Message) -> Unit = {}, ) { composeRule.setContent { var state by mutableStateOf(initial) @@ -56,6 +64,10 @@ class MessagesFeedScreenTest { onComposeTextChange = { state = state.copy(composeText = it) }, onPost = {}, onReport = onReport, + onEdit = onEdit, + onBlockUser = onBlockUser, + onMuteUser = onMuteUser, + onReportUser = onReportUser, ) } } @@ -125,6 +137,70 @@ class MessagesFeedScreenTest { composeRule.onNodeWithTag(ReportDialogTags.DIALOG).assertIsDisplayed() } + @Test + fun overflowMenu_offersEdit_onOwnMessage() { + var edited: String? = null + setFeed( + MessagesFeedUiState(messages = listOf(message("mine1", "my post", mine = true))), + onEdit = { edited = it.id }, + ) + composeRule.onNodeWithTag(MessageCardTags.MENU).performClick() + composeRule.onNodeWithTag(MessageCardTags.EDIT).performClick() + assert(edited == "mine1") + } + + @Test + fun overflowMenu_offersAuthorModeration_onOthersMessage() { + var blocked: String? = null + var muted: String? = null + var reportedUser: String? = null + setFeed( + MessagesFeedUiState(messages = listOf(message("77", "not mine"))), + onBlockUser = { blocked = it.id }, + onMuteUser = { muted = it.id }, + onReportUser = { reportedUser = it.id }, + ) + composeRule.onNodeWithTag(MessageCardTags.MENU).performClick() + composeRule.onNodeWithTag(MessageCardTags.BLOCK_USER).assertIsDisplayed() + composeRule.onNodeWithTag(MessageCardTags.MUTE_USER).assertIsDisplayed() + composeRule.onNodeWithTag(MessageCardTags.REPORT_USER).performClick() + assert(reportedUser == "77") + } + + @Test + fun editSheet_isShown_whenEditTargetIsSet() { + setFeed( + MessagesFeedUiState( + editTarget = message("mine1", "my post", mine = true), + editText = "my post", + ), + ) + composeRule.onNodeWithTag(EditMessageSheetTags.INPUT).assertIsDisplayed() + } + + @Test + fun moderationDialog_isShown_whenModerationTargetIsSet() { + setFeed( + MessagesFeedUiState( + moderationTarget = com.interlinedlist.android.feature.messages.ui.feed.ModerationTarget( + message = message("77", "not mine"), + action = com.interlinedlist.android.feature.messages.ui.feed.ModerationAction.BLOCK, + ), + ), + ) + composeRule.onNodeWithTag(ModerationDialogTags.DIALOG).assertIsDisplayed() + } + + @Test + fun editedMarker_isShown_forEditedMessage() { + setFeed( + MessagesFeedUiState( + messages = listOf(message("1", "edited body", editedAt = "2026-07-31T12:00:00Z")), + ), + ) + composeRule.onNodeWithTag(MessageCardTags.EDITED).assertIsDisplayed() + } + @Test fun scheduledAction_isPresent_inTheTopBar() { setFeed(MessagesFeedUiState(messages = listOf(message("1", "hi")))) diff --git a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/DefaultMessagesRepository.kt b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/DefaultMessagesRepository.kt index 9b8b35d..32951ee 100644 --- a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/DefaultMessagesRepository.kt +++ b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/DefaultMessagesRepository.kt @@ -8,8 +8,10 @@ import com.interlinedlist.android.feature.messages.data.local.toDomain import com.interlinedlist.android.feature.messages.data.local.toEntity import com.interlinedlist.android.feature.messages.data.remote.MessagesApi import com.interlinedlist.android.feature.messages.data.remote.dto.CreateMessageRequest +import com.interlinedlist.android.feature.messages.data.remote.dto.EditMessageRequest import com.interlinedlist.android.feature.messages.data.remote.dto.PaginationDto import com.interlinedlist.android.feature.messages.data.remote.dto.ReportRequest +import com.interlinedlist.android.feature.messages.data.remote.dto.UserReportRequest import com.interlinedlist.android.feature.messages.data.remote.dto.toDomain import com.interlinedlist.android.core.network.error.safeApiCall import com.interlinedlist.android.feature.messages.domain.Message @@ -195,6 +197,33 @@ class DefaultMessagesRepository @Inject constructor( } } + override suspend fun editMessage(messageId: String, content: String): ApiResult = + withContext(dispatchers.io) { + // Optimistically apply the new content + an "edited" marker so the feed + // and detail react immediately; roll back the whole row on failure. + val previous = currentEntity(messageId) + val editedAt = nowIso() + if (previous != null) { + messageDao.upsert(previous.copy(content = content, editedAt = editedAt)) + } + when (val result = safeCall { api.editMessage(messageId, EditMessageRequest(content = content)) }) { + is ApiResult.Success -> { + val updated = (previous?.copy(content = content, editedAt = editedAt))?.toDomain() + ?: Message( + id = messageId, content = content, authorId = "", authorUsername = "", + authorDisplayName = null, authorAvatarUrl = null, createdAt = null, + digCount = 0, replyCount = 0, dugByMe = false, parentId = null, + mine = true, editedAt = editedAt, + ) + ApiResult.Success(updated) + } + is ApiResult.Failure -> { + if (previous != null) messageDao.upsert(previous) + result + } + } + } + override suspend fun refreshScheduled(): ApiResult = withContext(dispatchers.io) { when (val result = safeCall { api.getScheduled() }) { is ApiResult.Success -> { @@ -232,6 +261,44 @@ class DefaultMessagesRepository @Inject constructor( } } + override suspend fun blockUser(username: String): ApiResult = withContext(dispatchers.io) { + when (val result = safeCall { api.blockUser(username) }) { + is ApiResult.Success -> { + // Hide the blocked author's messages from the local cache. + messageDao.deleteByAuthorUsername(username) + ApiResult.Success(Unit) + } + is ApiResult.Failure -> result + } + } + + override suspend fun muteUser(username: String): ApiResult = withContext(dispatchers.io) { + when (val result = safeCall { api.muteUser(username) }) { + is ApiResult.Success -> { + // Hide the muted author's messages from the local cache. + messageDao.deleteByAuthorUsername(username) + ApiResult.Success(Unit) + } + is ApiResult.Failure -> result + } + } + + override suspend fun reportUser( + username: String, + reason: ReportReason, + detail: String?, + ): ApiResult = withContext(dispatchers.io) { + safeCall { + api.reportUser( + username = username, + body = UserReportRequest( + reason = reason.wireValue, + detail = detail?.takeIf { it.isNotBlank() }, + ), + ) + } + } + override suspend fun fetchMetadata(messageId: String): ApiResult = withContext(dispatchers.io) { when (val result = safeCall { api.fetchMetadata(messageId) }) { is ApiResult.Success -> { @@ -284,6 +351,9 @@ class DefaultMessagesRepository @Inject constructor( private fun currentUserId(): String? = sessionStore.userId + /** Current instant as an ISO-8601 string, for the optimistic "edited" marker. */ + private fun nowIso(): String = java.time.Instant.now().toString() + /** Shared multipart upload path; extracts the hosted URL from the response. */ private suspend fun upload( bytes: ByteArray, diff --git a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/MessagesRepository.kt b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/MessagesRepository.kt index c5986f0..f6fb2d2 100644 --- a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/MessagesRepository.kt +++ b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/MessagesRepository.kt @@ -69,6 +69,13 @@ interface MessagesRepository { /** Deletes one of the caller's own messages, removing it from the cache. */ suspend fun deleteMessage(messageId: String): ApiResult + /** + * Edits the [content] of one of the caller's own messages. Optimistically + * updates the cached message (content + an "edited" marker) and rolls the + * change back on failure. Returns the updated [Message]. + */ + suspend fun editMessage(messageId: String, content: String): ApiResult + /** Refreshes the caller's scheduled messages from the API into the cache. */ suspend fun refreshScheduled(): ApiResult @@ -78,6 +85,21 @@ interface MessagesRepository { /** Reports a message with a [reason] and optional free-text [detail]. */ suspend fun report(messageId: String, reason: ReportReason, detail: String? = null): ApiResult + /** + * Blocks the user [username]. On success, removes that author's messages from + * the local feed/reply cache so the caller stops seeing them immediately. + */ + suspend fun blockUser(username: String): ApiResult + + /** + * Mutes the user [username]. On success, removes that author's messages from + * the local feed/reply cache so the caller stops seeing them immediately. + */ + suspend fun muteUser(username: String): ApiResult + + /** Reports the user [username] with a [reason] and optional free-text [detail]. */ + suspend fun reportUser(username: String, reason: ReportReason, detail: String? = null): ApiResult + /** * Fetches link-preview metadata for [messageId]'s links and updates the cached * message so the feed/detail can render a preview card. diff --git a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/local/MessageDao.kt b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/local/MessageDao.kt index 943547f..a19a63a 100644 --- a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/local/MessageDao.kt +++ b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/local/MessageDao.kt @@ -39,6 +39,13 @@ interface MessageDao { @Query("DELETE FROM message WHERE id = :id") suspend fun deleteById(id: String) + /** + * Removes every cached message authored by [username] (used to hide a blocked + * or muted author's messages from the local feed/replies immediately). + */ + @Query("DELETE FROM message WHERE authorUsername = :username") + suspend fun deleteByAuthorUsername(username: String) + /** Clears the top-level feed (used before writing a fresh refresh page). */ @Query("DELETE FROM message WHERE parentId IS NULL AND scheduledAt IS NULL") suspend fun clearFeed() diff --git a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/local/MessageEntity.kt b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/local/MessageEntity.kt index 3d81d1c..21a9d04 100644 --- a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/local/MessageEntity.kt +++ b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/local/MessageEntity.kt @@ -34,6 +34,8 @@ data class MessageEntity( val linkPreview: LinkPreview? = null, /** Future send time for a scheduled message; null for a normal message. */ val scheduledAt: String? = null, + /** Last-edited instant; null when the message has not been edited. */ + val editedAt: String? = null, ) fun MessageEntity.toDomain(): Message = Message( @@ -53,6 +55,7 @@ fun MessageEntity.toDomain(): Message = Message( videoUrls = videoUrls, linkPreview = linkPreview, scheduledAt = scheduledAt, + editedAt = editedAt, ) fun Message.toEntity(feedOrder: Long): MessageEntity = MessageEntity( @@ -73,4 +76,5 @@ fun Message.toEntity(feedOrder: Long): MessageEntity = MessageEntity( videoUrls = videoUrls, linkPreview = linkPreview, scheduledAt = scheduledAt, + editedAt = editedAt, ) diff --git a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/MessagesApi.kt b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/MessagesApi.kt index 5415994..8bc7ec3 100644 --- a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/MessagesApi.kt +++ b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/MessagesApi.kt @@ -2,17 +2,20 @@ package com.interlinedlist.android.feature.messages.data.remote import com.interlinedlist.android.feature.messages.data.remote.dto.CreateMessageRequest import com.interlinedlist.android.feature.messages.data.remote.dto.CreateMessageResponse +import com.interlinedlist.android.feature.messages.data.remote.dto.EditMessageRequest import com.interlinedlist.android.feature.messages.data.remote.dto.MediaUploadResponse import com.interlinedlist.android.feature.messages.data.remote.dto.MessageResponse import com.interlinedlist.android.feature.messages.data.remote.dto.MessagesResponse import com.interlinedlist.android.feature.messages.data.remote.dto.MetadataResponse import com.interlinedlist.android.feature.messages.data.remote.dto.ReportRequest import com.interlinedlist.android.feature.messages.data.remote.dto.ScheduledMessagesResponse +import com.interlinedlist.android.feature.messages.data.remote.dto.UserReportRequest import okhttp3.MultipartBody import retrofit2.http.Body import retrofit2.http.DELETE import retrofit2.http.GET import retrofit2.http.Multipart +import retrofit2.http.PATCH import retrofit2.http.POST import retrofit2.http.Part import retrofit2.http.Path @@ -57,6 +60,14 @@ interface MessagesApi { @DELETE("api/messages/{id}") suspend fun deleteMessage(@Path("id") id: String) + /** + * Edits the content of one of the caller's own messages. The endpoint's + * response body is not modelled in the OpenAPI spec; the repository updates + * the cache optimistically and treats a 2xx as success, so this returns Unit. + */ + @PATCH("api/messages/{id}") + suspend fun editMessage(@Path("id") id: String, @Body body: EditMessageRequest) + /** Full-text search over top-level messages. */ @GET("api/messages/search") suspend fun search( @@ -86,4 +97,18 @@ interface MessagesApi { /** Fetches and attaches link-preview metadata for a message's links. */ @POST("api/messages/{id}/metadata") suspend fun fetchMetadata(@Path("id") id: String): MetadataResponse + + // --- author moderation (on messages by other users) -------------------- + + /** Blocks a user by username. */ + @POST("api/users/{username}/block") + suspend fun blockUser(@Path("username") username: String) + + /** Mutes a user by username. */ + @POST("api/users/{username}/mute") + suspend fun muteUser(@Path("username") username: String) + + /** Reports a user with a reason (and optional free-text detail). */ + @POST("api/users/{username}/report") + suspend fun reportUser(@Path("username") username: String, @Body body: UserReportRequest) } diff --git a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/dto/MessageDto.kt b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/dto/MessageDto.kt index 15079ab..bbaae53 100644 --- a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/dto/MessageDto.kt +++ b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/dto/MessageDto.kt @@ -21,6 +21,8 @@ data class MessageDto( * `author`; [toDomain] falls back to whichever the payload used. */ val user: MessageAuthorDto? = null, val createdAt: String? = null, + /** Last-modified instant; when it differs from [createdAt] the message was edited. */ + val updatedAt: String? = null, val digCount: Int = 0, val replyCount: Int = 0, val dugByCurrentUser: Boolean = false, @@ -74,6 +76,8 @@ fun MessageDto.toDomain(currentUserId: String?): Message { authorDisplayName = person?.displayName, authorAvatarUrl = person?.avatar, createdAt = createdAt, + // Treat the message as edited only when it was modified after creation. + editedAt = updatedAt?.takeIf { createdAt == null || it != createdAt }, digCount = digCount, replyCount = replyCount, dugByMe = dugByCurrentUser, diff --git a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/dto/MessagesResponse.kt b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/dto/MessagesResponse.kt index c62f929..7e7460d 100644 --- a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/dto/MessagesResponse.kt +++ b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/dto/MessagesResponse.kt @@ -90,6 +90,26 @@ data class ReportRequest( val detail: String? = null, ) +/** + * Request body for editing one of the caller's own messages via + * `PATCH /api/messages/{id}`. Only `content` is sent; the shared Json uses + * `explicitNulls = false`, so the body is a plain `{ "content": "…" }`. + */ +@Serializable +data class EditMessageRequest( + val content: String, +) + +/** + * Request body for reporting a *user* via `POST /api/users/{username}/report`. + * Mirrors the message [ReportRequest] shape: `{ reason, detail? }`. + */ +@Serializable +data class UserReportRequest( + val reason: String, + val detail: String? = null, +) + /** * Response from the metadata endpoint. The updated message (with its populated * `linkMetadata`) is returned either at the top level or under `message`. diff --git a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/domain/Message.kt b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/domain/Message.kt index a7348f1..dbfcc1f 100644 --- a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/domain/Message.kt +++ b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/domain/Message.kt @@ -34,12 +34,20 @@ data class Message( * normal message. Present on rows returned by the scheduled endpoint. */ val scheduledAt: String? = null, + /** + * ISO-8601 last-edited instant for a message whose content was changed after + * it was posted; null for an un-edited message. Drives the "edited" indicator. + */ + val editedAt: String? = null, ) { /** Best available display label for the author. */ val authorLabel: String get() = authorDisplayName?.takeIf { it.isNotBlank() } ?: authorUsername /** True when any image or video media is attached. */ val hasMedia: Boolean get() = imageUrls.isNotEmpty() || videoUrls.isNotEmpty() + + /** True when the message has been edited since it was posted. */ + val isEdited: Boolean get() = editedAt != null } /** diff --git a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/components/EditMessageSheet.kt b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/components/EditMessageSheet.kt new file mode 100644 index 0000000..3a0cfe1 --- /dev/null +++ b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/components/EditMessageSheet.kt @@ -0,0 +1,96 @@ +package com.interlinedlist.android.feature.messages.ui.components + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.unit.dp + +/** Stable test tags for the in-place edit sheet. */ +object EditMessageSheetTags { + const val SHEET = "editMessageSheet" + const val INPUT = "editMessageInput" + const val SAVE = "editMessageSave" +} + +/** + * In-place editor for one of the caller's own messages: a bottom sheet seeded with + * the current content. Saving PATCHes the new text; the feed/detail update + * optimistically and gain an "edited" marker. + * + * @param onSave invoked when the user confirms the edit. + */ +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun EditMessageSheet( + text: String, + canSave: Boolean, + isSaving: Boolean, + onTextChange: (String) -> Unit, + onDismiss: () -> Unit, + onSave: () -> Unit, +) { + ModalBottomSheet( + onDismissRequest = onDismiss, + modifier = Modifier.testTag(EditMessageSheetTags.SHEET), + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .imePadding() + .padding(horizontal = 20.dp, vertical = 12.dp), + ) { + Text( + text = "Edit message", + style = MaterialTheme.typography.titleMedium, + ) + Spacer(Modifier.height(12.dp)) + OutlinedTextField( + value = text, + onValueChange = onTextChange, + placeholder = { Text("Edit your message…") }, + enabled = !isSaving, + minLines = 3, + modifier = Modifier + .fillMaxWidth() + .testTag(EditMessageSheetTags.INPUT), + ) + Spacer(Modifier.height(12.dp)) + Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) { + TextButton(onClick = onDismiss, enabled = !isSaving) { Text("Cancel") } + Spacer(Modifier.height(8.dp)) + Button( + onClick = onSave, + enabled = canSave, + modifier = Modifier.testTag(EditMessageSheetTags.SAVE), + ) { + if (isSaving) { + CircularProgressIndicator( + modifier = Modifier.height(20.dp), + strokeWidth = 2.dp, + color = MaterialTheme.colorScheme.onPrimary, + ) + } else { + Text("Save") + } + } + } + Spacer(Modifier.height(12.dp)) + } + } +} diff --git a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/components/MessageCard.kt b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/components/MessageCard.kt index 26ba171..246b6e7 100644 --- a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/components/MessageCard.kt +++ b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/components/MessageCard.kt @@ -45,8 +45,13 @@ object MessageCardTags { const val REPLY = "messageReply" const val MENU = "messageMenu" const val DELETE = "messageDelete" + const val EDIT = "messageEdit" const val REPORT = "messageReport" + const val BLOCK_USER = "messageBlockUser" + const val MUTE_USER = "messageMuteUser" + const val REPORT_USER = "messageReportUser" const val BODY = "messageBody" + const val EDITED = "messageEdited" } /** @@ -62,6 +67,10 @@ fun MessageCard( onDelete: () -> Unit, modifier: Modifier = Modifier, onReport: () -> Unit = {}, + onEdit: () -> Unit = {}, + onBlockUser: () -> Unit = {}, + onMuteUser: () -> Unit = {}, + onReportUser: () -> Unit = {}, onOpenLink: (String) -> Unit = {}, ) { Row( @@ -87,8 +96,24 @@ fun MessageCard( color = MaterialTheme.colorScheme.onSurfaceVariant, ) } + if (message.isEdited) { + Text( + text = " · edited", + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.testTag(MessageCardTags.EDITED), + ) + } Spacer(Modifier.weight(1f)) - MessageMenu(isMine = message.mine, onDelete = onDelete, onReport = onReport) + MessageMenu( + isMine = message.mine, + onEdit = onEdit, + onDelete = onDelete, + onReport = onReport, + onBlockUser = onBlockUser, + onMuteUser = onMuteUser, + onReportUser = onReportUser, + ) } Spacer(Modifier.size(4.dp)) Text( @@ -125,11 +150,20 @@ fun MessageCard( } /** - * Overflow menu: own messages offer Delete; everyone else's offer Report. Renders - * nothing when there is no applicable action (defensive; both branches are covered). + * Overflow menu. Own messages offer Edit + Delete. Everyone else's offer author + * moderation — Block user, Mute user, Report user — plus the existing Report + * (message) action. */ @Composable -private fun MessageMenu(isMine: Boolean, onDelete: () -> Unit, onReport: () -> Unit) { +private fun MessageMenu( + isMine: Boolean, + onEdit: () -> Unit, + onDelete: () -> Unit, + onReport: () -> Unit, + onBlockUser: () -> Unit, + onMuteUser: () -> Unit, + onReportUser: () -> Unit, +) { var expanded by remember { mutableStateOf(false) } Box { IconButton( @@ -140,6 +174,14 @@ private fun MessageMenu(isMine: Boolean, onDelete: () -> Unit, onReport: () -> U } DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) { if (isMine) { + DropdownMenuItem( + text = { Text("Edit") }, + onClick = { + expanded = false + onEdit() + }, + modifier = Modifier.testTag(MessageCardTags.EDIT), + ) DropdownMenuItem( text = { Text("Delete") }, onClick = { @@ -150,13 +192,37 @@ private fun MessageMenu(isMine: Boolean, onDelete: () -> Unit, onReport: () -> U ) } else { DropdownMenuItem( - text = { Text("Report") }, + text = { Text("Report message") }, onClick = { expanded = false onReport() }, modifier = Modifier.testTag(MessageCardTags.REPORT), ) + DropdownMenuItem( + text = { Text("Block user") }, + onClick = { + expanded = false + onBlockUser() + }, + modifier = Modifier.testTag(MessageCardTags.BLOCK_USER), + ) + DropdownMenuItem( + text = { Text("Mute user") }, + onClick = { + expanded = false + onMuteUser() + }, + modifier = Modifier.testTag(MessageCardTags.MUTE_USER), + ) + DropdownMenuItem( + text = { Text("Report user") }, + onClick = { + expanded = false + onReportUser() + }, + modifier = Modifier.testTag(MessageCardTags.REPORT_USER), + ) } } } diff --git a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/components/ModerationDialog.kt b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/components/ModerationDialog.kt new file mode 100644 index 0000000..4e720bd --- /dev/null +++ b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/components/ModerationDialog.kt @@ -0,0 +1,118 @@ +package com.interlinedlist.android.feature.messages.ui.components + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.selection.selectable +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.RadioButton +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.unit.dp +import com.interlinedlist.android.feature.messages.domain.ReportReason +import com.interlinedlist.android.feature.messages.ui.feed.ModerationAction +import com.interlinedlist.android.feature.messages.ui.feed.ModerationTarget + +/** Stable test tags for the author-moderation confirm dialog. */ +object ModerationDialogTags { + const val DIALOG = "moderationDialog" + const val DETAIL = "moderationDetail" + const val CONFIRM = "moderationConfirm" +} + +/** + * Confirmation dialog for an author-moderation action. Block and Mute are simple + * confirms; Report additionally collects a [ReportReason] (required) and optional + * free-text detail before it can be submitted. + * + * @param onConfirm invoked with the chosen reason (Report only) and detail. + */ +@Composable +fun ModerationDialog( + target: ModerationTarget, + isSubmitting: Boolean, + onDismiss: () -> Unit, + onConfirm: (ReportReason?, String) -> Unit, +) { + val label = target.authorLabel + val isReport = target.action == ModerationAction.REPORT + var reason by remember { mutableStateOf(null) } + var detail by remember { mutableStateOf("") } + + val (title, body, confirmLabel) = when (target.action) { + ModerationAction.BLOCK -> + Triple("Block $label?", "You won't see their messages, and they can't interact with you.", "Block") + ModerationAction.MUTE -> + Triple("Mute $label?", "Their messages will be hidden from your feed.", "Mute") + ModerationAction.REPORT -> + Triple("Report $label", "Why are you reporting this user?", "Report") + } + + AlertDialog( + onDismissRequest = onDismiss, + modifier = Modifier.testTag(ModerationDialogTags.DIALOG), + title = { Text(title) }, + text = { + Column { + Text( + text = body, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + if (isReport) { + Spacer(Modifier.height(8.dp)) + ReportReason.entries.forEach { option -> + Row( + modifier = Modifier + .fillMaxWidth() + .selectable( + selected = reason == option, + onClick = { reason = option }, + ) + .padding(vertical = 4.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + RadioButton(selected = reason == option, onClick = { reason = option }) + Text(option.label, style = MaterialTheme.typography.bodyMedium) + } + } + Spacer(Modifier.height(8.dp)) + OutlinedTextField( + value = detail, + onValueChange = { detail = it }, + placeholder = { Text("Add details (optional)") }, + enabled = !isSubmitting, + modifier = Modifier + .fillMaxWidth() + .testTag(ModerationDialogTags.DETAIL), + ) + } + } + }, + confirmButton = { + TextButton( + onClick = { onConfirm(reason, detail) }, + enabled = !isSubmitting && (!isReport || reason != null), + modifier = Modifier.testTag(ModerationDialogTags.CONFIRM), + ) { + Text(confirmLabel) + } + }, + dismissButton = { + TextButton(onClick = onDismiss, enabled = !isSubmitting) { Text("Cancel") } + }, + ) +} diff --git a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/detail/MessageDetailScreen.kt b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/detail/MessageDetailScreen.kt index 2f21587..3c9b6a0 100644 --- a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/detail/MessageDetailScreen.kt +++ b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/detail/MessageDetailScreen.kt @@ -38,8 +38,11 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.interlinedlist.android.core.designsystem.theme.InterlinedListTheme import com.interlinedlist.android.feature.messages.domain.Message import com.interlinedlist.android.feature.messages.domain.ReportReason +import com.interlinedlist.android.feature.messages.ui.components.EditMessageSheet import com.interlinedlist.android.feature.messages.ui.components.MessageCard +import com.interlinedlist.android.feature.messages.ui.components.ModerationDialog import com.interlinedlist.android.feature.messages.ui.components.ReportDialog +import com.interlinedlist.android.feature.messages.ui.feed.ModerationAction /** Stable test tags for the detail screen. */ object MessageDetailTags { @@ -76,9 +79,18 @@ fun MessageDetailRoute( onPostReply = viewModel::postReply, onRetry = viewModel::load, onReport = viewModel::openReport, + onEdit = viewModel::openEdit, + onBlockUser = { viewModel.openModeration(it, ModerationAction.BLOCK) }, + onMuteUser = { viewModel.openModeration(it, ModerationAction.MUTE) }, + onReportUser = { viewModel.openModeration(it, ModerationAction.REPORT) }, onFetchMetadata = { viewModel.onFetchMetadata() }, onDismissReport = viewModel::dismissReport, onSubmitReport = viewModel::submitReport, + onEditTextChange = viewModel::onEditTextChange, + onDismissEdit = viewModel::dismissEdit, + onSaveEdit = viewModel::saveEdit, + onDismissModeration = viewModel::dismissModeration, + onConfirmModeration = viewModel::confirmModeration, modifier = modifier, ) } @@ -96,9 +108,18 @@ fun MessageDetailScreen( onRetry: () -> Unit, modifier: Modifier = Modifier, onReport: (Message) -> Unit = {}, + onEdit: (Message) -> Unit = {}, + onBlockUser: (Message) -> Unit = {}, + onMuteUser: (Message) -> Unit = {}, + onReportUser: (Message) -> Unit = {}, onFetchMetadata: (Message) -> Unit = {}, onDismissReport: () -> Unit = {}, onSubmitReport: (ReportReason, String) -> Unit = { _, _ -> }, + onEditTextChange: (String) -> Unit = {}, + onDismissEdit: () -> Unit = {}, + onSaveEdit: () -> Unit = {}, + onDismissModeration: () -> Unit = {}, + onConfirmModeration: (ReportReason?, String) -> Unit = { _, _ -> }, ) { Scaffold( modifier = modifier @@ -128,6 +149,10 @@ fun MessageDetailScreen( onReplyTextChange = onReplyTextChange, onPostReply = onPostReply, onReport = onReport, + onEdit = onEdit, + onBlockUser = onBlockUser, + onMuteUser = onMuteUser, + onReportUser = onReportUser, onFetchMetadata = onFetchMetadata, ) } @@ -140,6 +165,26 @@ fun MessageDetailScreen( isSubmitting = state.isReporting, ) } + + if (state.editTarget != null) { + EditMessageSheet( + text = state.editText, + canSave = state.canSaveEdit, + isSaving = state.isSavingEdit, + onTextChange = onEditTextChange, + onDismiss = onDismissEdit, + onSave = onSaveEdit, + ) + } + + state.moderationTarget?.let { target -> + ModerationDialog( + target = target, + isSubmitting = state.isModerating, + onDismiss = onDismissModeration, + onConfirm = onConfirmModeration, + ) + } } @Composable @@ -151,6 +196,10 @@ private fun Content( onReplyTextChange: (String) -> Unit, onPostReply: () -> Unit, onReport: (Message) -> Unit, + onEdit: (Message) -> Unit, + onBlockUser: (Message) -> Unit, + onMuteUser: (Message) -> Unit, + onReportUser: (Message) -> Unit, onFetchMetadata: (Message) -> Unit, ) { val message = state.message @@ -172,6 +221,10 @@ private fun Content( onDig = onDig, onDelete = {}, onReport = { onReport(message) }, + onEdit = { onEdit(message) }, + onBlockUser = { onBlockUser(message) }, + onMuteUser = { onMuteUser(message) }, + onReportUser = { onReportUser(message) }, onOpenLink = { onFetchMetadata(message) }, ) HorizontalDivider(thickness = 2.dp, color = MaterialTheme.colorScheme.outlineVariant) @@ -189,6 +242,10 @@ private fun Content( onDig = {}, onDelete = {}, onReport = { onReport(reply) }, + onEdit = { onEdit(reply) }, + onBlockUser = { onBlockUser(reply) }, + onMuteUser = { onMuteUser(reply) }, + onReportUser = { onReportUser(reply) }, onOpenLink = { onFetchMetadata(reply) }, ) HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant) diff --git a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/detail/MessageDetailViewModel.kt b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/detail/MessageDetailViewModel.kt index 615805c..a11a22c 100644 --- a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/detail/MessageDetailViewModel.kt +++ b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/detail/MessageDetailViewModel.kt @@ -8,6 +8,8 @@ import com.interlinedlist.android.core.common.result.AppError import com.interlinedlist.android.feature.messages.data.MessagesRepository import com.interlinedlist.android.feature.messages.domain.Message import com.interlinedlist.android.feature.messages.domain.ReportReason +import com.interlinedlist.android.feature.messages.ui.feed.ModerationAction +import com.interlinedlist.android.feature.messages.ui.feed.ModerationTarget import com.interlinedlist.android.feature.messages.ui.isSubscriptionGate import com.interlinedlist.android.feature.messages.ui.toUserMessage import dagger.hilt.android.lifecycle.HiltViewModel @@ -35,8 +37,16 @@ data class MessageDetailUiState( /** The message (root or a reply) being reported, if any. */ val reportTarget: Message? = null, val isReporting: Boolean = false, + /** The message (root or a reply) being edited in-place, if any. */ + val editTarget: Message? = null, + val editText: String = "", + val isSavingEdit: Boolean = false, + /** A pending author-moderation action awaiting confirmation, if any. */ + val moderationTarget: ModerationTarget? = null, + val isModerating: Boolean = false, ) { val canReply: Boolean get() = replyText.isNotBlank() && !isPostingReply + val canSaveEdit: Boolean get() = editText.isNotBlank() && !isSavingEdit } private data class DetailTransientState( @@ -47,6 +57,11 @@ private data class DetailTransientState( val isPostingReply: Boolean = false, val reportTarget: Message? = null, val isReporting: Boolean = false, + val editTarget: Message? = null, + val editText: String = "", + val isSavingEdit: Boolean = false, + val moderationTarget: ModerationTarget? = null, + val isModerating: Boolean = false, ) @HiltViewModel @@ -77,6 +92,11 @@ class MessageDetailViewModel @Inject constructor( isPostingReply = t.isPostingReply, reportTarget = t.reportTarget, isReporting = t.isReporting, + editTarget = t.editTarget, + editText = t.editText, + isSavingEdit = t.isSavingEdit, + moderationTarget = t.moderationTarget, + isModerating = t.isModerating, ) }.stateIn( scope = viewModelScope, @@ -152,6 +172,66 @@ class MessageDetailViewModel @Inject constructor( } } + // --- edit own message -------------------------------------------------- + + fun openEdit(message: Message) = transient.update { + it.copy(editTarget = message, editText = message.content, errorMessage = null) + } + + fun onEditTextChange(value: String) = transient.update { it.copy(editText = value) } + + fun dismissEdit() = transient.update { + it.copy(editTarget = null, editText = "", isSavingEdit = false) + } + + fun saveEdit() { + val target = transient.value.editTarget ?: return + val text = transient.value.editText.trim() + if (text.isBlank()) return + transient.update { it.copy(isSavingEdit = true, errorMessage = null) } + viewModelScope.launch { + when (val result = repository.editMessage(target.id, text)) { + is ApiResult.Success -> transient.update { + it.copy(isSavingEdit = false, editTarget = null, editText = "") + } + is ApiResult.Failure -> transient.update { + it.copy(isSavingEdit = false).withError(result.error) + } + } + } + } + + // --- author moderation ------------------------------------------------- + + fun openModeration(message: Message, action: ModerationAction) = transient.update { + it.copy(moderationTarget = ModerationTarget(message, action), errorMessage = null) + } + + fun dismissModeration() = transient.update { + it.copy(moderationTarget = null, isModerating = false) + } + + fun confirmModeration(reason: ReportReason? = null, detail: String = "") { + val target = transient.value.moderationTarget ?: return + transient.update { it.copy(isModerating = true, errorMessage = null) } + viewModelScope.launch { + val result = when (target.action) { + ModerationAction.BLOCK -> repository.blockUser(target.username) + ModerationAction.MUTE -> repository.muteUser(target.username) + ModerationAction.REPORT -> + repository.reportUser(target.username, reason ?: ReportReason.OTHER, detail) + } + when (result) { + is ApiResult.Success -> transient.update { + it.copy(isModerating = false, moderationTarget = null) + } + is ApiResult.Failure -> transient.update { + it.copy(isModerating = false, moderationTarget = null).withError(result.error) + } + } + } + } + // --- link metadata ----------------------------------------------------- /** Fetches link-preview metadata for the current message; cache re-emits it. */ diff --git a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedScreen.kt b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedScreen.kt index f5feb47..cbef1ae 100644 --- a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedScreen.kt +++ b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedScreen.kt @@ -56,7 +56,9 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.interlinedlist.android.core.designsystem.theme.InterlinedListTheme import com.interlinedlist.android.feature.messages.domain.Message import com.interlinedlist.android.feature.messages.domain.ReportReason +import com.interlinedlist.android.feature.messages.ui.components.EditMessageSheet import com.interlinedlist.android.feature.messages.ui.components.MessageCard +import com.interlinedlist.android.feature.messages.ui.components.ModerationDialog import com.interlinedlist.android.feature.messages.ui.components.ReportDialog import com.interlinedlist.android.feature.messages.ui.readMediaBytes import java.time.Instant @@ -102,6 +104,10 @@ fun MessagesRoute( onDig = viewModel::onDig, onDelete = viewModel::onDelete, onReport = viewModel::openReport, + onEdit = viewModel::openEdit, + onBlockUser = { viewModel.openModeration(it, ModerationAction.BLOCK) }, + onMuteUser = { viewModel.openModeration(it, ModerationAction.MUTE) }, + onReportUser = { viewModel.openModeration(it, ModerationAction.REPORT) }, onFetchMetadata = viewModel::onFetchMetadata, onOpenCompose = viewModel::openCompose, onDismissCompose = viewModel::dismissCompose, @@ -118,6 +124,11 @@ fun MessagesRoute( onScheduleChange = viewModel::onScheduleChange, onDismissReport = viewModel::dismissReport, onSubmitReport = viewModel::submitReport, + onEditTextChange = viewModel::onEditTextChange, + onDismissEdit = viewModel::dismissEdit, + onSaveEdit = viewModel::saveEdit, + onDismissModeration = viewModel::dismissModeration, + onConfirmModeration = viewModel::confirmModeration, modifier = modifier, ) } @@ -139,12 +150,21 @@ fun MessagesFeedScreen( modifier: Modifier = Modifier, onOpenScheduled: () -> Unit = {}, onReport: (Message) -> Unit = {}, + onEdit: (Message) -> Unit = {}, + onBlockUser: (Message) -> Unit = {}, + onMuteUser: (Message) -> Unit = {}, + onReportUser: (Message) -> Unit = {}, onFetchMetadata: (Message) -> Unit = {}, onAttachMedia: (Uri, Boolean) -> Unit = { _, _ -> }, onRemoveAttachment: (PendingAttachment) -> Unit = {}, onScheduleChange: (String?) -> Unit = {}, onDismissReport: () -> Unit = {}, onSubmitReport: (ReportReason, String) -> Unit = { _, _ -> }, + onEditTextChange: (String) -> Unit = {}, + onDismissEdit: () -> Unit = {}, + onSaveEdit: () -> Unit = {}, + onDismissModeration: () -> Unit = {}, + onConfirmModeration: (ReportReason?, String) -> Unit = { _, _ -> }, ) { Scaffold( modifier = modifier.fillMaxSize(), @@ -186,6 +206,10 @@ fun MessagesFeedScreen( onDig = onDig, onDelete = onDelete, onReport = onReport, + onEdit = onEdit, + onBlockUser = onBlockUser, + onMuteUser = onMuteUser, + onReportUser = onReportUser, onFetchMetadata = onFetchMetadata, ) } @@ -210,6 +234,26 @@ fun MessagesFeedScreen( isSubmitting = state.isReporting, ) } + + if (state.editTarget != null) { + EditMessageSheet( + text = state.editText, + canSave = state.canSaveEdit, + isSaving = state.isSavingEdit, + onTextChange = onEditTextChange, + onDismiss = onDismissEdit, + onSave = onSaveEdit, + ) + } + + state.moderationTarget?.let { target -> + ModerationDialog( + target = target, + isSubmitting = state.isModerating, + onDismiss = onDismissModeration, + onConfirm = onConfirmModeration, + ) + } } @OptIn(ExperimentalMaterial3Api::class) @@ -223,6 +267,10 @@ private fun FeedContent( onDig: (Message) -> Unit, onDelete: (Message) -> Unit, onReport: (Message) -> Unit, + onEdit: (Message) -> Unit, + onBlockUser: (Message) -> Unit, + onMuteUser: (Message) -> Unit, + onReportUser: (Message) -> Unit, onFetchMetadata: (Message) -> Unit, ) { PullToRefreshBox( @@ -243,6 +291,10 @@ private fun FeedContent( onDig = onDig, onDelete = onDelete, onReport = onReport, + onEdit = onEdit, + onBlockUser = onBlockUser, + onMuteUser = onMuteUser, + onReportUser = onReportUser, onFetchMetadata = onFetchMetadata, ) } @@ -257,6 +309,10 @@ private fun FeedList( onDig: (Message) -> Unit, onDelete: (Message) -> Unit, onReport: (Message) -> Unit, + onEdit: (Message) -> Unit, + onBlockUser: (Message) -> Unit, + onMuteUser: (Message) -> Unit, + onReportUser: (Message) -> Unit, onFetchMetadata: (Message) -> Unit, ) { val listState = rememberLazyListState() @@ -282,6 +338,10 @@ private fun FeedList( onDig = { onDig(message) }, onDelete = { onDelete(message) }, onReport = { onReport(message) }, + onEdit = { onEdit(message) }, + onBlockUser = { onBlockUser(message) }, + onMuteUser = { onMuteUser(message) }, + onReportUser = { onReportUser(message) }, onOpenLink = { onFetchMetadata(message) }, ) HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant) diff --git a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedViewModel.kt b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedViewModel.kt index f0b2e05..531a40c 100644 --- a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedViewModel.kt +++ b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedViewModel.kt @@ -47,6 +47,14 @@ data class MessagesFeedUiState( /** The message currently being reported (drives the report dialog), if any. */ val reportTarget: Message? = null, val isReporting: Boolean = false, + /** The message currently being edited in-place (drives the edit sheet), if any. */ + val editTarget: Message? = null, + /** The working edit text seeded from [editTarget]'s content. */ + val editText: String = "", + val isSavingEdit: Boolean = false, + /** A pending author-moderation action awaiting confirmation, if any. */ + val moderationTarget: ModerationTarget? = null, + val isModerating: Boolean = false, ) { val isEmpty: Boolean get() = messages.isEmpty() val hasAttachments: Boolean get() = attachments.isNotEmpty() @@ -55,8 +63,26 @@ data class MessagesFeedUiState( val canPost: Boolean get() = (composeText.isNotBlank() || attachments.any { it.hostedUrl != null }) && !isPosting && !isUploading + + /** Edit can be saved when the text is non-blank and not currently saving. */ + val canSaveEdit: Boolean get() = editText.isNotBlank() && !isSavingEdit } +/** + * A pending author-moderation action, captured when the user taps Block / Mute / + * Report on someone else's message. Drives a confirm dialog before the call runs. + */ +data class ModerationTarget( + val message: Message, + val action: ModerationAction, +) { + val username: String get() = message.authorUsername + val authorLabel: String get() = message.authorLabel +} + +/** The three author-level moderation actions (distinct from reporting a message). */ +enum class ModerationAction { BLOCK, MUTE, REPORT } + /** Transient (non-cached) UI flags kept separate from the Room-backed message list. */ private data class FeedTransientState( val isRefreshing: Boolean = false, @@ -71,6 +97,11 @@ private data class FeedTransientState( val scheduledAt: String? = null, val reportTarget: Message? = null, val isReporting: Boolean = false, + val editTarget: Message? = null, + val editText: String = "", + val isSavingEdit: Boolean = false, + val moderationTarget: ModerationTarget? = null, + val isModerating: Boolean = false, ) @HiltViewModel @@ -100,6 +131,11 @@ class MessagesFeedViewModel @Inject constructor( scheduledAt = t.scheduledAt, reportTarget = t.reportTarget, isReporting = t.isReporting, + editTarget = t.editTarget, + editText = t.editText, + isSavingEdit = t.isSavingEdit, + moderationTarget = t.moderationTarget, + isModerating = t.isModerating, ) }.stateIn( scope = viewModelScope, @@ -271,6 +307,74 @@ class MessagesFeedViewModel @Inject constructor( } } + // --- edit own message -------------------------------------------------- + + /** Opens the in-place editor seeded with [message]'s current content. */ + fun openEdit(message: Message) = transient.update { + it.copy(editTarget = message, editText = message.content, errorMessage = null) + } + + fun onEditTextChange(value: String) = transient.update { it.copy(editText = value) } + + fun dismissEdit() = transient.update { + it.copy(editTarget = null, editText = "", isSavingEdit = false) + } + + /** Saves the edit: PATCHes the new content (repository updates the cache). */ + fun saveEdit() { + val target = transient.value.editTarget ?: return + val text = transient.value.editText.trim() + if (text.isBlank()) return + transient.update { it.copy(isSavingEdit = true, errorMessage = null) } + viewModelScope.launch { + when (val result = repository.editMessage(target.id, text)) { + is ApiResult.Success -> transient.update { + it.copy(isSavingEdit = false, editTarget = null, editText = "") + } + is ApiResult.Failure -> transient.update { + it.copy(isSavingEdit = false).withError(result.error) + } + } + } + } + + // --- author moderation ------------------------------------------------- + + /** Queues a Block / Mute / Report-user action for confirmation. */ + fun openModeration(message: Message, action: ModerationAction) = transient.update { + it.copy(moderationTarget = ModerationTarget(message, action), errorMessage = null) + } + + fun dismissModeration() = transient.update { + it.copy(moderationTarget = null, isModerating = false) + } + + /** + * Confirms the queued moderation action. Block/Mute additionally hide the + * author's messages from the local feed (handled in the repository); Report + * carries the chosen [reason] and optional [detail]. + */ + fun confirmModeration(reason: ReportReason? = null, detail: String = "") { + val target = transient.value.moderationTarget ?: return + transient.update { it.copy(isModerating = true, errorMessage = null) } + viewModelScope.launch { + val result = when (target.action) { + ModerationAction.BLOCK -> repository.blockUser(target.username) + ModerationAction.MUTE -> repository.muteUser(target.username) + ModerationAction.REPORT -> + repository.reportUser(target.username, reason ?: ReportReason.OTHER, detail) + } + when (result) { + is ApiResult.Success -> transient.update { + it.copy(isModerating = false, moderationTarget = null) + } + is ApiResult.Failure -> transient.update { + it.copy(isModerating = false, moderationTarget = null).withError(result.error) + } + } + } + } + // --- link metadata ----------------------------------------------------- /** Fetches link-preview metadata for a message; the cache Flow re-emits it. */ diff --git a/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/data/DefaultMessagesRepositoryTest.kt b/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/data/DefaultMessagesRepositoryTest.kt index 17aa29d..75b6ea9 100644 --- a/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/data/DefaultMessagesRepositoryTest.kt +++ b/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/data/DefaultMessagesRepositoryTest.kt @@ -369,6 +369,139 @@ class DefaultMessagesRepositoryTest { assertThat(body).doesNotContain("detail") } + @Test + fun `editMessage PATCHes the content and updates the cached message`() = runTest(dispatcher) { + enqueueJson( + 200, + """{ "data": [ { "id": "1", "content": "original", "author": { "id": "me", "username": "me" } } ], + "pagination": { "hasMore": false } }""", + ) + enqueueJson(200, "") // PATCH response body is not modelled; a 2xx is success. + val repo = repository() + repo.refreshFeed() + + val result = repo.editMessage("1", content = "edited body") + + assertThat(result).isInstanceOf(ApiResult.Success::class.java) + assertThat((result as ApiResult.Success).data.content).isEqualTo("edited body") + // The cache reflects the new content and now carries an "edited" marker. + val cached = repo.observeMessage("1").first() + assertThat(cached?.content).isEqualTo("edited body") + assertThat(cached?.isEdited).isTrue() + + server.takeRequest() // the refresh GET + val patch = server.takeRequest() + assertThat(patch.method).isEqualTo("PATCH") + assertThat(patch.path).contains("api/messages/1") + assertThat(patch.body.readUtf8()).contains("\"content\":\"edited body\"") + } + + @Test + fun `editMessage rolls back the cached content on failure`() = runTest(dispatcher) { + enqueueJson( + 200, + """{ "data": [ { "id": "1", "content": "original", "author": { "id": "me", "username": "me" } } ], + "pagination": { "hasMore": false } }""", + ) + enqueueJson(500, """{ "error": "boom" }""") + val repo = repository() + repo.refreshFeed() + + val result = repo.editMessage("1", content = "will not stick") + + assertThat(result).isInstanceOf(ApiResult.Failure::class.java) + val cached = repo.observeMessage("1").first() + assertThat(cached?.content).isEqualTo("original") + assertThat(cached?.isEdited).isFalse() + } + + @Test + fun `blockUser posts and hides the author's messages from the feed`() = runTest(dispatcher) { + enqueueJson( + 200, + """{ "data": [ + { "id": "1", "content": "by amy", "author": { "id": "a", "username": "amy" } }, + { "id": "2", "content": "by bob", "author": { "id": "b", "username": "bob" } } + ], "pagination": { "hasMore": false } }""", + ) + enqueueJson(201, "") + val repo = repository() + repo.refreshFeed() + + val result = repo.blockUser("amy") + + assertThat(result).isInstanceOf(ApiResult.Success::class.java) + val request = server.let { it.takeRequest(); it.takeRequest() } + assertThat(request.method).isEqualTo("POST") + assertThat(request.path).contains("api/users/amy/block") + // Amy's message is gone; bob's remains. + assertThat(repo.observeFeed().first().map { it.id }).containsExactly("2") + } + + @Test + fun `muteUser posts and hides the author's messages from the feed`() = runTest(dispatcher) { + enqueueJson( + 200, + """{ "data": [ + { "id": "1", "content": "by amy", "author": { "id": "a", "username": "amy" } } + ], "pagination": { "hasMore": false } }""", + ) + enqueueJson(201, "") + val repo = repository() + repo.refreshFeed() + + val result = repo.muteUser("amy") + + assertThat(result).isInstanceOf(ApiResult.Success::class.java) + server.takeRequest() + assertThat(server.takeRequest().path).contains("api/users/amy/mute") + assertThat(repo.observeFeed().first()).isEmpty() + } + + @Test + fun `blockUser failure leaves the feed intact`() = runTest(dispatcher) { + enqueueJson( + 200, + """{ "data": [ + { "id": "1", "content": "by amy", "author": { "id": "a", "username": "amy" } } + ], "pagination": { "hasMore": false } }""", + ) + enqueueJson(500, """{ "error": "boom" }""") + val repo = repository() + repo.refreshFeed() + + val result = repo.blockUser("amy") + + assertThat(result).isInstanceOf(ApiResult.Failure::class.java) + assertThat(repo.observeFeed().first().map { it.id }).containsExactly("1") + } + + @Test + fun `reportUser posts the reason and detail to the user report endpoint`() = runTest(dispatcher) { + enqueueJson(201, "") + val repo = repository() + + val result = repo.reportUser("amy", ReportReason.HARASSMENT, detail = "abusive dms") + + assertThat(result).isInstanceOf(ApiResult.Success::class.java) + val request = server.takeRequest() + assertThat(request.path).contains("api/users/amy/report") + val body = request.body.readUtf8() + assertThat(body).contains("\"reason\":\"harassment\"") + assertThat(body).contains("abusive dms") + } + + @Test + fun `reportUser omits blank detail`() = runTest(dispatcher) { + enqueueJson(201, "") + val repo = repository() + + repo.reportUser("amy", ReportReason.OTHER, detail = " ") + + val body = server.takeRequest().body.readUtf8() + assertThat(body).doesNotContain("detail") + } + @Test fun `fetchMetadata attaches a link preview to the cached message`() = runTest(dispatcher) { enqueueJson( diff --git a/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/data/FakeMessageDao.kt b/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/data/FakeMessageDao.kt index ca684ea..d395836 100644 --- a/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/data/FakeMessageDao.kt +++ b/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/data/FakeMessageDao.kt @@ -46,6 +46,10 @@ class FakeMessageDao : MessageDao { rows.value = rows.value.toMutableMap().apply { remove(id) } } + override suspend fun deleteByAuthorUsername(username: String) { + rows.value = rows.value.filterValues { it.authorUsername != username } + } + override suspend fun clearFeed() { rows.value = rows.value.filterValues { it.parentId != null || it.scheduledAt != null } } diff --git a/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/data/remote/dto/MessageDtoMapperTest.kt b/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/data/remote/dto/MessageDtoMapperTest.kt index f60a9e1..65c8df4 100644 --- a/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/data/remote/dto/MessageDtoMapperTest.kt +++ b/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/data/remote/dto/MessageDtoMapperTest.kt @@ -118,6 +118,43 @@ class MessageDtoMapperTest { assertThat(message.linkPreview).isNull() } + @Test + fun `flags an edited message when updatedAt differs from createdAt`() { + val message = MessageDto( + id = "m8", + content = "edited", + createdAt = "2026-07-18T10:00:00Z", + updatedAt = "2026-07-18T11:30:00Z", + ).toDomain(currentUserId = null) + + assertThat(message.isEdited).isTrue() + assertThat(message.editedAt).isEqualTo("2026-07-18T11:30:00Z") + } + + @Test + fun `does not flag as edited when updatedAt equals createdAt`() { + val message = MessageDto( + id = "m9", + content = "fresh", + createdAt = "2026-07-18T10:00:00Z", + updatedAt = "2026-07-18T10:00:00Z", + ).toDomain(currentUserId = null) + + assertThat(message.isEdited).isFalse() + assertThat(message.editedAt).isNull() + } + + @Test + fun `is not edited when updatedAt is absent`() { + val message = MessageDto( + id = "m10", + content = "no updatedAt", + createdAt = "2026-07-18T10:00:00Z", + ).toDomain(currentUserId = null) + + assertThat(message.isEdited).isFalse() + } + @Test fun `drops a link preview without a url`() { val message = MessageDto( diff --git a/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/ui/FakeMessagesRepository.kt b/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/ui/FakeMessagesRepository.kt index 44cb89f..15e9eff 100644 --- a/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/ui/FakeMessagesRepository.kt +++ b/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/ui/FakeMessagesRepository.kt @@ -29,6 +29,10 @@ class FakeMessagesRepository : MessagesRepository { var postReplyResult: ApiResult? = null var setDugResult: ApiResult = ApiResult.Success(Unit) var deleteResult: ApiResult = ApiResult.Success(Unit) + var editResult: ApiResult? = null + var blockResult: ApiResult = ApiResult.Success(Unit) + var muteResult: ApiResult = ApiResult.Success(Unit) + var reportUserResult: ApiResult = ApiResult.Success(Unit) var searchResult: ApiResult> = ApiResult.Success(emptyList()) var uploadImageResult: ApiResult = ApiResult.Success("https://cdn/image.png") var uploadVideoResult: ApiResult = ApiResult.Success("https://cdn/video.mp4") @@ -48,6 +52,10 @@ class FakeMessagesRepository : MessagesRepository { var cancelledScheduledIds = mutableListOf() var lastReport: ReportArgs? = null var metadataFetchedIds = mutableListOf() + var lastEdit: Pair? = null + var blockedUsernames = mutableListOf() + var mutedUsernames = mutableListOf() + var lastReportUser: ReportUserArgs? = null /** Snapshot of the arguments passed to the last [createMessage] call. */ data class CreateArgs( @@ -60,6 +68,9 @@ class FakeMessagesRepository : MessagesRepository { /** Snapshot of the arguments passed to the last [report] call. */ data class ReportArgs(val messageId: String, val reason: ReportReason, val detail: String?) + /** Snapshot of the arguments passed to the last [reportUser] call. */ + data class ReportUserArgs(val username: String, val reason: ReportReason, val detail: String?) + fun emitFeed(messages: List) { feed.value = messages } fun emitReplies(parentId: String, messages: List) { replies.value = replies.value + (parentId to messages) @@ -125,6 +136,17 @@ class FakeMessagesRepository : MessagesRepository { return deleteResult } + override suspend fun editMessage(messageId: String, content: String): ApiResult { + lastEdit = messageId to content + val result = editResult ?: ApiResult.Failure(AppError.Unknown("editResult not set")) + if (result is ApiResult.Success) { + // Reflect the edit into the observable feed/message so the UI re-emits. + feed.value = feed.value.map { if (it.id == messageId) result.data else it } + single.value = single.value + (messageId to result.data) + } + return result + } + override suspend fun refreshScheduled(): ApiResult { refreshScheduledCount++ return refreshScheduledResult @@ -140,6 +162,30 @@ class FakeMessagesRepository : MessagesRepository { return reportResult } + override suspend fun blockUser(username: String): ApiResult { + blockedUsernames += username + val result = blockResult + if (result is ApiResult.Success) { + // Mirror the repository's hide-on-block behaviour for ViewModel tests. + feed.value = feed.value.filterNot { it.authorUsername == username } + } + return result + } + + override suspend fun muteUser(username: String): ApiResult { + mutedUsernames += username + val result = muteResult + if (result is ApiResult.Success) { + feed.value = feed.value.filterNot { it.authorUsername == username } + } + return result + } + + override suspend fun reportUser(username: String, reason: ReportReason, detail: String?): ApiResult { + lastReportUser = ReportUserArgs(username, reason, detail) + return reportUserResult + } + override suspend fun fetchMetadata(messageId: String): ApiResult { metadataFetchedIds += messageId return metadataResult ?: ApiResult.Failure(AppError.Unknown("metadataResult not set")) @@ -160,11 +206,13 @@ fun sampleMessage( imageUrls: List = emptyList(), videoUrls: List = emptyList(), scheduledAt: String? = null, + authorUsername: String = "adron", + editedAt: String? = null, ) = Message( id = id, content = content, authorId = "u1", - authorUsername = "adron", + authorUsername = authorUsername, authorDisplayName = "Adron", authorAvatarUrl = null, createdAt = null, @@ -176,4 +224,5 @@ fun sampleMessage( imageUrls = imageUrls, videoUrls = videoUrls, scheduledAt = scheduledAt, + editedAt = editedAt, ) diff --git a/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/ui/detail/MessageDetailViewModelTest.kt b/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/ui/detail/MessageDetailViewModelTest.kt index 45450d6..815647a 100644 --- a/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/ui/detail/MessageDetailViewModelTest.kt +++ b/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/ui/detail/MessageDetailViewModelTest.kt @@ -6,6 +6,7 @@ import com.interlinedlist.android.core.common.result.ApiResult import com.interlinedlist.android.core.common.result.AppError import com.interlinedlist.android.feature.messages.domain.ReportReason import com.interlinedlist.android.feature.messages.ui.FakeMessagesRepository +import com.interlinedlist.android.feature.messages.ui.feed.ModerationAction import com.interlinedlist.android.feature.messages.ui.sampleMessage import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -148,6 +149,92 @@ class MessageDetailViewModelTest { assertThat(vm.uiState.value.reportTarget).isNull() } + @Test + fun `edit seeds and saves the current message content`() = runTest(dispatcher) { + val repo = FakeMessagesRepository().apply { + fetchResult = ApiResult.Success(sampleMessage(id = "m1", content = "original", mine = true)) + editResult = ApiResult.Success( + sampleMessage(id = "m1", content = "edited", mine = true, editedAt = "2026-07-31T12:00:00Z"), + ) + } + repo.emitMessage(sampleMessage(id = "m1", content = "original", mine = true)) + val vm = MessageDetailViewModel(repo, handle("m1")) + backgroundScope.launch { vm.uiState.collect {} } + advanceUntilIdle() + + vm.openEdit(sampleMessage(id = "m1", content = "original", mine = true)) + advanceUntilIdle() + assertThat(vm.uiState.value.editText).isEqualTo("original") + + vm.onEditTextChange("edited") + vm.saveEdit() + advanceUntilIdle() + + assertThat(repo.lastEdit).isEqualTo("m1" to "edited") + assertThat(vm.uiState.value.editTarget).isNull() + assertThat(vm.uiState.value.message?.content).isEqualTo("edited") + assertThat(vm.uiState.value.message?.isEdited).isTrue() + } + + @Test + fun `edit failure keeps the sheet open and shows an error`() = runTest(dispatcher) { + val repo = FakeMessagesRepository().apply { + fetchResult = ApiResult.Success(sampleMessage(id = "m1", mine = true)) + editResult = ApiResult.Failure(AppError.Server("nope")) + } + val vm = MessageDetailViewModel(repo, handle("m1")) + backgroundScope.launch { vm.uiState.collect {} } + advanceUntilIdle() + + vm.openEdit(sampleMessage(id = "m1", content = "original", mine = true)) + vm.onEditTextChange("changed") + vm.saveEdit() + advanceUntilIdle() + + assertThat(vm.uiState.value.editTarget?.id).isEqualTo("m1") + assertThat(vm.uiState.value.errorMessage).isNotEmpty() + } + + @Test + fun `block on a reply delegates to the repository`() = runTest(dispatcher) { + val repo = FakeMessagesRepository().apply { + fetchResult = ApiResult.Success(sampleMessage(id = "m1")) + } + val vm = MessageDetailViewModel(repo, handle("m1")) + backgroundScope.launch { vm.uiState.collect {} } + advanceUntilIdle() + + val reply = sampleMessage(id = "r1", parentId = "m1", authorUsername = "amy") + vm.openModeration(reply, ModerationAction.BLOCK) + advanceUntilIdle() + assertThat(vm.uiState.value.moderationTarget?.username).isEqualTo("amy") + + vm.confirmModeration() + advanceUntilIdle() + + assertThat(repo.blockedUsernames).containsExactly("amy") + assertThat(vm.uiState.value.moderationTarget).isNull() + } + + @Test + fun `report user submits the reason and detail`() = runTest(dispatcher) { + val repo = FakeMessagesRepository().apply { + fetchResult = ApiResult.Success(sampleMessage(id = "m1")) + } + val vm = MessageDetailViewModel(repo, handle("m1")) + backgroundScope.launch { vm.uiState.collect {} } + advanceUntilIdle() + + vm.openModeration(sampleMessage(id = "m1", authorUsername = "amy"), ModerationAction.REPORT) + vm.confirmModeration(ReportReason.SPAM, "spammer") + advanceUntilIdle() + + val report = repo.lastReportUser!! + assertThat(report.username).isEqualTo("amy") + assertThat(report.reason).isEqualTo(ReportReason.SPAM) + assertThat(report.detail).isEqualTo("spammer") + } + @Test fun `fetchMetadata delegates for the current message`() = runTest(dispatcher) { val repo = FakeMessagesRepository().apply { diff --git a/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedViewModelTest.kt b/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedViewModelTest.kt index c3e1616..b9a3ea2 100644 --- a/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedViewModelTest.kt +++ b/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedViewModelTest.kt @@ -286,6 +286,133 @@ class MessagesFeedViewModelTest { assertThat(vm.uiState.value.reportTarget).isNull() } + @Test + fun `edit seeds the sheet and saves the new content marking it edited`() = runTest(dispatcher) { + val repo = FakeMessagesRepository().apply { + editResult = ApiResult.Success( + sampleMessage(id = "own", content = "updated body", mine = true, editedAt = "2026-07-31T12:00:00Z"), + ) + } + repo.emitFeed(listOf(sampleMessage(id = "own", content = "original body", mine = true))) + val vm = MessagesFeedViewModel(repo) + backgroundScope.launch { vm.uiState.collect {} } + advanceUntilIdle() + + vm.openEdit(sampleMessage(id = "own", content = "original body", mine = true)) + advanceUntilIdle() + // Editor is seeded with the current content. + assertThat(vm.uiState.value.editTarget?.id).isEqualTo("own") + assertThat(vm.uiState.value.editText).isEqualTo("original body") + + vm.onEditTextChange("updated body") + vm.saveEdit() + advanceUntilIdle() + + assertThat(repo.lastEdit).isEqualTo("own" to "updated body") + // Sheet closed and the feed reflects the edited, marked message. + assertThat(vm.uiState.value.editTarget).isNull() + val edited = vm.uiState.value.messages.first { it.id == "own" } + assertThat(edited.content).isEqualTo("updated body") + assertThat(edited.isEdited).isTrue() + } + + @Test + fun `edit failure keeps the sheet open and surfaces an error`() = runTest(dispatcher) { + val repo = FakeMessagesRepository().apply { + editResult = ApiResult.Failure(AppError.Server("nope")) + } + val vm = MessagesFeedViewModel(repo) + backgroundScope.launch { vm.uiState.collect {} } + advanceUntilIdle() + + vm.openEdit(sampleMessage(id = "own", content = "original", mine = true)) + vm.onEditTextChange("changed") + vm.saveEdit() + advanceUntilIdle() + + val state = vm.uiState.value + assertThat(state.editTarget?.id).isEqualTo("own") + assertThat(state.isSavingEdit).isFalse() + assertThat(state.errorMessage).isNotEmpty() + } + + @Test + fun `block hides the author's messages from the feed`() = runTest(dispatcher) { + val repo = FakeMessagesRepository() + repo.emitFeed( + listOf( + sampleMessage(id = "1", authorUsername = "amy"), + sampleMessage(id = "2", authorUsername = "bob"), + ), + ) + val vm = MessagesFeedViewModel(repo) + backgroundScope.launch { vm.uiState.collect {} } + advanceUntilIdle() + + vm.openModeration(sampleMessage(id = "1", authorUsername = "amy"), ModerationAction.BLOCK) + advanceUntilIdle() + assertThat(vm.uiState.value.moderationTarget?.username).isEqualTo("amy") + + vm.confirmModeration() + advanceUntilIdle() + + assertThat(repo.blockedUsernames).containsExactly("amy") + assertThat(vm.uiState.value.moderationTarget).isNull() + assertThat(vm.uiState.value.messages.map { it.id }).containsExactly("2") + } + + @Test + fun `block failure surfaces an error and keeps the feed`() = runTest(dispatcher) { + val repo = FakeMessagesRepository().apply { + blockResult = ApiResult.Failure(AppError.Server("boom")) + } + repo.emitFeed(listOf(sampleMessage(id = "1", authorUsername = "amy"))) + val vm = MessagesFeedViewModel(repo) + backgroundScope.launch { vm.uiState.collect {} } + advanceUntilIdle() + + vm.openModeration(sampleMessage(id = "1", authorUsername = "amy"), ModerationAction.BLOCK) + vm.confirmModeration() + advanceUntilIdle() + + assertThat(vm.uiState.value.errorMessage).isNotEmpty() + assertThat(vm.uiState.value.messages.map { it.id }).containsExactly("1") + } + + @Test + fun `mute delegates to the repository`() = runTest(dispatcher) { + val repo = FakeMessagesRepository() + repo.emitFeed(listOf(sampleMessage(id = "1", authorUsername = "amy"))) + val vm = MessagesFeedViewModel(repo) + backgroundScope.launch { vm.uiState.collect {} } + advanceUntilIdle() + + vm.openModeration(sampleMessage(id = "1", authorUsername = "amy"), ModerationAction.MUTE) + vm.confirmModeration() + advanceUntilIdle() + + assertThat(repo.mutedUsernames).containsExactly("amy") + assertThat(vm.uiState.value.messages).isEmpty() + } + + @Test + fun `report user submits the chosen reason and detail`() = runTest(dispatcher) { + val repo = FakeMessagesRepository() + val vm = MessagesFeedViewModel(repo) + backgroundScope.launch { vm.uiState.collect {} } + advanceUntilIdle() + + vm.openModeration(sampleMessage(id = "1", authorUsername = "amy"), ModerationAction.REPORT) + vm.confirmModeration(ReportReason.HARASSMENT, "abusive") + advanceUntilIdle() + + val report = repo.lastReportUser!! + assertThat(report.username).isEqualTo("amy") + assertThat(report.reason).isEqualTo(ReportReason.HARASSMENT) + assertThat(report.detail).isEqualTo("abusive") + assertThat(vm.uiState.value.moderationTarget).isNull() + } + @Test fun `fetchMetadata delegates to the repository`() = runTest(dispatcher) { val repo = FakeMessagesRepository().apply { From b494b9529cdaa97a17774181e67c999976806a9d Mon Sep 17 00:00:00 2001 From: Adron Hall Date: Sun, 2 Aug 2026 12:59:52 -0700 Subject: [PATCH 3/5] feat(messages): cross-post compose targets (Milestone H, cross-posting half) Composer destinations row: InterlinedList + a toggle per already-linked network (GET /api/user/identities). Post includes targets via the real create fields (mastodonProviderIds[], crossPostToBluesky/LinkedIn/Twitter); surfaces the crossPosts response status. Empty state hints to link accounts on web. OAuth connect flow deferred (needs redirect URIs). 94 unit tests green; assemble green. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../ui/feed/MessagesFeedScreenTest.kt | 42 +++++ .../data/DefaultMessagesRepository.kt | 22 ++- .../messages/data/MessagesRepository.kt | 20 +- .../messages/data/remote/MessagesApi.kt | 9 + .../messages/data/remote/dto/IdentityDto.kt | 39 ++++ .../data/remote/dto/MessagesResponse.kt | 43 ++++- .../feature/messages/domain/CrossPost.kt | 133 ++++++++++++++ .../messages/ui/feed/MessagesFeedScreen.kt | 171 ++++++++++++++++++ .../messages/ui/feed/MessagesFeedViewModel.kt | 78 +++++++- .../data/DefaultMessagesRepositoryTest.kt | 153 +++++++++++++++- .../messages/ui/FakeMessagesRepository.kt | 32 +++- .../ui/feed/MessagesFeedViewModelTest.kt | 144 +++++++++++++++ 12 files changed, 870 insertions(+), 16 deletions(-) create mode 100644 feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/dto/IdentityDto.kt create mode 100644 feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/domain/CrossPost.kt diff --git a/feature/messages/src/androidTest/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedScreenTest.kt b/feature/messages/src/androidTest/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedScreenTest.kt index 584aa41..f61c7cf 100644 --- a/feature/messages/src/androidTest/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedScreenTest.kt +++ b/feature/messages/src/androidTest/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedScreenTest.kt @@ -4,12 +4,14 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.assertIsSelected import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.test.ext.junit.runners.AndroidJUnit4 import com.interlinedlist.android.core.designsystem.theme.InterlinedListTheme +import com.interlinedlist.android.feature.messages.domain.LinkedNetwork import com.interlinedlist.android.feature.messages.domain.Message import com.interlinedlist.android.feature.messages.ui.components.EditMessageSheetTags import com.interlinedlist.android.feature.messages.ui.components.MessageCardTags @@ -63,6 +65,14 @@ class MessagesFeedScreenTest { onDismissCompose = { state = state.copy(isComposeOpen = false) }, onComposeTextChange = { state = state.copy(composeText = it) }, onPost = {}, + onToggleNetwork = { id -> + val selected = if (id in state.selectedNetworkIds) { + state.selectedNetworkIds - id + } else { + state.selectedNetworkIds + id + } + state = state.copy(selectedNetworkIds = selected) + }, onReport = onReport, onEdit = onEdit, onBlockUser = onBlockUser, @@ -206,4 +216,36 @@ class MessagesFeedScreenTest { setFeed(MessagesFeedUiState(messages = listOf(message("1", "hi")))) composeRule.onNodeWithTag(MessagesFeedTags.SCHEDULED_ACTION).assertIsDisplayed() } + + @Test + fun destinationsRow_rendersInterlinedListAndLinkedNetwork_andTogglesIt() { + val linkedIn = LinkedNetwork(id = "l1", provider = "linkedin", providerUsername = "Adron Hall") + setFeed( + MessagesFeedUiState( + isComposeOpen = true, + composeText = "cross-post me", + linkedNetworks = listOf(linkedIn), + ), + ) + + // InterlinedList is always present; the linked network chip is offered too. + composeRule.onNodeWithTag(MessagesFeedTags.DESTINATION_IL).assertIsDisplayed() + composeRule.onNodeWithTag(MessagesFeedTags.destinationTag("l1")).assertIsDisplayed() + + // Tapping the LinkedIn chip selects it as a cross-post target. + composeRule.onNodeWithTag(MessagesFeedTags.destinationTag("l1")).performClick() + composeRule.onNodeWithTag(MessagesFeedTags.destinationTag("l1")).assertIsSelected() + } + + @Test + fun destinationsHint_isShown_whenNoNetworksAreLinked() { + setFeed( + MessagesFeedUiState( + isComposeOpen = true, + composeText = "hi", + linkedNetworks = emptyList(), + ), + ) + composeRule.onNodeWithTag(MessagesFeedTags.DESTINATIONS_HINT).assertIsDisplayed() + } } diff --git a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/DefaultMessagesRepository.kt b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/DefaultMessagesRepository.kt index 32951ee..b1bf7bc 100644 --- a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/DefaultMessagesRepository.kt +++ b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/DefaultMessagesRepository.kt @@ -14,6 +14,9 @@ import com.interlinedlist.android.feature.messages.data.remote.dto.ReportRequest import com.interlinedlist.android.feature.messages.data.remote.dto.UserReportRequest import com.interlinedlist.android.feature.messages.data.remote.dto.toDomain import com.interlinedlist.android.core.network.error.safeApiCall +import com.interlinedlist.android.feature.messages.domain.CreatedMessage +import com.interlinedlist.android.feature.messages.domain.CrossPostSelection +import com.interlinedlist.android.feature.messages.domain.LinkedNetwork import com.interlinedlist.android.feature.messages.domain.Message import com.interlinedlist.android.feature.messages.domain.ReportReason import kotlinx.coroutines.flow.Flow @@ -83,12 +86,19 @@ class DefaultMessagesRepository @Inject constructor( imageUrls: List, videoUrls: List, scheduledAt: String?, - ): ApiResult = withContext(dispatchers.io) { + crossPost: CrossPostSelection, + ): ApiResult = withContext(dispatchers.io) { val request = CreateMessageRequest( content = content, imageUrls = imageUrls.ifEmpty { null }, videoUrls = videoUrls.ifEmpty { null }, scheduledAt = scheduledAt, + // Encode cross-post targets per the create schema. explicitNulls=false + // drops these when empty/false, so a plain post keeps its original body. + mastodonProviderIds = crossPost.mastodonProviderIds.ifEmpty { null }, + crossPostToBluesky = crossPost.bluesky.takeIf { it }, + crossPostToLinkedIn = crossPost.linkedIn.takeIf { it }, + crossPostToTwitter = crossPost.twitter.takeIf { it }, ) when (val result = safeCall { api.createMessage(request) }) { is ApiResult.Success -> { @@ -101,12 +111,20 @@ class DefaultMessagesRepository @Inject constructor( val topOrder = (messageDao.maxFeedOrder() ?: 0L) messageDao.upsert(message.toEntity(feedOrder = topOrder - 1L)) } - ApiResult.Success(message) + val crossPosts = result.data.crossPosts.mapNotNull { it.toDomainOrNull() } + ApiResult.Success(CreatedMessage(message = message, crossPosts = crossPosts)) } is ApiResult.Failure -> result } } + override suspend fun getLinkedNetworks(): ApiResult> = withContext(dispatchers.io) { + when (val result = safeCall { api.getIdentities() }) { + is ApiResult.Success -> ApiResult.Success(result.data.identities.map { it.toDomain() }) + is ApiResult.Failure -> result + } + } + override suspend fun uploadImage( bytes: ByteArray, fileName: String, diff --git a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/MessagesRepository.kt b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/MessagesRepository.kt index f6fb2d2..5d0cf76 100644 --- a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/MessagesRepository.kt +++ b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/MessagesRepository.kt @@ -1,6 +1,9 @@ package com.interlinedlist.android.feature.messages.data import com.interlinedlist.android.core.common.result.ApiResult +import com.interlinedlist.android.feature.messages.domain.CreatedMessage +import com.interlinedlist.android.feature.messages.domain.CrossPostSelection +import com.interlinedlist.android.feature.messages.domain.LinkedNetwork import com.interlinedlist.android.feature.messages.domain.Message import com.interlinedlist.android.feature.messages.domain.ReportReason import kotlinx.coroutines.flow.Flow @@ -38,15 +41,26 @@ interface MessagesRepository { /** * Creates a new top-level message and caches it. Optionally attaches already - * uploaded [imageUrls] / [videoUrls] and defers publishing to [scheduledAt] - * (ISO-8601). A scheduled message does not enter the feed cache. + * uploaded [imageUrls] / [videoUrls], defers publishing to [scheduledAt] + * (ISO-8601), and cross-posts to the already-linked networks named by + * [crossPost] (InterlinedList-only when [CrossPostSelection.NONE]). A scheduled + * message does not enter the feed cache. Returns the created message plus any + * per-network cross-post delivery statuses the endpoint reported. */ suspend fun createMessage( content: String, imageUrls: List = emptyList(), videoUrls: List = emptyList(), scheduledAt: String? = null, - ): ApiResult + crossPost: CrossPostSelection = CrossPostSelection.NONE, + ): ApiResult + + /** + * The caller's already-linked social networks, offered as cross-post + * destinations in the composer. Read directly from the API (not cached); an + * empty list means nothing is linked yet. + */ + suspend fun getLinkedNetworks(): ApiResult> /** Uploads image [bytes] and returns the hosted URL to attach on compose. */ suspend fun uploadImage(bytes: ByteArray, fileName: String, mimeType: String): ApiResult diff --git a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/MessagesApi.kt b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/MessagesApi.kt index 8bc7ec3..fe84d22 100644 --- a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/MessagesApi.kt +++ b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/MessagesApi.kt @@ -3,6 +3,7 @@ package com.interlinedlist.android.feature.messages.data.remote import com.interlinedlist.android.feature.messages.data.remote.dto.CreateMessageRequest import com.interlinedlist.android.feature.messages.data.remote.dto.CreateMessageResponse import com.interlinedlist.android.feature.messages.data.remote.dto.EditMessageRequest +import com.interlinedlist.android.feature.messages.data.remote.dto.IdentitiesResponse import com.interlinedlist.android.feature.messages.data.remote.dto.MediaUploadResponse import com.interlinedlist.android.feature.messages.data.remote.dto.MessageResponse import com.interlinedlist.android.feature.messages.data.remote.dto.MessagesResponse @@ -90,6 +91,14 @@ interface MessagesApi { @GET("api/messages/scheduled") suspend fun getScheduled(): ScheduledMessagesResponse + /** + * The caller's already-linked social identities (Mastodon/LinkedIn/X/Bluesky), + * used to offer cross-post destinations in the composer. Linking new accounts + * is a web-only OAuth flow and is not exposed here. + */ + @GET("api/user/identities") + suspend fun getIdentities(): IdentitiesResponse + /** Reports a message with a reason (and optional free-text detail). */ @POST("api/messages/{id}/report") suspend fun report(@Path("id") id: String, @Body body: ReportRequest) diff --git a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/dto/IdentityDto.kt b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/dto/IdentityDto.kt new file mode 100644 index 0000000..3a7900d --- /dev/null +++ b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/dto/IdentityDto.kt @@ -0,0 +1,39 @@ +package com.interlinedlist.android.feature.messages.data.remote.dto + +import com.interlinedlist.android.feature.messages.domain.LinkedNetwork +import kotlinx.serialization.Serializable + +/** + * Response from `GET /api/user/identities`: + * `{ "identities": [ { id, provider, providerUsername, profileUrl, avatarUrl, + * connectedAt, lastVerifiedAt } ] }`. + * + * The endpoint isn't schema-modelled in the OpenAPI spec, so this mirrors the + * verified live shape; the shared Json `ignoreUnknownKeys`, so extra fields (e.g. + * `lastVerifiedAt`) are tolerated. + */ +@Serializable +data class IdentitiesResponse( + val identities: List = emptyList(), +) + +/** A single linked social identity. */ +@Serializable +data class IdentityDto( + val id: String, + val provider: String, + val providerUsername: String = "", + val profileUrl: String? = null, + val avatarUrl: String? = null, + val connectedAt: String? = null, +) + +/** Maps a wire identity into the domain [LinkedNetwork]. */ +fun IdentityDto.toDomain(): LinkedNetwork = LinkedNetwork( + id = id, + provider = provider, + providerUsername = providerUsername, + profileUrl = profileUrl, + avatarUrl = avatarUrl, + connectedAt = connectedAt, +) diff --git a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/dto/MessagesResponse.kt b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/dto/MessagesResponse.kt index 7e7460d..ab22db9 100644 --- a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/dto/MessagesResponse.kt +++ b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/data/remote/dto/MessagesResponse.kt @@ -1,5 +1,6 @@ package com.interlinedlist.android.feature.messages.data.remote.dto +import com.interlinedlist.android.feature.messages.domain.CrossPostStatus import kotlinx.serialization.Serializable /** @@ -36,19 +37,55 @@ data class MessageResponse( * here `message` is a human-readable status string ("Message created * successfully") and the created message is under [data]: * `{ message: "…", data: { …message… }, crossPosts: [...] }`. + * + * [crossPosts] carries per-network delivery status when the post targeted linked + * networks. Its shape is not modelled in the OpenAPI spec, so it is best-effort + * and defaults to empty (the shared Json `coerceInputValues`, so an explicit + * `null` also becomes the empty default). */ @Serializable data class CreateMessageResponse( val data: MessageDto, + val crossPosts: List = emptyList(), ) +/** + * Per-network delivery status entry in a create response's `crossPosts` array. + * Not schema-modelled, so every field is optional; the repository maps only + * entries that name a [provider]. + */ +@Serializable +data class CrossPostStatusDto( + val provider: String? = null, + val status: String? = null, + val url: String? = null, + val error: String? = null, +) { + /** Maps into the domain, or null when the entry names no provider. */ + fun toDomainOrNull(): CrossPostStatus? { + val networkProvider = provider?.takeIf { it.isNotBlank() } ?: return null + return CrossPostStatus( + provider = networkProvider, + status = status?.takeIf { it.isNotBlank() } ?: if (error != null) "failed" else "pending", + url = url, + error = error, + ) + } +} + /** * Request body for creating a message or posting a reply. * * [imageUrls] / [videoUrls] carry media previously uploaded via the upload * endpoints, and [scheduledAt] (ISO-8601) defers publishing to a future time. + * + * Cross-posting targets are encoded per the create schema: [mastodonProviderIds] + * lists the selected mastodon identity ids (a user may link several instances), + * while [crossPostToBluesky] / [crossPostToLinkedIn] / [crossPostToTwitter] are + * single boolean flags for the one-account networks. + * * Only non-null fields are serialised (the shared Json uses `explicitNulls = - * false`), so a plain post still sends just `{ content }`. + * false`), so a plain InterlinedList-only post still sends just `{ content }`. */ @Serializable data class CreateMessageRequest( @@ -57,6 +94,10 @@ data class CreateMessageRequest( val imageUrls: List? = null, val videoUrls: List? = null, val scheduledAt: String? = null, + val mastodonProviderIds: List? = null, + val crossPostToBluesky: Boolean? = null, + val crossPostToLinkedIn: Boolean? = null, + val crossPostToTwitter: Boolean? = null, ) /** diff --git a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/domain/CrossPost.kt b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/domain/CrossPost.kt new file mode 100644 index 0000000..e74f43e --- /dev/null +++ b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/domain/CrossPost.kt @@ -0,0 +1,133 @@ +package com.interlinedlist.android.feature.messages.domain + +/** + * A social network the current user has already linked on the web, returned by + * `GET /api/user/identities`. These are the candidate cross-post destinations the + * composer offers; linking new accounts (OAuth) happens on the web and is out of + * scope for the app. + * + * [provider] is the raw wire value (e.g. `mastodon:techhub.social`, `linkedin`); + * [networkProvider] normalises it to a known [NetworkProvider] for icon/label and, + * critically, to decide how the selection is encoded on the create request (a + * per-instance mastodon id vs. a single boolean flag for the others). + */ +data class LinkedNetwork( + /** The identity's stable id (used as a `mastodonProviderIds` entry). */ + val id: String, + /** Raw provider string from the API, e.g. `mastodon:techhub.social`. */ + val provider: String, + /** The handle/display name on that network, for the chip label. */ + val providerUsername: String, + val profileUrl: String? = null, + val avatarUrl: String? = null, + val connectedAt: String? = null, +) { + /** The normalised provider family, derived from [provider]'s prefix. */ + val networkProvider: NetworkProvider get() = NetworkProvider.fromWire(provider) + + /** + * A short label for the destination chip. Mastodon shows its instance host + * (e.g. `techhub.social`) since a user may link more than one; the others + * show the single network's display name. + */ + val chipLabel: String + get() = when (networkProvider) { + NetworkProvider.MASTODON -> provider.substringAfter(':', missingDelimiterValue = "Mastodon") + else -> networkProvider.displayName + } +} + +/** The cross-post networks the create endpoint understands. */ +enum class NetworkProvider(val displayName: String) { + MASTODON("Mastodon"), + LINKEDIN("LinkedIn"), + TWITTER("X"), + BLUESKY("Bluesky"), + /** Any linked provider the app doesn't yet know how to cross-post to. */ + OTHER("Other"); + + companion object { + /** + * Maps a raw `provider` value to a [NetworkProvider]. Mastodon arrives as + * `mastodon:` (per-instance), so match on the prefix; the rest are + * plain tokens. + */ + fun fromWire(provider: String): NetworkProvider = when { + provider.startsWith("mastodon", ignoreCase = true) -> MASTODON + provider.equals("linkedin", ignoreCase = true) -> LINKEDIN + provider.equals("twitter", ignoreCase = true) || + provider.equals("x", ignoreCase = true) -> TWITTER + provider.equals("bluesky", ignoreCase = true) -> BLUESKY + else -> OTHER + } + } +} + +/** + * The set of already-linked networks the composer has selected as cross-post + * targets for a post. Built from the toggled [LinkedNetwork]s; the repository + * translates it into the discrete create-request fields (a mastodon id list plus + * the per-network boolean flags). An empty selection means InterlinedList-only. + */ +data class CrossPostSelection( + /** Selected mastodon identity ids (a user may link several instances). */ + val mastodonProviderIds: List = emptyList(), + val bluesky: Boolean = false, + val linkedIn: Boolean = false, + val twitter: Boolean = false, +) { + /** True when at least one external network is targeted. */ + val hasTargets: Boolean + get() = mastodonProviderIds.isNotEmpty() || bluesky || linkedIn || twitter + + companion object { + val NONE = CrossPostSelection() + + /** Folds a set of selected [networks] into a [CrossPostSelection]. */ + fun from(networks: Collection): CrossPostSelection = CrossPostSelection( + mastodonProviderIds = networks + .filter { it.networkProvider == NetworkProvider.MASTODON } + .map { it.id }, + bluesky = networks.any { it.networkProvider == NetworkProvider.BLUESKY }, + linkedIn = networks.any { it.networkProvider == NetworkProvider.LINKEDIN }, + twitter = networks.any { it.networkProvider == NetworkProvider.TWITTER }, + ) + } +} + +/** + * Per-network delivery status for a cross-posted message, parsed from the create + * response's `crossPosts` array. The response shape is not modelled in the OpenAPI + * spec, so all fields are best-effort: [provider] identifies the target, [status] + * is a free-text state (e.g. `success`, `pending`, `failed`), and [url]/[error] + * are surfaced when present. + */ +data class CrossPostStatus( + val provider: String, + val status: String, + val url: String? = null, + val error: String? = null, +) { + val networkProvider: NetworkProvider get() = NetworkProvider.fromWire(provider) + + /** A human label for the network the post was sent to. */ + val label: String + get() = when (networkProvider) { + NetworkProvider.MASTODON -> provider.substringAfter(':', missingDelimiterValue = "Mastodon") + NetworkProvider.OTHER -> provider + else -> networkProvider.displayName + } + + val isSuccess: Boolean get() = status.equals("success", ignoreCase = true) || url != null && error == null + val isFailed: Boolean get() = status.equals("failed", ignoreCase = true) || error != null +} + +/** + * The outcome of creating a message: the created [message] plus any per-network + * [crossPosts] delivery statuses the create endpoint reported. [crossPosts] is + * empty for a plain InterlinedList-only post. + */ +data class CreatedMessage( + val message: Message, + val crossPosts: List = emptyList(), +) diff --git a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedScreen.kt b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedScreen.kt index cbef1ae..523a1c5 100644 --- a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedScreen.kt +++ b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedScreen.kt @@ -13,8 +13,11 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState @@ -24,10 +27,12 @@ import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Image import androidx.compose.material.icons.filled.Schedule import androidx.compose.material.icons.filled.Videocam +import androidx.compose.material.icons.filled.Check import androidx.compose.material3.AssistChip import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FilterChip import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon @@ -36,6 +41,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.TopAppBar @@ -54,6 +60,8 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.interlinedlist.android.core.designsystem.theme.InterlinedListTheme +import com.interlinedlist.android.feature.messages.domain.CrossPostStatus +import com.interlinedlist.android.feature.messages.domain.LinkedNetwork import com.interlinedlist.android.feature.messages.domain.Message import com.interlinedlist.android.feature.messages.domain.ReportReason import com.interlinedlist.android.feature.messages.ui.components.EditMessageSheet @@ -78,6 +86,17 @@ object MessagesFeedTags { const val COMPOSE_ADD_VIDEO = "messagesComposeAddVideo" const val COMPOSE_SCHEDULE = "messagesComposeSchedule" const val SCHEDULED_ACTION = "messagesFeedScheduledAction" + + /** The always-on InterlinedList destination chip. */ + const val DESTINATION_IL = "messagesComposeDestinationInterlinedList" + /** Prefix for a per-network destination chip; suffixed with the network id. */ + const val DESTINATION_PREFIX = "messagesComposeDestination_" + /** Hint shown when the account has no linked networks to cross-post to. */ + const val DESTINATIONS_HINT = "messagesComposeDestinationsHint" + /** Post-send banner listing per-network cross-post statuses. */ + const val CROSS_POST_STATUS = "messagesFeedCrossPostStatus" + + fun destinationTag(networkId: String): String = DESTINATION_PREFIX + networkId } /** @@ -122,6 +141,8 @@ fun MessagesRoute( }, onRemoveAttachment = viewModel::onRemoveAttachment, onScheduleChange = viewModel::onScheduleChange, + onToggleNetwork = viewModel::onToggleNetwork, + onDismissCrossPostStatuses = viewModel::dismissCrossPostStatuses, onDismissReport = viewModel::dismissReport, onSubmitReport = viewModel::submitReport, onEditTextChange = viewModel::onEditTextChange, @@ -158,6 +179,8 @@ fun MessagesFeedScreen( onAttachMedia: (Uri, Boolean) -> Unit = { _, _ -> }, onRemoveAttachment: (PendingAttachment) -> Unit = {}, onScheduleChange: (String?) -> Unit = {}, + onToggleNetwork: (String) -> Unit = {}, + onDismissCrossPostStatuses: () -> Unit = {}, onDismissReport: () -> Unit = {}, onSubmitReport: (ReportReason, String) -> Unit = { _, _ -> }, onEditTextChange: (String) -> Unit = {}, @@ -224,6 +247,14 @@ fun MessagesFeedScreen( onAttachMedia = onAttachMedia, onRemoveAttachment = onRemoveAttachment, onScheduleChange = onScheduleChange, + onToggleNetwork = onToggleNetwork, + ) + } + + if (state.crossPostStatuses.isNotEmpty()) { + CrossPostStatusBanner( + statuses = state.crossPostStatuses, + onDismiss = onDismissCrossPostStatuses, ) } @@ -427,6 +458,7 @@ private fun ComposeSheet( onAttachMedia: (Uri, Boolean) -> Unit, onRemoveAttachment: (PendingAttachment) -> Unit, onScheduleChange: (String?) -> Unit, + onToggleNetwork: (String) -> Unit, ) { val imagePicker = rememberLauncherForActivityResult( ActivityResultContracts.GetContent(), @@ -486,6 +518,14 @@ private fun ComposeSheet( ) } + Spacer(Modifier.height(12.dp)) + DestinationsRow( + networks = state.linkedNetworks, + selectedIds = state.selectedNetworkIds, + enabled = !state.isPosting, + onToggleNetwork = onToggleNetwork, + ) + Spacer(Modifier.height(12.dp)) Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) { TextButton(onClick = onDismiss, enabled = !state.isPosting) { Text("Cancel") } @@ -549,6 +589,137 @@ private fun AttachmentRow( } } +/** + * The cross-post destinations row: InterlinedList is always-on (rendered as a + * disabled, always-selected chip), followed by a toggle chip per already-linked + * network. When nothing is linked, a subtle hint points the user to the web to + * link accounts — the app does not build an OAuth connect flow. + */ +@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class) +@Composable +private fun DestinationsRow( + networks: List, + selectedIds: Set, + enabled: Boolean, + onToggleNetwork: (String) -> Unit, +) { + Column { + Text( + text = "Post to", + style = MaterialTheme.typography.labelMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + Spacer(Modifier.height(6.dp)) + FlowRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) { + // InterlinedList is always a destination; shown selected and locked. + FilterChip( + selected = true, + onClick = {}, + enabled = false, + label = { Text("InterlinedList") }, + leadingIcon = { + Icon(Icons.Filled.Check, contentDescription = null, modifier = Modifier.size(16.dp)) + }, + modifier = Modifier.testTag(MessagesFeedTags.DESTINATION_IL), + ) + networks.forEach { network -> + val isSelected = network.id in selectedIds + FilterChip( + selected = isSelected, + onClick = { onToggleNetwork(network.id) }, + enabled = enabled, + label = { Text(network.chipLabel) }, + leadingIcon = if (isSelected) { + { + Icon( + Icons.Filled.Check, + contentDescription = null, + modifier = Modifier.size(16.dp), + ) + } + } else { + null + }, + modifier = Modifier.testTag(MessagesFeedTags.destinationTag(network.id)), + ) + } + } + if (networks.isEmpty()) { + Spacer(Modifier.height(4.dp)) + Text( + text = "Link accounts on the web to cross-post.", + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.testTag(MessagesFeedTags.DESTINATIONS_HINT), + ) + } + } +} + +/** + * A brief, dismissible banner surfacing the per-network cross-post statuses + * returned by the create endpoint. Anchored to the bottom of the screen. + */ +@OptIn(ExperimentalLayoutApi::class) +@Composable +private fun CrossPostStatusBanner( + statuses: List, + onDismiss: () -> Unit, +) { + Box(Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) { + Surface( + tonalElevation = 3.dp, + color = MaterialTheme.colorScheme.surfaceVariant, + modifier = Modifier + .fillMaxWidth() + .navigationBarsPadding() + .padding(16.dp) + .testTag(MessagesFeedTags.CROSS_POST_STATUS), + ) { + Column(Modifier.padding(16.dp)) { + Text( + text = "Cross-post results", + style = MaterialTheme.typography.titleSmall, + ) + Spacer(Modifier.height(8.dp)) + statuses.forEach { status -> + Row( + Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + val icon = if (status.isFailed) Icons.Filled.Close else Icons.Filled.Check + val tint = when { + status.isFailed -> MaterialTheme.colorScheme.error + status.isSuccess -> MaterialTheme.colorScheme.primary + else -> MaterialTheme.colorScheme.onSurfaceVariant + } + Icon(icon, contentDescription = null, tint = tint, modifier = Modifier.size(16.dp)) + Text( + text = buildString { + append(status.label) + append(": ") + append( + when { + status.isFailed -> status.error ?: "Failed" + status.isSuccess -> "Posted" + else -> status.status + }, + ) + }, + style = MaterialTheme.typography.bodyMedium, + ) + } + } + Spacer(Modifier.height(4.dp)) + Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) { + TextButton(onClick = onDismiss) { Text("Dismiss") } + } + } + } + } +} + /** * Toggle chip for scheduling. To stay device- and dialog-independent (and easily * testable), tapping sets a fixed "1 hour from now" ISO time; tapping again clears diff --git a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedViewModel.kt b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedViewModel.kt index 531a40c..09d6af8 100644 --- a/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedViewModel.kt +++ b/feature/messages/src/main/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedViewModel.kt @@ -5,6 +5,9 @@ import androidx.lifecycle.viewModelScope import com.interlinedlist.android.core.common.result.ApiResult import com.interlinedlist.android.core.common.result.AppError import com.interlinedlist.android.feature.messages.data.MessagesRepository +import com.interlinedlist.android.feature.messages.domain.CrossPostSelection +import com.interlinedlist.android.feature.messages.domain.CrossPostStatus +import com.interlinedlist.android.feature.messages.domain.LinkedNetwork import com.interlinedlist.android.feature.messages.domain.Message import com.interlinedlist.android.feature.messages.domain.ReportReason import com.interlinedlist.android.feature.messages.ui.isSubscriptionGate @@ -44,6 +47,12 @@ data class MessagesFeedUiState( val attachments: List = emptyList(), /** Optional future send time (ISO-8601) for the in-progress compose. */ val scheduledAt: String? = null, + /** The caller's already-linked networks, offered as cross-post destinations. */ + val linkedNetworks: List = emptyList(), + /** Ids of the linked networks currently selected as cross-post targets. */ + val selectedNetworkIds: Set = emptySet(), + /** Per-network delivery statuses from the last successful post, if any. */ + val crossPostStatuses: List = emptyList(), /** The message currently being reported (drives the report dialog), if any. */ val reportTarget: Message? = null, val isReporting: Boolean = false, @@ -64,6 +73,9 @@ data class MessagesFeedUiState( get() = (composeText.isNotBlank() || attachments.any { it.hostedUrl != null }) && !isPosting && !isUploading + /** True when the account has no linked networks to cross-post to. */ + val hasNoLinkedNetworks: Boolean get() = linkedNetworks.isEmpty() + /** Edit can be saved when the text is non-blank and not currently saving. */ val canSaveEdit: Boolean get() = editText.isNotBlank() && !isSavingEdit } @@ -95,6 +107,9 @@ private data class FeedTransientState( val isPosting: Boolean = false, val attachments: List = emptyList(), val scheduledAt: String? = null, + val linkedNetworks: List = emptyList(), + val selectedNetworkIds: Set = emptySet(), + val crossPostStatuses: List = emptyList(), val reportTarget: Message? = null, val isReporting: Boolean = false, val editTarget: Message? = null, @@ -129,6 +144,9 @@ class MessagesFeedViewModel @Inject constructor( isPosting = t.isPosting, attachments = t.attachments, scheduledAt = t.scheduledAt, + linkedNetworks = t.linkedNetworks, + selectedNetworkIds = t.selectedNetworkIds, + crossPostStatuses = t.crossPostStatuses, reportTarget = t.reportTarget, isReporting = t.isReporting, editTarget = t.editTarget, @@ -145,6 +163,29 @@ class MessagesFeedViewModel @Inject constructor( init { refresh() + loadLinkedNetworks() + } + + /** + * Loads the caller's already-linked networks so the composer can offer them as + * cross-post destinations. Best-effort: a failure just leaves the list empty + * (the composer then shows the "link accounts on the web" hint) and does not + * surface a feed-level error. + */ + fun loadLinkedNetworks() { + viewModelScope.launch { + when (val result = repository.getLinkedNetworks()) { + is ApiResult.Success -> transient.update { state -> + // Prune any selections whose network is no longer linked. + val liveIds = result.data.map { it.id }.toSet() + state.copy( + linkedNetworks = result.data, + selectedNetworkIds = state.selectedNetworkIds.intersect(liveIds), + ) + } + is ApiResult.Failure -> Unit + } + } } fun refresh() { @@ -198,14 +239,36 @@ class MessagesFeedViewModel @Inject constructor( // --- compose sheet ----------------------------------------------------- - fun openCompose() = transient.update { it.copy(isComposeOpen = true, errorMessage = null) } + fun openCompose() = transient.update { + it.copy(isComposeOpen = true, errorMessage = null, crossPostStatuses = emptyList()) + } fun dismissCompose() = transient.update { - it.copy(isComposeOpen = false, composeText = "", attachments = emptyList(), scheduledAt = null) + it.copy( + isComposeOpen = false, + composeText = "", + attachments = emptyList(), + scheduledAt = null, + selectedNetworkIds = emptySet(), + ) } fun onComposeTextChange(value: String) = transient.update { it.copy(composeText = value) } + /** + * Toggles a linked network as a cross-post target for the in-progress compose. + * No-ops for an id that isn't currently linked. + */ + fun onToggleNetwork(networkId: String) = transient.update { state -> + if (state.linkedNetworks.none { it.id == networkId }) return@update state + val selected = if (networkId in state.selectedNetworkIds) { + state.selectedNetworkIds - networkId + } else { + state.selectedNetworkIds + networkId + } + state.copy(selectedNetworkIds = selected) + } + /** Sets (or clears with null) the future send time for the in-progress compose. */ fun onScheduleChange(isoTimestamp: String?) = transient.update { it.copy(scheduledAt = isoTimestamp) } @@ -260,7 +323,10 @@ class MessagesFeedViewModel @Inject constructor( if (snapshot.attachments.any { it.isUploading }) return val images = snapshot.attachments.filterNot { it.isVideo }.mapNotNull { it.hostedUrl } val videos = snapshot.attachments.filter { it.isVideo }.mapNotNull { it.hostedUrl } - transient.update { it.copy(isPosting = true, errorMessage = null) } + // Fold the selected linked networks into the cross-post request fields. + val selected = snapshot.linkedNetworks.filter { it.id in snapshot.selectedNetworkIds } + val crossPost = CrossPostSelection.from(selected) + transient.update { it.copy(isPosting = true, errorMessage = null, crossPostStatuses = emptyList()) } viewModelScope.launch { when ( val result = repository.createMessage( @@ -268,6 +334,7 @@ class MessagesFeedViewModel @Inject constructor( imageUrls = images, videoUrls = videos, scheduledAt = snapshot.scheduledAt, + crossPost = crossPost, ) ) { is ApiResult.Success -> transient.update { @@ -277,6 +344,8 @@ class MessagesFeedViewModel @Inject constructor( composeText = "", attachments = emptyList(), scheduledAt = null, + selectedNetworkIds = emptySet(), + crossPostStatuses = result.data.crossPosts, ) } is ApiResult.Failure -> transient.update { @@ -286,6 +355,9 @@ class MessagesFeedViewModel @Inject constructor( } } + /** Dismisses the post-send cross-post status banner. */ + fun dismissCrossPostStatuses() = transient.update { it.copy(crossPostStatuses = emptyList()) } + // --- report ------------------------------------------------------------ fun openReport(message: Message) = transient.update { it.copy(reportTarget = message, errorMessage = null) } diff --git a/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/data/DefaultMessagesRepositoryTest.kt b/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/data/DefaultMessagesRepositoryTest.kt index 75b6ea9..75dc8c5 100644 --- a/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/data/DefaultMessagesRepositoryTest.kt +++ b/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/data/DefaultMessagesRepositoryTest.kt @@ -4,6 +4,8 @@ import com.google.common.truth.Truth.assertThat import com.interlinedlist.android.core.common.result.ApiResult import com.interlinedlist.android.core.common.result.AppError import com.interlinedlist.android.feature.messages.data.remote.MessagesApi +import com.interlinedlist.android.feature.messages.domain.CrossPostSelection +import com.interlinedlist.android.feature.messages.domain.NetworkProvider import com.interlinedlist.android.feature.messages.domain.ReportReason import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -23,7 +25,13 @@ import retrofit2.Retrofit class DefaultMessagesRepositoryTest { private val dispatcher = StandardTestDispatcher() - private val json = Json { ignoreUnknownKeys = true; explicitNulls = false } + // Mirrors the production Json (see core:network NetworkModule): coerce explicit + // nulls (e.g. `crossPosts: null`) to the property's default rather than failing. + private val json = Json { + ignoreUnknownKeys = true + explicitNulls = false + coerceInputValues = true + } private lateinit var server: MockWebServer private lateinit var api: MessagesApi @@ -143,7 +151,7 @@ class DefaultMessagesRepositoryTest { val result = repo.createMessage("brand new") - assertThat((result as ApiResult.Success).data.id).isEqualTo("new") + assertThat((result as ApiResult.Success).data.message.id).isEqualTo("new") val ids = repo.observeFeed().first().map { it.id } assertThat(ids.first()).isEqualTo("new") } @@ -289,7 +297,7 @@ class DefaultMessagesRepositoryTest { imageUrls = listOf("https://cdn/a.png"), ) - assertThat((result as ApiResult.Success).data.imageUrls).containsExactly("https://cdn/a.png") + assertThat((result as ApiResult.Success).data.message.imageUrls).containsExactly("https://cdn/a.png") val body = server.takeRequest().body.readUtf8() assertThat(body).contains("https://cdn/a.png") assertThat(body).contains("imageUrls") @@ -307,7 +315,7 @@ class DefaultMessagesRepositoryTest { val result = repo.createMessage(content = "later", scheduledAt = "2026-07-19T09:00:00Z") - assertThat((result as ApiResult.Success).data.scheduledAt).isEqualTo("2026-07-19T09:00:00Z") + assertThat((result as ApiResult.Success).data.message.scheduledAt).isEqualTo("2026-07-19T09:00:00Z") assertThat(repo.observeFeed().first()).isEmpty() assertThat(repo.observeScheduled().first().map { it.id }).containsExactly("sch1") } @@ -502,6 +510,143 @@ class DefaultMessagesRepositoryTest { assertThat(body).doesNotContain("detail") } + // --- cross-posting ----------------------------------------------------- + + @Test + fun `getLinkedNetworks parses varied providers`() = runTest(dispatcher) { + enqueueJson( + 200, + """ + { "identities": [ + { "id": "m1", "provider": "mastodon:techhub.social", + "providerUsername": "crew@techhub.social", + "profileUrl": "https://techhub.social/@crew", "avatarUrl": null, + "connectedAt": "2026-04-07T16:35:32.476Z", "lastVerifiedAt": null }, + { "id": "l1", "provider": "linkedin", "providerUsername": "Adron Hall" }, + { "id": "t1", "provider": "twitter", "providerUsername": "interlinedlist" }, + { "id": "b1", "provider": "bluesky", "providerUsername": "il.bsky.social" } + ] } + """.trimIndent(), + ) + val repo = repository() + + val result = repo.getLinkedNetworks() + + val networks = (result as ApiResult.Success).data + assertThat(networks.map { it.id }).containsExactly("m1", "l1", "t1", "b1").inOrder() + assertThat(networks.map { it.networkProvider }).containsExactly( + NetworkProvider.MASTODON, + NetworkProvider.LINKEDIN, + NetworkProvider.TWITTER, + NetworkProvider.BLUESKY, + ).inOrder() + // The mastodon chip label surfaces the instance host. + assertThat(networks.first().chipLabel).isEqualTo("techhub.social") + assertThat(server.takeRequest().path).contains("api/user/identities") + } + + @Test + fun `getLinkedNetworks returns empty when nothing is linked`() = runTest(dispatcher) { + enqueueJson(200, """{ "identities": [] }""") + val repo = repository() + + val result = repo.getLinkedNetworks() + + assertThat((result as ApiResult.Success).data).isEmpty() + } + + @Test + fun `createMessage with targets sends the cross-post fields`() = runTest(dispatcher) { + enqueueJson( + 201, + """{ "message": "Message created successfully", + "data": { "id": "x1", "content": "cross-posted" } }""", + ) + val repo = repository() + + val result = repo.createMessage( + content = "cross-posted", + crossPost = CrossPostSelection( + mastodonProviderIds = listOf("m1"), + linkedIn = true, + twitter = true, + ), + ) + + assertThat(result).isInstanceOf(ApiResult.Success::class.java) + val body = server.takeRequest().body.readUtf8() + assertThat(body).contains("\"mastodonProviderIds\":[\"m1\"]") + assertThat(body).contains("\"crossPostToLinkedIn\":true") + assertThat(body).contains("\"crossPostToTwitter\":true") + // Unselected networks are omitted entirely (explicitNulls = false). + assertThat(body).doesNotContain("crossPostToBluesky") + } + + @Test + fun `createMessage without targets keeps the original body`() = runTest(dispatcher) { + enqueueJson( + 201, + """{ "message": "Message created successfully", + "data": { "id": "plain", "content": "just il" } }""", + ) + val repo = repository() + + repo.createMessage(content = "just il") + + val body = server.takeRequest().body.readUtf8() + assertThat(body).contains("\"content\":\"just il\"") + // None of the cross-post fields are present on a plain post. + assertThat(body).doesNotContain("mastodonProviderIds") + assertThat(body).doesNotContain("crossPostToBluesky") + assertThat(body).doesNotContain("crossPostToLinkedIn") + assertThat(body).doesNotContain("crossPostToTwitter") + } + + @Test + fun `createMessage parses the crossPosts statuses from the response`() = runTest(dispatcher) { + enqueueJson( + 201, + """{ "message": "Message created successfully", + "data": { "id": "x2", "content": "cross-posted" }, + "crossPosts": [ + { "provider": "linkedin", "status": "success", + "url": "https://linkedin.com/post/1" }, + { "provider": "mastodon:techhub.social", "status": "pending" }, + { "provider": "twitter", "status": "failed", "error": "rate limited" } + ] }""", + ) + val repo = repository() + + val result = repo.createMessage( + content = "cross-posted", + crossPost = CrossPostSelection(linkedIn = true, twitter = true), + ) + + val created = (result as ApiResult.Success).data + assertThat(created.crossPosts.map { it.provider }) + .containsExactly("linkedin", "mastodon:techhub.social", "twitter").inOrder() + val linkedIn = created.crossPosts.first { it.provider == "linkedin" } + assertThat(linkedIn.isSuccess).isTrue() + assertThat(linkedIn.url).isEqualTo("https://linkedin.com/post/1") + val twitter = created.crossPosts.first { it.provider == "twitter" } + assertThat(twitter.isFailed).isTrue() + assertThat(twitter.error).isEqualTo("rate limited") + } + + @Test + fun `createMessage defaults crossPosts to empty when absent`() = runTest(dispatcher) { + enqueueJson( + 201, + """{ "message": "Message created successfully", + "data": { "id": "x3", "content": "plain" }, "crossPosts": null }""", + ) + val repo = repository() + + val result = repo.createMessage(content = "plain") + + assertThat((result as ApiResult.Success).data.crossPosts).isEmpty() + } + @Test fun `fetchMetadata attaches a link preview to the cached message`() = runTest(dispatcher) { enqueueJson( diff --git a/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/ui/FakeMessagesRepository.kt b/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/ui/FakeMessagesRepository.kt index 15e9eff..c0928fb 100644 --- a/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/ui/FakeMessagesRepository.kt +++ b/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/ui/FakeMessagesRepository.kt @@ -3,6 +3,10 @@ package com.interlinedlist.android.feature.messages.ui import com.interlinedlist.android.core.common.result.ApiResult import com.interlinedlist.android.core.common.result.AppError import com.interlinedlist.android.feature.messages.data.MessagesRepository +import com.interlinedlist.android.feature.messages.domain.CreatedMessage +import com.interlinedlist.android.feature.messages.domain.CrossPostSelection +import com.interlinedlist.android.feature.messages.domain.CrossPostStatus +import com.interlinedlist.android.feature.messages.domain.LinkedNetwork import com.interlinedlist.android.feature.messages.domain.Message import com.interlinedlist.android.feature.messages.domain.ReportReason import kotlinx.coroutines.flow.Flow @@ -24,6 +28,9 @@ class FakeMessagesRepository : MessagesRepository { var refreshResult: ApiResult = ApiResult.Success(false) var loadMoreResult: ApiResult = ApiResult.Success(false) var createResult: ApiResult? = null + /** Cross-post statuses returned alongside a successful [createResult]. */ + var createCrossPosts: List = emptyList() + var linkedNetworksResult: ApiResult> = ApiResult.Success(emptyList()) var fetchResult: ApiResult? = null var refreshRepliesResult: ApiResult = ApiResult.Success(Unit) var postReplyResult: ApiResult? = null @@ -63,6 +70,7 @@ class FakeMessagesRepository : MessagesRepository { val imageUrls: List, val videoUrls: List, val scheduledAt: String?, + val crossPost: CrossPostSelection = CrossPostSelection.NONE, ) /** Snapshot of the arguments passed to the last [report] call. */ @@ -103,11 +111,18 @@ class FakeMessagesRepository : MessagesRepository { imageUrls: List, videoUrls: List, scheduledAt: String?, - ): ApiResult { - lastCreate = CreateArgs(content, imageUrls, videoUrls, scheduledAt) - return createResult ?: ApiResult.Failure(AppError.Unknown("createResult not set")) + crossPost: CrossPostSelection, + ): ApiResult { + lastCreate = CreateArgs(content, imageUrls, videoUrls, scheduledAt, crossPost) + return when (val result = createResult) { + is ApiResult.Success -> ApiResult.Success(CreatedMessage(result.data, createCrossPosts)) + is ApiResult.Failure -> result + null -> ApiResult.Failure(AppError.Unknown("createResult not set")) + } } + override suspend fun getLinkedNetworks(): ApiResult> = linkedNetworksResult + override suspend fun uploadImage(bytes: ByteArray, fileName: String, mimeType: String): ApiResult { uploadedImages++ return uploadImageResult @@ -194,6 +209,17 @@ class FakeMessagesRepository : MessagesRepository { override suspend fun search(query: String): ApiResult> = searchResult } +/** Builds a sample [LinkedNetwork] for tests. */ +fun sampleNetwork( + id: String, + provider: String, + providerUsername: String = "handle", +) = LinkedNetwork( + id = id, + provider = provider, + providerUsername = providerUsername, +) + /** Builds a sample [Message] for tests. */ fun sampleMessage( id: String = "1", diff --git a/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedViewModelTest.kt b/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedViewModelTest.kt index b9a3ea2..b354d92 100644 --- a/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedViewModelTest.kt +++ b/feature/messages/src/test/kotlin/com/interlinedlist/android/feature/messages/ui/feed/MessagesFeedViewModelTest.kt @@ -4,9 +4,11 @@ import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import com.interlinedlist.android.core.common.result.ApiResult import com.interlinedlist.android.core.common.result.AppError +import com.interlinedlist.android.feature.messages.domain.CrossPostStatus import com.interlinedlist.android.feature.messages.domain.ReportReason import com.interlinedlist.android.feature.messages.ui.FakeMessagesRepository import com.interlinedlist.android.feature.messages.ui.sampleMessage +import com.interlinedlist.android.feature.messages.ui.sampleNetwork import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch @@ -413,6 +415,148 @@ class MessagesFeedViewModelTest { assertThat(vm.uiState.value.moderationTarget).isNull() } + // --- cross-posting ----------------------------------------------------- + + @Test + fun `linked networks are loaded on init`() = runTest(dispatcher) { + val repo = FakeMessagesRepository().apply { + linkedNetworksResult = ApiResult.Success( + listOf( + sampleNetwork(id = "m1", provider = "mastodon:techhub.social"), + sampleNetwork(id = "l1", provider = "linkedin"), + ), + ) + } + val vm = MessagesFeedViewModel(repo) + + vm.uiState.test { + advanceUntilIdle() + val state = expectMostRecentItem() + assertThat(state.linkedNetworks.map { it.id }).containsExactly("m1", "l1").inOrder() + assertThat(state.hasNoLinkedNetworks).isFalse() + } + } + + @Test + fun `empty linked networks yields the no-networks state`() = runTest(dispatcher) { + val repo = FakeMessagesRepository().apply { + linkedNetworksResult = ApiResult.Success(emptyList()) + } + val vm = MessagesFeedViewModel(repo) + + vm.uiState.test { + advanceUntilIdle() + val state = expectMostRecentItem() + assertThat(state.linkedNetworks).isEmpty() + assertThat(state.hasNoLinkedNetworks).isTrue() + } + } + + @Test + fun `toggling a network selects then deselects it`() = runTest(dispatcher) { + val repo = FakeMessagesRepository().apply { + linkedNetworksResult = ApiResult.Success( + listOf(sampleNetwork(id = "l1", provider = "linkedin")), + ) + } + val vm = MessagesFeedViewModel(repo) + backgroundScope.launch { vm.uiState.collect {} } + advanceUntilIdle() + + vm.onToggleNetwork("l1") + advanceUntilIdle() + assertThat(vm.uiState.value.selectedNetworkIds).containsExactly("l1") + + vm.onToggleNetwork("l1") + advanceUntilIdle() + assertThat(vm.uiState.value.selectedNetworkIds).isEmpty() + } + + @Test + fun `post includes the selected networks as cross-post targets`() = runTest(dispatcher) { + val repo = FakeMessagesRepository().apply { + linkedNetworksResult = ApiResult.Success( + listOf( + sampleNetwork(id = "m1", provider = "mastodon:techhub.social"), + sampleNetwork(id = "l1", provider = "linkedin"), + sampleNetwork(id = "b1", provider = "bluesky"), + ), + ) + createResult = ApiResult.Success(sampleMessage(id = "new")) + } + val vm = MessagesFeedViewModel(repo) + backgroundScope.launch { vm.uiState.collect {} } + advanceUntilIdle() + + vm.openCompose() + vm.onComposeTextChange("hello networks") + vm.onToggleNetwork("m1") + vm.onToggleNetwork("l1") + advanceUntilIdle() + vm.post() + advanceUntilIdle() + + val crossPost = repo.lastCreate!!.crossPost + assertThat(crossPost.mastodonProviderIds).containsExactly("m1") + assertThat(crossPost.linkedIn).isTrue() + // Bluesky was never toggled. + assertThat(crossPost.bluesky).isFalse() + assertThat(crossPost.twitter).isFalse() + // Selection is cleared after a successful post. + assertThat(vm.uiState.value.selectedNetworkIds).isEmpty() + } + + @Test + fun `post with no targets sends an empty selection`() = runTest(dispatcher) { + val repo = FakeMessagesRepository().apply { + linkedNetworksResult = ApiResult.Success( + listOf(sampleNetwork(id = "l1", provider = "linkedin")), + ) + createResult = ApiResult.Success(sampleMessage(id = "new")) + } + val vm = MessagesFeedViewModel(repo) + backgroundScope.launch { vm.uiState.collect {} } + advanceUntilIdle() + + vm.openCompose() + vm.onComposeTextChange("just il") + vm.post() + advanceUntilIdle() + + assertThat(repo.lastCreate!!.crossPost.hasTargets).isFalse() + } + + @Test + fun `a successful cross-post surfaces the per-network statuses`() = runTest(dispatcher) { + val repo = FakeMessagesRepository().apply { + linkedNetworksResult = ApiResult.Success( + listOf(sampleNetwork(id = "l1", provider = "linkedin")), + ) + createResult = ApiResult.Success(sampleMessage(id = "new")) + createCrossPosts = listOf( + CrossPostStatus(provider = "linkedin", status = "success", url = "https://li/1"), + ) + } + val vm = MessagesFeedViewModel(repo) + backgroundScope.launch { vm.uiState.collect {} } + advanceUntilIdle() + + vm.openCompose() + vm.onComposeTextChange("cross-posted") + vm.onToggleNetwork("l1") + advanceUntilIdle() + vm.post() + advanceUntilIdle() + + val statuses = vm.uiState.value.crossPostStatuses + assertThat(statuses.map { it.provider }).containsExactly("linkedin") + assertThat(statuses.first().isSuccess).isTrue() + + vm.dismissCrossPostStatuses() + advanceUntilIdle() + assertThat(vm.uiState.value.crossPostStatuses).isEmpty() + } + @Test fun `fetchMetadata delegates to the repository`() = runTest(dispatcher) { val repo = FakeMessagesRepository().apply { From d1abe65d3eaf7a08faae7ad60f12e8f9c7b44f0d Mon Sep 17 00:00:00 2001 From: Adron Hall Date: Sun, 2 Aug 2026 14:41:22 -0700 Subject: [PATCH 4/5] feat(app): bootstrap WorkManager to run the documents delta-sync worker Make InterlinedListApplication a Configuration.Provider supplying the HiltWorkerFactory, remove the default androidx.startup WorkManager initializer (on-demand init), and schedule the periodic + one-shot delta-sync from the Documents tab, cancelling on sign-out. Device-verified: DocumentsSyncWorker runs to SUCCESS against the live API. Co-Authored-By: Claude Opus 4.8 (1M context) --- app/build.gradle.kts | 5 +++++ app/src/main/AndroidManifest.xml | 16 ++++++++++++++ .../android/InterlinedListApplication.kt | 22 +++++++++++++++++-- .../navigation/InterlinedListNavHost.kt | 17 ++++++++++++++ 4 files changed, 58 insertions(+), 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2868454..3f4747f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -80,6 +80,11 @@ dependencies { ksp(libs.hilt.compiler) implementation(libs.androidx.hilt.navigation.compose) + // WorkManager + Hilt worker factory (bootstraps the documents delta-sync @HiltWorker) + implementation(libs.androidx.work.runtime.ktx) + implementation(libs.androidx.hilt.work) + ksp(libs.androidx.hilt.compiler) + // Test testImplementation(libs.junit) androidTestImplementation(libs.androidx.test.ext.junit) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 759d7c7..43649bd 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,5 +49,21 @@ + + + + + diff --git a/app/src/main/java/com/interlinedlist/android/InterlinedListApplication.kt b/app/src/main/java/com/interlinedlist/android/InterlinedListApplication.kt index d33d7f7..14b9e7a 100644 --- a/app/src/main/java/com/interlinedlist/android/InterlinedListApplication.kt +++ b/app/src/main/java/com/interlinedlist/android/InterlinedListApplication.kt @@ -1,8 +1,26 @@ package com.interlinedlist.android import android.app.Application +import androidx.hilt.work.HiltWorkerFactory +import androidx.work.Configuration import dagger.hilt.android.HiltAndroidApp +import javax.inject.Inject -/** Application entry point; bootstraps the Hilt dependency graph. */ +/** + * Application entry point; bootstraps the Hilt dependency graph. + * + * Also supplies the WorkManager [Configuration] on demand (paired with removing the + * default `androidx.startup` WorkManager initializer in the manifest) so `@HiltWorker` + * instances — such as the documents delta-sync worker — can be constructed by Hilt. + */ @HiltAndroidApp -class InterlinedListApplication : Application() +class InterlinedListApplication : Application(), Configuration.Provider { + + @Inject + lateinit var workerFactory: HiltWorkerFactory + + override val workManagerConfiguration: Configuration + get() = Configuration.Builder() + .setWorkerFactory(workerFactory) + .build() +} diff --git a/app/src/main/java/com/interlinedlist/android/navigation/InterlinedListNavHost.kt b/app/src/main/java/com/interlinedlist/android/navigation/InterlinedListNavHost.kt index b39c2c7..5a8579a 100644 --- a/app/src/main/java/com/interlinedlist/android/navigation/InterlinedListNavHost.kt +++ b/app/src/main/java/com/interlinedlist/android/navigation/InterlinedListNavHost.kt @@ -13,9 +13,11 @@ import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.platform.LocalContext import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavDestination.Companion.hierarchy import androidx.navigation.NavGraph.Companion.findStartDestination @@ -36,6 +38,7 @@ import com.interlinedlist.android.feature.directmessages.navigation.navigateToNe import com.interlinedlist.android.feature.documents.ui.browser.DocumentsFolderRoute import com.interlinedlist.android.feature.documents.ui.browser.DocumentsRoute import com.interlinedlist.android.feature.documents.ui.editor.DocumentEditorRoute +import com.interlinedlist.android.feature.documents.sync.DocumentsSyncScheduler import com.interlinedlist.android.feature.documents.ui.share.DocumentShareRoute import com.interlinedlist.android.feature.documents.ui.share.SharedDocumentRoute import com.interlinedlist.android.feature.documents.ui.collaborators.DocumentCollaboratorsRoute @@ -197,8 +200,12 @@ fun InterlinedListNavHost(startLoggedIn: Boolean) { ) } composable(Routes.MAIN) { + val context = LocalContext.current MainShell( onLoggedOut = { + // Stop background document sync for the signed-out session. Cancellation + // must never crash the sign-out flow, so any failure is swallowed. + runCatching { DocumentsSyncScheduler.cancelAll(context) } navController.navigate(AuthRoutes.GRAPH) { popUpTo(Routes.MAIN) { inclusive = true } } @@ -345,6 +352,16 @@ private fun MainShell(onLoggedOut: () -> Unit) { // ---- Documents ---- composable(Routes.DOCUMENTS) { + // Bootstrap the documents delta-sync: register the periodic pull/push and + // kick a one-shot sync when the Documents tab is opened. Scheduling must + // never crash the UI, so any failure is swallowed defensively. + val context = LocalContext.current + LaunchedEffect(Unit) { + runCatching { + DocumentsSyncScheduler.schedulePeriodic(context) + DocumentsSyncScheduler.syncNow(context) + } + } DocumentsRoute( onOpenFolder = { id -> tabNav.navigate(Routes.documentFolder(id)) }, onOpenDocument = { id -> tabNav.navigate(Routes.documentEditor(id)) }, From 6091fbbff4806938e7834ce53f26509199ffa1a2 Mon Sep 17 00:00:00 2001 From: Adron Hall Date: Mon, 3 Aug 2026 00:32:20 -0700 Subject: [PATCH 5/5] feat(notifications): push notifications without Firebase (Milestone C) Background WorkManager poll of GET /api/notifications raises system-tray notifications (NotificationCompat, per-category channels, grouped, capped), tap deep-links into the app, respects the push channel in notification preferences, and persists a last-seen marker (no backlog dump / no re-notify). No FCM/Firebase/google-services. Scheduled on login, cancelled on sign-out; POST_NOTIFICATIONS requested on Android 13+. Also fixes a latent bug: NotificationsResponse never mapped the live 'items' key (only data/notifications), silently emptying the in-app feed AND the poll. Now maps items|data|notifications with a regression test. Device-verified: Documents/NotificationsPollWorker run to SUCCESS; forcing an older last-seen posts real tray notifications ('Adron Hall mentioned you...') in the il_mentions channel. Co-Authored-By: Claude Opus 4.8 (1M context) --- app/src/main/AndroidManifest.xml | 2 + .../android/InterlinedListApplication.kt | 11 +- .../interlinedlist/android/MainActivity.kt | 9 +- .../navigation/InterlinedListNavHost.kt | 53 ++++- .../android/navigation/NotificationLaunch.kt | 48 ++++ feature/notifications/build.gradle.kts | 9 + .../data/DefaultNotificationsRepository.kt | 7 + .../data/NotificationsRepository.kt | 7 + .../data/remote/dto/NotificationsResponse.kt | 7 +- .../notifications/di/NotificationsModule.kt | 18 ++ .../push/LastSeenNotificationStore.kt | 53 +++++ .../push/NewNotificationsSelector.kt | 68 ++++++ .../push/NotificationCategory.kt | 73 +++++++ .../push/NotificationDeepLink.kt | 65 ++++++ .../push/NotificationPollProcessor.kt | 50 +++++ .../push/NotificationPollRunner.kt | 61 ++++++ .../push/NotificationPushFilter.kt | 53 +++++ .../push/NotificationsPollWorker.kt | 35 +++ .../push/NotificationsSyncScheduler.kt | 70 ++++++ .../push/SystemNotificationChannels.kt | 36 +++ .../push/SystemNotificationPoster.kt | 153 +++++++++++++ .../res/drawable/il_notification_icon.xml | 15 ++ .../remote/dto/NotificationsResponseTest.kt | 13 ++ .../push/FakeLastSeenNotificationStore.kt | 19 ++ .../push/NewNotificationsSelectorTest.kt | 88 ++++++++ .../push/NotificationCategoryTest.kt | 48 ++++ .../push/NotificationDeepLinkTest.kt | 75 +++++++ .../push/NotificationPollProcessorTest.kt | 102 +++++++++ .../push/NotificationPollRunnerTest.kt | 205 ++++++++++++++++++ .../push/NotificationPushFilterTest.kt | 105 +++++++++ .../push/RecordingSystemNotificationRaiser.kt | 17 ++ .../ui/FakeNotificationsRepository.kt | 7 + 32 files changed, 1575 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/com/interlinedlist/android/navigation/NotificationLaunch.kt create mode 100644 feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/LastSeenNotificationStore.kt create mode 100644 feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NewNotificationsSelector.kt create mode 100644 feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationCategory.kt create mode 100644 feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationDeepLink.kt create mode 100644 feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationPollProcessor.kt create mode 100644 feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationPollRunner.kt create mode 100644 feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationPushFilter.kt create mode 100644 feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationsPollWorker.kt create mode 100644 feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationsSyncScheduler.kt create mode 100644 feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/SystemNotificationChannels.kt create mode 100644 feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/SystemNotificationPoster.kt create mode 100644 feature/notifications/src/main/res/drawable/il_notification_icon.xml create mode 100644 feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/FakeLastSeenNotificationStore.kt create mode 100644 feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/NewNotificationsSelectorTest.kt create mode 100644 feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationCategoryTest.kt create mode 100644 feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationDeepLinkTest.kt create mode 100644 feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationPollProcessorTest.kt create mode 100644 feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationPollRunnerTest.kt create mode 100644 feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationPushFilterTest.kt create mode 100644 feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/RecordingSystemNotificationRaiser.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 43649bd..b99fdcc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,8 @@ xmlns:tools="http://schemas.android.com/tools"> + + isSystemInDarkTheme() } InterlinedListTheme(darkTheme = darkTheme) { - InterlinedListNavHost(startLoggedIn = startLoggedIn) + InterlinedListNavHost( + startLoggedIn = startLoggedIn, + notificationRoute = notificationRoute, + ) } } } diff --git a/app/src/main/java/com/interlinedlist/android/navigation/InterlinedListNavHost.kt b/app/src/main/java/com/interlinedlist/android/navigation/InterlinedListNavHost.kt index 5a8579a..cf0ec9d 100644 --- a/app/src/main/java/com/interlinedlist/android/navigation/InterlinedListNavHost.kt +++ b/app/src/main/java/com/interlinedlist/android/navigation/InterlinedListNavHost.kt @@ -1,5 +1,10 @@ package com.interlinedlist.android.navigation +import android.Manifest +import android.content.pm.PackageManager +import android.os.Build +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.List @@ -15,9 +20,11 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext +import androidx.core.content.ContextCompat import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavDestination.Companion.hierarchy import androidx.navigation.NavGraph.Companion.findStartDestination @@ -59,6 +66,7 @@ import com.interlinedlist.android.feature.lists.ui.watchers.WatchersRoute import com.interlinedlist.android.feature.messages.ui.detail.MessageDetailRoute import com.interlinedlist.android.feature.messages.ui.feed.MessagesRoute import com.interlinedlist.android.feature.messages.ui.scheduled.ScheduledMessagesRoute +import com.interlinedlist.android.feature.notifications.push.NotificationsSyncScheduler import com.interlinedlist.android.feature.notifications.ui.NotificationPreferencesRoute import com.interlinedlist.android.feature.notifications.ui.NotificationsRoute import com.interlinedlist.android.feature.organizations.ui.detail.OrganizationDetailRoute @@ -180,7 +188,10 @@ private enum class HomeTab(val route: String, val label: String, val icon: Image * back stack; sign-out returns to login. */ @Composable -fun InterlinedListNavHost(startLoggedIn: Boolean) { +fun InterlinedListNavHost( + startLoggedIn: Boolean, + notificationRoute: String? = null, +) { val navController = rememberNavController() NavHost( navController = navController, @@ -202,10 +213,12 @@ fun InterlinedListNavHost(startLoggedIn: Boolean) { composable(Routes.MAIN) { val context = LocalContext.current MainShell( + notificationRoute = notificationRoute, onLoggedOut = { - // Stop background document sync for the signed-out session. Cancellation + // Stop background sync/poll for the signed-out session. Cancellation // must never crash the sign-out flow, so any failure is swallowed. runCatching { DocumentsSyncScheduler.cancelAll(context) } + runCatching { NotificationsSyncScheduler.cancelAll(context) } navController.navigate(AuthRoutes.GRAPH) { popUpTo(Routes.MAIN) { inclusive = true } } @@ -221,11 +234,45 @@ fun InterlinedListNavHost(startLoggedIn: Boolean) { * their own back navigation. */ @Composable -private fun MainShell(onLoggedOut: () -> Unit) { +private fun MainShell( + notificationRoute: String? = null, + onLoggedOut: () -> Unit, +) { val tabNav = rememberNavController() val backStackEntry by tabNav.currentBackStackEntryAsState() val currentRoute = backStackEntry?.destination?.route val onTabRoot = HomeTab.entries.any { it.route == currentRoute } + val context = LocalContext.current + + // Bootstrap the notification poll for the signed-in session: register the periodic + // near-real-time poll and kick a one-shot so the last-seen marker seeds immediately. + // On Android 13+ request POST_NOTIFICATIONS first (silently ignored below 13, where + // the permission does not exist). Scheduling must never crash the shell, so failures + // are swallowed. Runs once when the shell enters. + val requestNotificationsPermission = rememberLauncherForActivityResult( + ActivityResultContracts.RequestPermission(), + ) { /* result ignored: the poll still runs; posting is a no-op if denied */ } + LaunchedEffect(Unit) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + val granted = ContextCompat.checkSelfPermission( + context, + Manifest.permission.POST_NOTIFICATIONS, + ) == PackageManager.PERMISSION_GRANTED + if (!granted) requestNotificationsPermission.launch(Manifest.permission.POST_NOTIFICATIONS) + } + runCatching { + NotificationsSyncScheduler.schedulePeriodic(context) + NotificationsSyncScheduler.syncNow(context) + } + } + + // Route straight to a tapped notification's destination once, when present. + val pendingRoute by rememberUpdatedState(notificationRoute) + LaunchedEffect(Unit) { + pendingRoute?.let { route -> + runCatching { tabNav.navigate(route) } + } + } Scaffold( bottomBar = { diff --git a/app/src/main/java/com/interlinedlist/android/navigation/NotificationLaunch.kt b/app/src/main/java/com/interlinedlist/android/navigation/NotificationLaunch.kt new file mode 100644 index 0000000..4034325 --- /dev/null +++ b/app/src/main/java/com/interlinedlist/android/navigation/NotificationLaunch.kt @@ -0,0 +1,48 @@ +package com.interlinedlist.android.navigation + +import android.content.Intent +import com.interlinedlist.android.feature.notifications.push.NotificationDeepLink + +/** + * A pending in-app destination derived from a tapped system notification. The + * notifications module attaches [NotificationDeepLink] extras to the launch intent; + * this parses them into a concrete nav route (falling back to the notifications feed), + * so the tap lands the signed-in user on the right screen. + */ +data class NotificationLaunch(val route: String) { + + companion object { + + /** + * Parses [intent] into a [NotificationLaunch], or null when it did not + * originate from a notification tap. Unresolvable targets fall back to the + * in-app notifications feed. + */ + fun fromIntent(intent: Intent?): NotificationLaunch? { + if (intent?.getBooleanExtra(NotificationDeepLink.EXTRA_FROM_NOTIFICATION, false) != true) { + return null + } + val destination = runCatching { + NotificationDeepLink.Destination.valueOf( + intent.getStringExtra(NotificationDeepLink.EXTRA_DESTINATION).orEmpty(), + ) + }.getOrDefault(NotificationDeepLink.Destination.NOTIFICATIONS) + + val targetId = intent.getStringExtra(NotificationDeepLink.EXTRA_TARGET_ID) + return NotificationLaunch(routeFor(destination, targetId)) + } + + private fun routeFor( + destination: NotificationDeepLink.Destination, + targetId: String?, + ): String = when (destination) { + NotificationDeepLink.Destination.MESSAGE -> + targetId?.let { Routes.messageDetail(it) } ?: Routes.NOTIFICATIONS + NotificationDeepLink.Destination.USER -> + targetId?.let { Routes.userProfile(it) } ?: Routes.NOTIFICATIONS + NotificationDeepLink.Destination.LIST -> + targetId?.let { Routes.listDetail(it) } ?: Routes.NOTIFICATIONS + NotificationDeepLink.Destination.NOTIFICATIONS -> Routes.NOTIFICATIONS + } + } +} diff --git a/feature/notifications/build.gradle.kts b/feature/notifications/build.gradle.kts index 827ee9f..f0f0b30 100644 --- a/feature/notifications/build.gradle.kts +++ b/feature/notifications/build.gradle.kts @@ -32,6 +32,10 @@ dependencies { implementation(project(":core:network")) implementation(project(":core:datastore")) + // core-ktx provides NotificationManagerCompat / NotificationCompat and + // getSystemService used by the system-notification poll (push tray). + implementation(libs.androidx.core.ktx) + implementation(platform(libs.androidx.compose.bom)) implementation(libs.androidx.compose.ui) implementation(libs.androidx.compose.material3) @@ -53,6 +57,11 @@ dependencies { ksp(libs.hilt.compiler) implementation(libs.androidx.hilt.navigation.compose) + // Background notification polling via WorkManager, with Hilt-injected workers. + implementation(libs.androidx.work.runtime.ktx) + implementation(libs.androidx.hilt.work) + ksp(libs.androidx.hilt.compiler) + implementation(libs.coil.compose) implementation(libs.retrofit.core) diff --git a/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/data/DefaultNotificationsRepository.kt b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/data/DefaultNotificationsRepository.kt index 72c20e2..c8e8d7d 100644 --- a/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/data/DefaultNotificationsRepository.kt +++ b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/data/DefaultNotificationsRepository.kt @@ -30,6 +30,13 @@ class DefaultNotificationsRepository @Inject constructor( override fun observeUnreadCount(): Flow = notificationDao.observeUnreadCount() + override suspend fun fetchLatest(): ApiResult> = withContext(dispatchers.io) { + when (val result = safeCall { api.getNotifications(limit = PaginationDto.DEFAULT_LIMIT, offset = 0) }) { + is ApiResult.Success -> ApiResult.Success(result.data.items.map { it.toDomain() }) + is ApiResult.Failure -> result + } + } + override suspend fun refresh(): ApiResult = withContext(dispatchers.io) { when (val result = safeCall { api.getNotifications(limit = PaginationDto.DEFAULT_LIMIT, offset = 0) }) { is ApiResult.Success -> { diff --git a/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/data/NotificationsRepository.kt b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/data/NotificationsRepository.kt index 14d128d..0566ada 100644 --- a/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/data/NotificationsRepository.kt +++ b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/data/NotificationsRepository.kt @@ -21,6 +21,13 @@ interface NotificationsRepository { /** Live count of unread notifications (drives the badge/header styling). */ fun observeUnreadCount(): Flow + /** + * Fetches the first page of notifications from the API as domain models WITHOUT + * touching the Room cache. Used by the background poll worker, which must not + * disturb the offline-first feed the UI observes. + */ + suspend fun fetchLatest(): ApiResult> + /** * Refreshes the first page from the API and replaces the cached list. * Returns whether more pages are available. diff --git a/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/data/remote/dto/NotificationsResponse.kt b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/data/remote/dto/NotificationsResponse.kt index 87fe0fd..3df79ce 100644 --- a/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/data/remote/dto/NotificationsResponse.kt +++ b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/data/remote/dto/NotificationsResponse.kt @@ -1,5 +1,6 @@ package com.interlinedlist.android.feature.notifications.data.remote.dto +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable /** @@ -12,6 +13,8 @@ import kotlinx.serialization.Serializable */ @Serializable data class NotificationsResponse( + /** Primary key the live API uses: `{ unreadCount, items: [...] }`. */ + @SerialName("items") private val itemsKey: List = emptyList(), val data: List = emptyList(), /** Alternate key some payloads use for the list. */ val notifications: List = emptyList(), @@ -19,8 +22,8 @@ data class NotificationsResponse( /** Server-provided unread count, when present; otherwise derived from [items]. */ val unreadCount: Int? = null, ) { - /** The notification list, whichever key the server populated. */ - val items: List get() = data.ifEmpty { notifications } + /** The notification list, whichever key the server populated (`items`, `data`, or `notifications`). */ + val items: List get() = itemsKey.ifEmpty { data.ifEmpty { notifications } } } /** Pagination cursor returned alongside a list of notifications. */ diff --git a/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/di/NotificationsModule.kt b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/di/NotificationsModule.kt index ae5c8b1..4a9311f 100644 --- a/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/di/NotificationsModule.kt +++ b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/di/NotificationsModule.kt @@ -7,6 +7,10 @@ import com.interlinedlist.android.feature.notifications.data.NotificationsReposi import com.interlinedlist.android.feature.notifications.data.local.NotificationDao import com.interlinedlist.android.feature.notifications.data.local.NotificationsDatabase import com.interlinedlist.android.feature.notifications.data.remote.NotificationsApi +import com.interlinedlist.android.feature.notifications.push.LastSeenNotificationStore +import com.interlinedlist.android.feature.notifications.push.SharedPrefsLastSeenNotificationStore +import com.interlinedlist.android.feature.notifications.push.SystemNotificationPoster +import com.interlinedlist.android.feature.notifications.push.SystemNotificationRaiser import dagger.Binds import dagger.Module import dagger.Provides @@ -26,6 +30,13 @@ abstract class NotificationsRepositoryModule { abstract fun bindNotificationsRepository( impl: DefaultNotificationsRepository, ): NotificationsRepository + + /** Persists the background-poll last-seen marker (see NotificationsPollWorker). */ + @Binds + @Singleton + abstract fun bindLastSeenNotificationStore( + impl: SharedPrefsLastSeenNotificationStore, + ): LastSeenNotificationStore } /** @@ -56,4 +67,11 @@ object NotificationsDataModule { @Provides fun provideNotificationDao(db: NotificationsDatabase): NotificationDao = db.notificationDao() + + /** The system-tray poster used by the background notification poll worker. */ + @Provides + @Singleton + fun provideSystemNotificationRaiser( + @ApplicationContext context: Context, + ): SystemNotificationRaiser = SystemNotificationPoster(context) } diff --git a/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/LastSeenNotificationStore.kt b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/LastSeenNotificationStore.kt new file mode 100644 index 0000000..01bf1c9 --- /dev/null +++ b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/LastSeenNotificationStore.kt @@ -0,0 +1,53 @@ +package com.interlinedlist.android.feature.notifications.push + +import android.content.Context +import android.content.SharedPreferences +import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Inject +import javax.inject.Singleton + +/** + * Persists the "last-seen" marker for the background notification poll so the + * worker only raises a system notification for items that arrived since the + * previous run — never re-notifying on every poll. + * + * The marker is the newest notification id the poll has already surfaced. It is a + * tiny scalar with no secrets, so it lives in plain SharedPreferences (mirroring + * [com.interlinedlist.android.core.datastore.ThemeSettingsStore]). Modelled behind + * an interface so the worker's selection logic can be unit-tested with an in-memory + * fake, with no Android dependency. + */ +interface LastSeenNotificationStore { + + /** The id of the newest notification already surfaced, or null on first run. */ + fun lastSeenId(): String? + + /** Records [id] as the newest notification already surfaced. */ + fun setLastSeenId(id: String) + + /** Whether any marker has been recorded yet (false = first ever poll). */ + fun hasSeenAny(): Boolean +} + +/** SharedPreferences-backed [LastSeenNotificationStore]. */ +@Singleton +class SharedPrefsLastSeenNotificationStore @Inject constructor( + @ApplicationContext context: Context, +) : LastSeenNotificationStore { + + private val prefs: SharedPreferences = + context.getSharedPreferences(PREFS_FILE, Context.MODE_PRIVATE) + + override fun lastSeenId(): String? = prefs.getString(KEY_LAST_SEEN_ID, null) + + override fun setLastSeenId(id: String) { + prefs.edit().putString(KEY_LAST_SEEN_ID, id).apply() + } + + override fun hasSeenAny(): Boolean = prefs.contains(KEY_LAST_SEEN_ID) + + companion object { + private const val PREFS_FILE = "il_notifications_poll.prefs" + private const val KEY_LAST_SEEN_ID = "last_seen_notification_id" + } +} diff --git a/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NewNotificationsSelector.kt b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NewNotificationsSelector.kt new file mode 100644 index 0000000..aee2975 --- /dev/null +++ b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NewNotificationsSelector.kt @@ -0,0 +1,68 @@ +package com.interlinedlist.android.feature.notifications.push + +import com.interlinedlist.android.feature.notifications.domain.Notification + +/** + * Pure logic that decides which notifications are NEW relative to the last-seen + * marker, and what the new marker should be. No Android or IO dependencies, so it + * is fully unit-testable. + * + * Input is the API's newest-first page (index 0 is the most recent). Behaviour: + * - The newest id in the page becomes the [SelectionResult.newLastSeenId] to persist. + * - When a [lastSeenId] is present, everything strictly newer than it (i.e. every + * item preceding the matching id in the newest-first list) is NEW. + * - When [lastSeenId] is not found in the page (it fell off the end, or the list was + * cleared server-side), we conservatively treat the whole page as NEW rather than + * guessing — the cap in the poster keeps this from spamming. + * - On the very FIRST poll ([hasSeenAny] == false) we surface nothing and simply + * record the newest id, so logging in / installing never dumps the entire backlog + * into the tray. + */ +object NewNotificationsSelector { + + /** + * @param notifications the newest-first page from `GET /api/notifications`. + * @param lastSeenId the previously-recorded newest id (null on first run). + * @param hasSeenAny whether any marker has ever been recorded. + */ + fun select( + notifications: List, + lastSeenId: String?, + hasSeenAny: Boolean, + ): SelectionResult { + if (notifications.isEmpty()) { + // Nothing to surface; keep the existing marker untouched. + return SelectionResult(newNotifications = emptyList(), newLastSeenId = lastSeenId) + } + + val newestId = notifications.first().id + + // First ever poll: adopt the newest id as a baseline, surface nothing. + if (!hasSeenAny || lastSeenId == null) { + return SelectionResult(newNotifications = emptyList(), newLastSeenId = newestId) + } + + // Already up to date. + if (newestId == lastSeenId) { + return SelectionResult(newNotifications = emptyList(), newLastSeenId = newestId) + } + + val markerIndex = notifications.indexOfFirst { it.id == lastSeenId } + val newItems = if (markerIndex >= 0) { + notifications.subList(0, markerIndex).toList() + } else { + // Marker not in this page — treat the whole page as new (bounded by the cap). + notifications.toList() + } + + return SelectionResult(newNotifications = newItems, newLastSeenId = newestId) + } +} + +/** Outcome of [NewNotificationsSelector.select]. */ +data class SelectionResult( + /** Notifications that arrived since the last poll, newest-first. */ + val newNotifications: List, + /** The marker to persist as the new last-seen id (null only when there is nothing to record). */ + val newLastSeenId: String?, +) diff --git a/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationCategory.kt b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationCategory.kt new file mode 100644 index 0000000..d81ef51 --- /dev/null +++ b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationCategory.kt @@ -0,0 +1,73 @@ +package com.interlinedlist.android.feature.notifications.push + +import com.interlinedlist.android.feature.notifications.domain.NotificationType + +/** + * A system-notification channel category. Each maps a coarse [NotificationType] to a + * user-facing Android notification channel (registered on O+) and to the notification + * -preference event keys that gate its `push` delivery. + * + * The channel ids are stable strings persisted by the OS, so they must not change once + * shipped. [preferenceKeys] lists the candidate event keys the backend uses for this + * category (`GET /api/user/notification-preferences`); the first one present in the + * fetched preferences wins when deciding whether push is enabled. + */ +enum class NotificationCategory( + val channelId: String, + val channelName: String, + val channelDescription: String, + val preferenceKeys: List, +) { + MESSAGES( + channelId = "il_messages", + channelName = "Messages", + channelDescription = "New messages and direct messages", + preferenceKeys = listOf("message", "messages", "dm", "direct_message"), + ), + FOLLOWS( + channelId = "il_follows", + channelName = "Follows", + channelDescription = "New followers and follow requests", + preferenceKeys = listOf("follow", "follower", "new_follower", "follow_request"), + ), + DIGS( + channelId = "il_digs", + channelName = "Digs", + channelDescription = "When someone digs your content", + preferenceKeys = listOf("dig", "like", "favorite"), + ), + MENTIONS( + channelId = "il_mentions", + channelName = "Mentions", + channelDescription = "When someone mentions you", + preferenceKeys = listOf("mention", "tag"), + ), + REPLIES( + channelId = "il_replies", + channelName = "Replies", + channelDescription = "Replies and comments on your content", + preferenceKeys = listOf("reply", "comment"), + ), + OTHER( + channelId = "il_other", + channelName = "Other", + channelDescription = "Shared lists, system and account notifications", + preferenceKeys = listOf("list", "list_share", "system", "account"), + ); + + companion object { + + /** Maps a coarse [NotificationType] to the category whose channel it belongs to. */ + fun forType(type: NotificationType): NotificationCategory = when (type) { + NotificationType.MESSAGE -> MESSAGES + NotificationType.FOLLOW -> FOLLOWS + NotificationType.LIKE -> DIGS + NotificationType.MENTION -> MENTIONS + NotificationType.REPLY -> REPLIES + NotificationType.LIST_SHARE, + NotificationType.SYSTEM, + NotificationType.OTHER, + -> OTHER + } + } +} diff --git a/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationDeepLink.kt b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationDeepLink.kt new file mode 100644 index 0000000..bca5054 --- /dev/null +++ b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationDeepLink.kt @@ -0,0 +1,65 @@ +package com.interlinedlist.android.feature.notifications.push + +import com.interlinedlist.android.feature.notifications.domain.Notification +import com.interlinedlist.android.feature.notifications.domain.NotificationTargetKind + +/** + * The intent extras carried by a tapped system notification, and the pure logic that + * turns a [Notification]'s target into a destination the app can route to. + * + * The worker attaches these extras to the [android.app.PendingIntent] that opens + * [MainActivity]; the app reads them and navigates. Keeping the keys and the + * type→destination mapping here means the module owns the contract and it stays + * unit-testable without any Android UI. + */ +object NotificationDeepLink { + + /** Extra flagging that the launch originated from a notification tap. */ + const val EXTRA_FROM_NOTIFICATION = "il.extra.from_notification" + + /** Extra naming the destination the app should route to (see [Destination]). */ + const val EXTRA_DESTINATION = "il.extra.notification_destination" + + /** Extra carrying the target entity id, when the destination needs one. */ + const val EXTRA_TARGET_ID = "il.extra.notification_target_id" + + /** + * Where a tapped notification should land. The app maps these to concrete nav + * routes; anything without a resolvable target falls back to [NOTIFICATIONS]. + */ + enum class Destination { + /** Open a specific message thread ([EXTRA_TARGET_ID] = message id). */ + MESSAGE, + + /** Open a user's profile ([EXTRA_TARGET_ID] = username/id). */ + USER, + + /** Open a specific list ([EXTRA_TARGET_ID] = list id). */ + LIST, + + /** Open the in-app notifications feed (the safe fallback). */ + NOTIFICATIONS, + } + + /** + * Resolves the destination for [notification] from its target. Falls back to + * [Destination.NOTIFICATIONS] when there is no usable, specific target. + */ + fun destinationFor(notification: Notification): Destination { + val target = notification.target ?: return Destination.NOTIFICATIONS + if (target.id.isBlank()) return Destination.NOTIFICATIONS + return when (target.kind) { + NotificationTargetKind.MESSAGE -> Destination.MESSAGE + NotificationTargetKind.USER -> Destination.USER + NotificationTargetKind.LIST -> Destination.LIST + NotificationTargetKind.OTHER -> Destination.NOTIFICATIONS + } + } + + /** The target id to carry for [notification], or null when routing to the feed. */ + fun targetIdFor(notification: Notification): String? = + when (destinationFor(notification)) { + Destination.NOTIFICATIONS -> null + else -> notification.target?.id?.takeIf { it.isNotBlank() } + } +} diff --git a/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationPollProcessor.kt b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationPollProcessor.kt new file mode 100644 index 0000000..99ff4cb --- /dev/null +++ b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationPollProcessor.kt @@ -0,0 +1,50 @@ +package com.interlinedlist.android.feature.notifications.push + +import com.interlinedlist.android.feature.notifications.domain.Notification +import com.interlinedlist.android.feature.notifications.domain.NotificationPreference + +/** + * Pure orchestration of one poll pass, with no Android/IO dependencies so it is fully + * unit-testable. Given the freshly-fetched notifications, the current preferences, and + * the persisted last-seen state, it decides: + * - which notifications must be raised in the tray (NEW since last-seen AND push-enabled), and + * - the marker to persist next. + * + * The worker feeds it inputs it gathered over the network / store and applies its + * [Outcome] (post + persist). This keeps the "what to do" logic separate from the + * "how to do it" side-effects (SRP). + */ +object NotificationPollProcessor { + + /** + * @param fetched newest-first page from `GET /api/notifications`. + * @param preferences current per-event preferences (empty if the fetch failed). + * @param lastSeenId the previously-recorded newest id (null on first run). + * @param hasSeenAny whether any marker has ever been recorded. + */ + fun process( + fetched: List, + preferences: List, + lastSeenId: String?, + hasSeenAny: Boolean, + ): Outcome { + val selection = NewNotificationsSelector.select( + notifications = fetched, + lastSeenId = lastSeenId, + hasSeenAny = hasSeenAny, + ) + val toPost = NotificationPushFilter(preferences).filter(selection.newNotifications) + return Outcome( + toPost = toPost, + newLastSeenId = selection.newLastSeenId, + ) + } + + /** The actions the worker should apply after a poll pass. */ + data class Outcome( + /** Notifications to raise in the system tray (newest-first). */ + val toPost: List, + /** The marker to persist, or null when there is nothing to record. */ + val newLastSeenId: String?, + ) +} diff --git a/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationPollRunner.kt b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationPollRunner.kt new file mode 100644 index 0000000..654b2c1 --- /dev/null +++ b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationPollRunner.kt @@ -0,0 +1,61 @@ +package com.interlinedlist.android.feature.notifications.push + +import com.interlinedlist.android.core.common.result.ApiResult +import com.interlinedlist.android.feature.notifications.data.NotificationPreferencesRepository +import com.interlinedlist.android.feature.notifications.data.NotificationsRepository +import javax.inject.Inject + +/** + * Runs one background-poll pass: fetch the latest notifications, resolve the recipient's + * preferences, decide what is NEW and push-enabled (via the pure [NotificationPollProcessor]), + * raise the tray notifications, and advance the persisted last-seen marker. + * + * Holding this outside the `@HiltWorker` keeps it free of the Android `CoroutineWorker` + * superclass, so it is fully unit-testable; [NotificationsPollWorker] is a thin adapter + * that maps the [Result] onto WorkManager's outcome. + */ +class NotificationPollRunner @Inject constructor( + private val notificationsRepository: NotificationsRepository, + private val preferencesRepository: NotificationPreferencesRepository, + private val lastSeenStore: LastSeenNotificationStore, + private val raiser: SystemNotificationRaiser, +) { + + /** The outcome of a poll, mapped by the worker onto WorkManager's Result. */ + enum class Result { + /** Completed (whether or not anything was posted). */ + SUCCESS, + + /** Transient failure (fetch failed) — the worker should retry with backoff. */ + RETRY, + } + + suspend fun run(): Result { + val fetched = when (val result = notificationsRepository.fetchLatest()) { + is ApiResult.Success -> result.data + is ApiResult.Failure -> return Result.RETRY + } + + // Preferences are best-effort: on failure, fall back to no preferences, which + // the push filter interprets as default-notify. + val preferences = when (val prefs = preferencesRepository.getPreferences()) { + is ApiResult.Success -> prefs.data + is ApiResult.Failure -> emptyList() + } + + val outcome = NotificationPollProcessor.process( + fetched = fetched, + preferences = preferences, + lastSeenId = lastSeenStore.lastSeenId(), + hasSeenAny = lastSeenStore.hasSeenAny(), + ) + + if (outcome.toPost.isNotEmpty()) { + raiser.post(outcome.toPost) + } + // Advance the marker after posting, so a crash before posting doesn't skip items. + outcome.newLastSeenId?.let(lastSeenStore::setLastSeenId) + + return Result.SUCCESS + } +} diff --git a/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationPushFilter.kt b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationPushFilter.kt new file mode 100644 index 0000000..8dc0ae4 --- /dev/null +++ b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationPushFilter.kt @@ -0,0 +1,53 @@ +package com.interlinedlist.android.feature.notifications.push + +import com.interlinedlist.android.feature.notifications.domain.Notification +import com.interlinedlist.android.feature.notifications.domain.NotificationChannel +import com.interlinedlist.android.feature.notifications.domain.NotificationPreference + +/** + * Pure decision logic that gates which notifications may be raised as a system + * (push-tray) notification, based on the recipient's per-event `push` preference. + * No Android or IO dependencies, so it is fully unit-testable. + * + * Mapping is best-effort: a notification's [NotificationType][com.interlinedlist.android.feature.notifications.domain.NotificationType] + * is mapped to a [NotificationCategory], whose candidate [NotificationCategory.preferenceKeys] + * are matched (case-insensitively) against the fetched preference event keys. The first + * matching preference's [NotificationChannel.PUSH] state decides delivery. + * + * DEFAULT-NOTIFY: when no preference maps to the notification (unknown/unmodelled + * event, or preferences could not be fetched), we err on the side of notifying — a + * missed alert is worse than an unexpected one, and the recipient can still mute the + * channel from system settings. + */ +class NotificationPushFilter( + preferences: List, +) { + + /** Preferences indexed by their lower-cased key for O(1), case-insensitive lookup. */ + private val byKey: Map = + preferences.associateBy { it.key.trim().lowercase() } + + /** + * Whether [notification] should be raised in the system tray. True when the mapped + * `push` preference is enabled, or when nothing maps (default-notify). False only + * when a matching preference explicitly disables `push`. + */ + fun shouldNotify(notification: Notification): Boolean { + val preference = matchPreference(notification) ?: return true // default-notify + // If the event doesn't model a push channel at all, treat it as allowed. + if (NotificationChannel.PUSH !in preference.channels) return true + return preference.isEnabled(NotificationChannel.PUSH) + } + + /** Keeps only the notifications whose `push` channel is enabled (or unmapped). */ + fun filter(notifications: List): List = + notifications.filter(::shouldNotify) + + private fun matchPreference(notification: Notification): NotificationPreference? { + val category = NotificationCategory.forType(notification.type) + for (candidate in category.preferenceKeys) { + byKey[candidate.lowercase()]?.let { return it } + } + return null + } +} diff --git a/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationsPollWorker.kt b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationsPollWorker.kt new file mode 100644 index 0000000..afa0dea --- /dev/null +++ b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationsPollWorker.kt @@ -0,0 +1,35 @@ +package com.interlinedlist.android.feature.notifications.push + +import android.content.Context +import androidx.hilt.work.HiltWorker +import androidx.work.CoroutineWorker +import androidx.work.WorkerParameters +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject + +/** + * Background poll that stands in for FCM push (the product ships no Firebase project). + * A thin `@HiltWorker` adapter: it delegates the actual work to the injectable, + * unit-tested [NotificationPollRunner] and maps its outcome onto WorkManager's [Result]. + * + * The `HiltWorkerFactory` supplied by `InterlinedListApplication` (Configuration.Provider) + * constructs this with its dependencies — no extra bootstrap needed. + */ +@HiltWorker +class NotificationsPollWorker @AssistedInject constructor( + @Assisted appContext: Context, + @Assisted params: WorkerParameters, + private val runner: NotificationPollRunner, +) : CoroutineWorker(appContext, params) { + + override suspend fun doWork(): Result = when (runner.run()) { + NotificationPollRunner.Result.SUCCESS -> Result.success() + NotificationPollRunner.Result.RETRY -> Result.retry() + } + + companion object { + /** Unique names for the scheduled work (see [NotificationsSyncScheduler]). */ + const val PERIODIC_WORK_NAME = "notifications-poll-periodic" + const val ONE_SHOT_WORK_NAME = "notifications-poll-oneshot" + } +} diff --git a/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationsSyncScheduler.kt b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationsSyncScheduler.kt new file mode 100644 index 0000000..ec6c7ed --- /dev/null +++ b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationsSyncScheduler.kt @@ -0,0 +1,70 @@ +package com.interlinedlist.android.feature.notifications.push + +import android.content.Context +import androidx.work.BackoffPolicy +import androidx.work.Constraints +import androidx.work.ExistingPeriodicWorkPolicy +import androidx.work.ExistingWorkPolicy +import androidx.work.NetworkType +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.PeriodicWorkRequestBuilder +import androidx.work.WorkManager +import java.time.Duration + +/** + * Schedules the background notification poll. Keep this the single place that enqueues + * [NotificationsPollWorker] so the unique-work names and constraints stay consistent + * (mirrors `DocumentsSyncScheduler`). + * + * Wiring (call from the app after sign-in): + * ``` + * NotificationsSyncScheduler.schedulePeriodic(context) // near-real-time tray poll + * NotificationsSyncScheduler.syncNow(context) // e.g. right after login + * NotificationsSyncScheduler.cancelAll(context) // on sign-out + * ``` + */ +object NotificationsSyncScheduler { + + /** WorkManager's minimum periodic interval; also our chosen cadence. */ + val MIN_INTERVAL: Duration = Duration.ofMinutes(15) + + private val NETWORK_CONSTRAINTS = Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build() + + /** + * Periodic background poll. WorkManager's floor is 15 minutes, so any shorter + * [interval] is clamped up to [MIN_INTERVAL]. + */ + fun schedulePeriodic(context: Context, interval: Duration = MIN_INTERVAL) { + val effective = if (interval < MIN_INTERVAL) MIN_INTERVAL else interval + val request = PeriodicWorkRequestBuilder(effective) + .setConstraints(NETWORK_CONSTRAINTS) + .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, Duration.ofSeconds(30)) + .build() + WorkManager.getInstance(context).enqueueUniquePeriodicWork( + NotificationsPollWorker.PERIODIC_WORK_NAME, + ExistingPeriodicWorkPolicy.KEEP, + request, + ) + } + + /** One-shot poll now — e.g. immediately after login so the marker seeds fast. */ + fun syncNow(context: Context) { + val request = OneTimeWorkRequestBuilder() + .setConstraints(NETWORK_CONSTRAINTS) + .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, Duration.ofSeconds(15)) + .build() + WorkManager.getInstance(context).enqueueUniqueWork( + NotificationsPollWorker.ONE_SHOT_WORK_NAME, + ExistingWorkPolicy.REPLACE, + request, + ) + } + + /** Cancels all scheduled notification polling (e.g. on sign-out). */ + fun cancelAll(context: Context) { + WorkManager.getInstance(context).cancelUniqueWork(NotificationsPollWorker.PERIODIC_WORK_NAME) + WorkManager.getInstance(context).cancelUniqueWork(NotificationsPollWorker.ONE_SHOT_WORK_NAME) + } +} diff --git a/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/SystemNotificationChannels.kt b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/SystemNotificationChannels.kt new file mode 100644 index 0000000..902bf77 --- /dev/null +++ b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/SystemNotificationChannels.kt @@ -0,0 +1,36 @@ +package com.interlinedlist.android.feature.notifications.push + +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import android.os.Build +import androidx.core.content.getSystemService + +/** + * Registers the app's system notification channels (one per [NotificationCategory]) + * on Android O+. Idempotent — creating a channel that already exists is a no-op, and + * pre-O is skipped entirely (channels don't exist there). Safe to call at every app + * start. + * + * The decision of WHICH channels to register (their ids/names) is the pure + * [NotificationCategory] enum, which is unit-tested; this helper is the thin Android + * side-effect that installs them. + */ +object SystemNotificationChannels { + + /** Creates every category channel. No-op below Android O. */ + fun ensureRegistered(context: Context) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return + val manager = context.getSystemService() ?: return + NotificationCategory.entries.forEach { category -> + val channel = NotificationChannel( + category.channelId, + category.channelName, + NotificationManager.IMPORTANCE_DEFAULT, + ).apply { + description = category.channelDescription + } + manager.createNotificationChannel(channel) + } + } +} diff --git a/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/SystemNotificationPoster.kt b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/SystemNotificationPoster.kt new file mode 100644 index 0000000..b52c5ae --- /dev/null +++ b/feature/notifications/src/main/kotlin/com/interlinedlist/android/feature/notifications/push/SystemNotificationPoster.kt @@ -0,0 +1,153 @@ +package com.interlinedlist.android.feature.notifications.push + +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import com.interlinedlist.android.feature.notifications.R +import com.interlinedlist.android.feature.notifications.domain.Notification + +/** + * Raises system-tray notifications for a batch of new notifications. Abstracted behind + * an interface (DIP) so the worker can be unit-tested with a fake, and the real Android + * side-effects live only in [SystemNotificationPoster]. + */ +interface SystemNotificationRaiser { + /** Raises tray notifications for [items] (newest-first, already push-filtered). */ + fun post(items: List) +} + +/** + * Posts system-tray notifications for a batch of NEW notifications. The batch is + * capped so a large backlog can't spam the tray: at most [MAX_INDIVIDUAL] individual + * items are shown, and when there are more, only a single summary is posted. + * + * Each notification's tap opens the app's launcher activity (resolved via the package + * manager, so this module needs no compile-time reference to `MainActivity`) carrying + * the [NotificationDeepLink] extras the app reads to route. + * + * Grouping: individual items share a [GROUP_KEY] and a summary notification anchors the + * group so the shade collapses them tidily on Android N+. + */ +class SystemNotificationPoster( + private val context: Context, +) : SystemNotificationRaiser { + + /** + * Posts [items] (newest-first, already push-filtered). No-op when the list is empty + * or the user has notifications disabled at the OS level. + */ + override fun post(items: List) { + if (items.isEmpty()) return + val manager = NotificationManagerCompat.from(context) + if (!manager.areNotificationsEnabled()) return + + if (items.size > MAX_INDIVIDUAL) { + postSummaryOnly(manager, items) + return + } + + items.forEach { notification -> + manager.safeNotify(notification.id.hashCode(), buildIndividual(notification)) + } + if (items.size > 1) { + manager.safeNotify(SUMMARY_ID, buildGroupSummary(items)) + } + } + + private fun postSummaryOnly(manager: NotificationManagerCompat, items: List) { + val category = NotificationCategory.OTHER + val summary = NotificationCompat.Builder(context, category.channelId) + .setSmallIcon(R.drawable.il_notification_icon) + .setContentTitle("$COUNT_TITLE_PREFIX ${items.size} new notifications") + .setContentText(items.first().subject) + .setAutoCancel(true) + .setContentIntent(feedPendingIntent()) + .build() + manager.safeNotify(SUMMARY_ID, summary) + } + + private fun buildIndividual(notification: Notification): android.app.Notification { + val category = NotificationCategory.forType(notification.type) + val title = notification.actorLabel?.let { "$it" } ?: category.channelName + return NotificationCompat.Builder(context, category.channelId) + .setSmallIcon(R.drawable.il_notification_icon) + .setContentTitle(notification.subject.ifBlank { title }) + .apply { notification.body?.let { setContentText(it) } } + .setAutoCancel(true) + .setGroup(GROUP_KEY) + .setContentIntent(pendingIntentFor(notification)) + .build() + } + + private fun buildGroupSummary(items: List): android.app.Notification = + NotificationCompat.Builder(context, NotificationCategory.OTHER.channelId) + .setSmallIcon(R.drawable.il_notification_icon) + .setContentTitle("$COUNT_TITLE_PREFIX ${items.size} new notifications") + .setContentText(items.first().subject) + .setGroup(GROUP_KEY) + .setGroupSummary(true) + .setAutoCancel(true) + .setContentIntent(feedPendingIntent()) + .build() + + // --- intents ----------------------------------------------------------- + + /** Launch intent → the app, tagged so the app routes to the specific target. */ + private fun pendingIntentFor(notification: Notification): PendingIntent { + val destination = NotificationDeepLink.destinationFor(notification) + val intent = launchIntent().apply { + putExtra(NotificationDeepLink.EXTRA_FROM_NOTIFICATION, true) + putExtra(NotificationDeepLink.EXTRA_DESTINATION, destination.name) + NotificationDeepLink.targetIdFor(notification)?.let { + putExtra(NotificationDeepLink.EXTRA_TARGET_ID, it) + } + } + return activityPendingIntent(notification.id.hashCode(), intent) + } + + /** Launch intent → the app, routed to the in-app notifications feed (fallback). */ + private fun feedPendingIntent(): PendingIntent { + val intent = launchIntent().apply { + putExtra(NotificationDeepLink.EXTRA_FROM_NOTIFICATION, true) + putExtra( + NotificationDeepLink.EXTRA_DESTINATION, + NotificationDeepLink.Destination.NOTIFICATIONS.name, + ) + } + return activityPendingIntent(SUMMARY_ID, intent) + } + + private fun launchIntent(): Intent = + context.packageManager.getLaunchIntentForPackage(context.packageName) + ?.apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP } + ?: Intent(Intent.ACTION_MAIN).apply { setPackage(context.packageName) } + + private fun activityPendingIntent(requestCode: Int, intent: Intent): PendingIntent = + PendingIntent.getActivity( + context, + requestCode, + intent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE, + ) + + private fun NotificationManagerCompat.safeNotify(id: Int, notification: android.app.Notification) { + // areNotificationsEnabled() is checked up front; guard the individual post too + // so a revoked POST_NOTIFICATIONS permission never throws on the worker thread. + runCatching { notify(id, notification) } + } + + companion object { + /** Max individual notifications before collapsing to a single summary. */ + const val MAX_INDIVIDUAL = 5 + + /** Shared group key so the shade collapses our notifications together. */ + const val GROUP_KEY = "il.notifications.group" + + /** Stable id for the group-summary / overflow notification. */ + const val SUMMARY_ID = 424242 + + private const val COUNT_TITLE_PREFIX = "InterlinedList:" + } +} diff --git a/feature/notifications/src/main/res/drawable/il_notification_icon.xml b/feature/notifications/src/main/res/drawable/il_notification_icon.xml new file mode 100644 index 0000000..611d9fa --- /dev/null +++ b/feature/notifications/src/main/res/drawable/il_notification_icon.xml @@ -0,0 +1,15 @@ + + + + diff --git a/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/data/remote/dto/NotificationsResponseTest.kt b/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/data/remote/dto/NotificationsResponseTest.kt index aa2bd6d..bb8713f 100644 --- a/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/data/remote/dto/NotificationsResponseTest.kt +++ b/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/data/remote/dto/NotificationsResponseTest.kt @@ -28,6 +28,19 @@ class NotificationsResponseTest { assertThat(response.items.map { it.id }).containsExactly("a") } + @Test + fun `reads the list from the live items key`() { + // The production API returns `{ "unreadCount": N, "items": [...] }`. Regression + // guard: this key was previously unmapped, silently emptying the notifications + // feed and the background push poll. + val response = json.decodeFromString( + NotificationsResponse.serializer(), + """{ "unreadCount": 3, "items": [ { "id": "x" }, { "id": "y" } ] }""", + ) + assertThat(response.items.map { it.id }).containsExactly("x", "y").inOrder() + assertThat(response.unreadCount).isEqualTo(3) + } + @Test fun `an empty body decodes with sane defaults`() { val response = json.decodeFromString(NotificationsResponse.serializer(), "{}") diff --git a/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/FakeLastSeenNotificationStore.kt b/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/FakeLastSeenNotificationStore.kt new file mode 100644 index 0000000..c919193 --- /dev/null +++ b/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/FakeLastSeenNotificationStore.kt @@ -0,0 +1,19 @@ +package com.interlinedlist.android.feature.notifications.push + +/** In-memory [LastSeenNotificationStore] for unit tests (no Android dependency). */ +class FakeLastSeenNotificationStore( + initial: String? = null, +) : LastSeenNotificationStore { + + private var lastSeen: String? = initial + private var seenAny: Boolean = initial != null + + override fun lastSeenId(): String? = lastSeen + + override fun setLastSeenId(id: String) { + lastSeen = id + seenAny = true + } + + override fun hasSeenAny(): Boolean = seenAny +} diff --git a/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/NewNotificationsSelectorTest.kt b/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/NewNotificationsSelectorTest.kt new file mode 100644 index 0000000..14cfa11 --- /dev/null +++ b/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/NewNotificationsSelectorTest.kt @@ -0,0 +1,88 @@ +package com.interlinedlist.android.feature.notifications.push + +import com.google.common.truth.Truth.assertThat +import com.interlinedlist.android.feature.notifications.domain.Notification +import com.interlinedlist.android.feature.notifications.domain.NotificationType +import org.junit.Test + +class NewNotificationsSelectorTest { + + private fun notif(id: String) = Notification( + id = id, + type = NotificationType.FOLLOW, + actor = null, + subject = "s-$id", + body = null, + createdAt = null, + read = false, + target = null, + ) + + @Test + fun `first ever poll surfaces nothing and adopts the newest id as baseline`() { + val page = listOf(notif("3"), notif("2"), notif("1")) + + val result = NewNotificationsSelector.select( + notifications = page, + lastSeenId = null, + hasSeenAny = false, + ) + + assertThat(result.newNotifications).isEmpty() + assertThat(result.newLastSeenId).isEqualTo("3") + } + + @Test + fun `only items newer than the marker are new and marker advances`() { + val page = listOf(notif("5"), notif("4"), notif("3"), notif("2")) + + val result = NewNotificationsSelector.select( + notifications = page, + lastSeenId = "3", + hasSeenAny = true, + ) + + assertThat(result.newNotifications.map { it.id }).containsExactly("5", "4").inOrder() + assertThat(result.newLastSeenId).isEqualTo("5") + } + + @Test + fun `no new items when the newest already equals the marker`() { + val page = listOf(notif("9"), notif("8")) + + val result = NewNotificationsSelector.select( + notifications = page, + lastSeenId = "9", + hasSeenAny = true, + ) + + assertThat(result.newNotifications).isEmpty() + assertThat(result.newLastSeenId).isEqualTo("9") + } + + @Test + fun `marker missing from the page treats the whole page as new`() { + val page = listOf(notif("7"), notif("6")) + + val result = NewNotificationsSelector.select( + notifications = page, + lastSeenId = "1", // fell off the page + hasSeenAny = true, + ) + + assertThat(result.newNotifications.map { it.id }).containsExactly("7", "6").inOrder() + assertThat(result.newLastSeenId).isEqualTo("7") + } + + @Test + fun `empty page is a no-op and keeps the existing marker`() { + val result = NewNotificationsSelector.select( + notifications = emptyList(), + lastSeenId = "4", + hasSeenAny = true, + ) + + assertThat(result.newNotifications).isEmpty() + assertThat(result.newLastSeenId).isEqualTo("4") + } +} diff --git a/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationCategoryTest.kt b/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationCategoryTest.kt new file mode 100644 index 0000000..205f7db --- /dev/null +++ b/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationCategoryTest.kt @@ -0,0 +1,48 @@ +package com.interlinedlist.android.feature.notifications.push + +import com.google.common.truth.Truth.assertThat +import com.interlinedlist.android.feature.notifications.domain.NotificationType +import org.junit.Test + +class NotificationCategoryTest { + + @Test + fun `every notification type maps to a category`() { + // Exhaustive: no type should be unmapped. + NotificationType.entries.forEach { type -> + val category = NotificationCategory.forType(type) + assertThat(category).isNotNull() + } + } + + @Test + fun `core types map to their expected categories`() { + assertThat(NotificationCategory.forType(NotificationType.MESSAGE)) + .isEqualTo(NotificationCategory.MESSAGES) + assertThat(NotificationCategory.forType(NotificationType.FOLLOW)) + .isEqualTo(NotificationCategory.FOLLOWS) + assertThat(NotificationCategory.forType(NotificationType.LIKE)) + .isEqualTo(NotificationCategory.DIGS) + assertThat(NotificationCategory.forType(NotificationType.MENTION)) + .isEqualTo(NotificationCategory.MENTIONS) + assertThat(NotificationCategory.forType(NotificationType.REPLY)) + .isEqualTo(NotificationCategory.REPLIES) + } + + @Test + fun `list share, system and unknown fall back to OTHER`() { + assertThat(NotificationCategory.forType(NotificationType.LIST_SHARE)) + .isEqualTo(NotificationCategory.OTHER) + assertThat(NotificationCategory.forType(NotificationType.SYSTEM)) + .isEqualTo(NotificationCategory.OTHER) + assertThat(NotificationCategory.forType(NotificationType.OTHER)) + .isEqualTo(NotificationCategory.OTHER) + } + + @Test + fun `channel ids are unique and non-blank`() { + val ids = NotificationCategory.entries.map { it.channelId } + assertThat(ids).containsNoDuplicates() + assertThat(ids.none { it.isBlank() }).isTrue() + } +} diff --git a/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationDeepLinkTest.kt b/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationDeepLinkTest.kt new file mode 100644 index 0000000..f14a1b5 --- /dev/null +++ b/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationDeepLinkTest.kt @@ -0,0 +1,75 @@ +package com.interlinedlist.android.feature.notifications.push + +import com.google.common.truth.Truth.assertThat +import com.interlinedlist.android.feature.notifications.domain.Notification +import com.interlinedlist.android.feature.notifications.domain.NotificationTarget +import com.interlinedlist.android.feature.notifications.domain.NotificationTargetKind +import com.interlinedlist.android.feature.notifications.domain.NotificationType +import org.junit.Test + +class NotificationDeepLinkTest { + + private fun notif(target: NotificationTarget?) = Notification( + id = "1", + type = NotificationType.OTHER, + actor = null, + subject = "s", + body = null, + createdAt = null, + read = false, + target = target, + ) + + @Test + fun `message target routes to MESSAGE with its id`() { + val n = notif(NotificationTarget(NotificationTargetKind.MESSAGE, "m1")) + + assertThat(NotificationDeepLink.destinationFor(n)) + .isEqualTo(NotificationDeepLink.Destination.MESSAGE) + assertThat(NotificationDeepLink.targetIdFor(n)).isEqualTo("m1") + } + + @Test + fun `user target routes to USER`() { + val n = notif(NotificationTarget(NotificationTargetKind.USER, "amy")) + + assertThat(NotificationDeepLink.destinationFor(n)) + .isEqualTo(NotificationDeepLink.Destination.USER) + assertThat(NotificationDeepLink.targetIdFor(n)).isEqualTo("amy") + } + + @Test + fun `list target routes to LIST`() { + val n = notif(NotificationTarget(NotificationTargetKind.LIST, "l9")) + + assertThat(NotificationDeepLink.destinationFor(n)) + .isEqualTo(NotificationDeepLink.Destination.LIST) + assertThat(NotificationDeepLink.targetIdFor(n)).isEqualTo("l9") + } + + @Test + fun `null target falls back to the notifications feed`() { + val n = notif(null) + + assertThat(NotificationDeepLink.destinationFor(n)) + .isEqualTo(NotificationDeepLink.Destination.NOTIFICATIONS) + assertThat(NotificationDeepLink.targetIdFor(n)).isNull() + } + + @Test + fun `OTHER target kind falls back to the notifications feed`() { + val n = notif(NotificationTarget(NotificationTargetKind.OTHER, "x")) + + assertThat(NotificationDeepLink.destinationFor(n)) + .isEqualTo(NotificationDeepLink.Destination.NOTIFICATIONS) + assertThat(NotificationDeepLink.targetIdFor(n)).isNull() + } + + @Test + fun `blank target id falls back to the notifications feed`() { + val n = notif(NotificationTarget(NotificationTargetKind.MESSAGE, " ")) + + assertThat(NotificationDeepLink.destinationFor(n)) + .isEqualTo(NotificationDeepLink.Destination.NOTIFICATIONS) + } +} diff --git a/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationPollProcessorTest.kt b/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationPollProcessorTest.kt new file mode 100644 index 0000000..b15213f --- /dev/null +++ b/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationPollProcessorTest.kt @@ -0,0 +1,102 @@ +package com.interlinedlist.android.feature.notifications.push + +import com.google.common.truth.Truth.assertThat +import com.interlinedlist.android.feature.notifications.domain.Notification +import com.interlinedlist.android.feature.notifications.domain.NotificationChannel +import com.interlinedlist.android.feature.notifications.domain.NotificationPreference +import com.interlinedlist.android.feature.notifications.domain.NotificationType +import org.junit.Test + +class NotificationPollProcessorTest { + + private fun notif(id: String, type: NotificationType) = Notification( + id = id, + type = type, + actor = null, + subject = "s-$id", + body = null, + createdAt = null, + read = false, + target = null, + ) + + private fun pref(key: String, push: Boolean) = NotificationPreference( + key = key, + label = key, + description = "", + channels = mapOf(NotificationChannel.PUSH to push), + ) + + @Test + fun `first poll posts nothing and seeds the marker`() { + val outcome = NotificationPollProcessor.process( + fetched = listOf(notif("3", NotificationType.FOLLOW), notif("2", NotificationType.FOLLOW)), + preferences = emptyList(), + lastSeenId = null, + hasSeenAny = false, + ) + + assertThat(outcome.toPost).isEmpty() + assertThat(outcome.newLastSeenId).isEqualTo("3") + } + + @Test + fun `only new items are posted and the marker advances`() { + val outcome = NotificationPollProcessor.process( + fetched = listOf( + notif("5", NotificationType.FOLLOW), + notif("4", NotificationType.MENTION), + notif("3", NotificationType.FOLLOW), + ), + preferences = emptyList(), + lastSeenId = "3", + hasSeenAny = true, + ) + + assertThat(outcome.toPost.map { it.id }).containsExactly("5", "4").inOrder() + assertThat(outcome.newLastSeenId).isEqualTo("5") + } + + @Test + fun `push-disabled events are filtered out of the post list`() { + val outcome = NotificationPollProcessor.process( + fetched = listOf( + notif("5", NotificationType.FOLLOW), // follow push disabled -> dropped + notif("4", NotificationType.MENTION), // no pref -> default notify + ), + preferences = listOf(pref("follow", push = false)), + lastSeenId = "3", + hasSeenAny = true, + ) + + assertThat(outcome.toPost.map { it.id }).containsExactly("4") + // Marker still advances to the true newest, even though "5" wasn't posted. + assertThat(outcome.newLastSeenId).isEqualTo("5") + } + + @Test + fun `no new items is a no-op post but keeps the marker current`() { + val outcome = NotificationPollProcessor.process( + fetched = listOf(notif("9", NotificationType.FOLLOW)), + preferences = emptyList(), + lastSeenId = "9", + hasSeenAny = true, + ) + + assertThat(outcome.toPost).isEmpty() + assertThat(outcome.newLastSeenId).isEqualTo("9") + } + + @Test + fun `empty fetch posts nothing`() { + val outcome = NotificationPollProcessor.process( + fetched = emptyList(), + preferences = emptyList(), + lastSeenId = "4", + hasSeenAny = true, + ) + + assertThat(outcome.toPost).isEmpty() + assertThat(outcome.newLastSeenId).isEqualTo("4") + } +} diff --git a/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationPollRunnerTest.kt b/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationPollRunnerTest.kt new file mode 100644 index 0000000..f43c65c --- /dev/null +++ b/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationPollRunnerTest.kt @@ -0,0 +1,205 @@ +package com.interlinedlist.android.feature.notifications.push + +import com.google.common.truth.Truth.assertThat +import com.interlinedlist.android.core.common.result.ApiResult +import com.interlinedlist.android.feature.notifications.data.DefaultNotificationsRepository +import com.interlinedlist.android.feature.notifications.data.FakeNotificationDao +import com.interlinedlist.android.feature.notifications.data.NotificationPreferencesRepository +import com.interlinedlist.android.feature.notifications.data.TestDispatcherProvider +import com.interlinedlist.android.feature.notifications.data.remote.NotificationsApi +import com.interlinedlist.android.feature.notifications.domain.NotificationChannel +import com.interlinedlist.android.feature.notifications.domain.NotificationPreference +import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.runTest +import kotlinx.serialization.json.Json +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.mockwebserver.MockResponse +import okhttp3.mockwebserver.MockWebServer +import org.junit.After +import org.junit.Before +import org.junit.Test +import retrofit2.Retrofit + +@OptIn(ExperimentalCoroutinesApi::class) +class NotificationPollRunnerTest { + + private val dispatcher = StandardTestDispatcher() + private val json = Json { ignoreUnknownKeys = true; explicitNulls = false } + + private lateinit var server: MockWebServer + private lateinit var api: NotificationsApi + private lateinit var dao: FakeNotificationDao + + @Before + fun setUp() { + server = MockWebServer() + server.start() + val contentType = "application/json".toMediaType() + api = Retrofit.Builder() + .baseUrl(server.url("/")) + .addConverterFactory(json.asConverterFactory(contentType)) + .build() + .create(NotificationsApi::class.java) + dao = FakeNotificationDao() + } + + @After + fun tearDown() = server.shutdown() + + private fun notificationsRepo() = DefaultNotificationsRepository( + api = api, + notificationDao = dao, + json = json, + dispatchers = TestDispatcherProvider(dispatcher), + ) + + /** Preferences repo whose GET result is fixed. */ + private fun prefsRepo( + result: ApiResult> = ApiResult.Success(emptyList()), + ): NotificationPreferencesRepository = object : NotificationPreferencesRepository { + override suspend fun getPreferences() = result + override suspend fun updatePreference(preference: NotificationPreference) = + ApiResult.Success(Unit) + } + + private fun runner( + store: LastSeenNotificationStore, + raiser: SystemNotificationRaiser, + prefs: NotificationPreferencesRepository = prefsRepo(), + ) = NotificationPollRunner( + notificationsRepository = notificationsRepo(), + preferencesRepository = prefs, + lastSeenStore = store, + raiser = raiser, + ) + + private fun enqueue(body: String, code: Int = 200) { + server.enqueue(MockResponse().setResponseCode(code).setBody(body)) + } + + @Test + fun `first poll posts nothing and seeds the marker`() = runTest(dispatcher) { + enqueue( + """{ "data": [ { "id": "3", "type": "follow", "subject": "c" }, + { "id": "2", "type": "follow", "subject": "b" } ], + "pagination": { "hasMore": false } }""", + ) + val store = FakeLastSeenNotificationStore(initial = null) + val raiser = RecordingSystemNotificationRaiser() + + val result = runner(store, raiser).run() + + assertThat(result).isEqualTo(NotificationPollRunner.Result.SUCCESS) + assertThat(raiser.batches).isEmpty() + assertThat(store.lastSeenId()).isEqualTo("3") + } + + @Test + fun `only new items since last-seen are posted and the marker advances`() = runTest(dispatcher) { + enqueue( + """{ "data": [ { "id": "5", "type": "mention", "subject": "e" }, + { "id": "4", "type": "follow", "subject": "d" }, + { "id": "3", "type": "follow", "subject": "c" } ], + "pagination": { "hasMore": false } }""", + ) + val store = FakeLastSeenNotificationStore(initial = "3") + val raiser = RecordingSystemNotificationRaiser() + + runner(store, raiser).run() + + assertThat(raiser.postedIds).containsExactly("5", "4").inOrder() + assertThat(store.lastSeenId()).isEqualTo("5") + } + + @Test + fun `push-disabled events are filtered out of the tray`() = runTest(dispatcher) { + enqueue( + """{ "data": [ { "id": "5", "type": "follow", "subject": "e" }, + { "id": "4", "type": "mention", "subject": "d" } ], + "pagination": { "hasMore": false } }""", + ) + val store = FakeLastSeenNotificationStore(initial = "3") + val raiser = RecordingSystemNotificationRaiser() + val prefs = prefsRepo( + ApiResult.Success( + listOf( + NotificationPreference( + key = "follow", + label = "follow", + description = "", + channels = mapOf(NotificationChannel.PUSH to false), + ), + ), + ), + ) + + runner(store, raiser, prefs).run() + + // "5" (follow) is dropped; "4" (mention, unmapped) defaults to notify. + assertThat(raiser.postedIds).containsExactly("4") + // Marker still advances to the true newest. + assertThat(store.lastSeenId()).isEqualTo("5") + } + + @Test + fun `no new items is a no-op post`() = runTest(dispatcher) { + enqueue( + """{ "data": [ { "id": "9", "type": "follow", "subject": "i" } ], + "pagination": { "hasMore": false } }""", + ) + val store = FakeLastSeenNotificationStore(initial = "9") + val raiser = RecordingSystemNotificationRaiser() + + runner(store, raiser).run() + + assertThat(raiser.batches).isEmpty() + assertThat(store.lastSeenId()).isEqualTo("9") + } + + @Test + fun `empty page posts nothing and keeps the marker`() = runTest(dispatcher) { + enqueue("""{ "data": [], "pagination": { "hasMore": false } }""") + val store = FakeLastSeenNotificationStore(initial = "4") + val raiser = RecordingSystemNotificationRaiser() + + runner(store, raiser).run() + + assertThat(raiser.batches).isEmpty() + assertThat(store.lastSeenId()).isEqualTo("4") + } + + @Test + fun `fetch failure asks WorkManager to retry`() = runTest(dispatcher) { + enqueue("""{ "error": "boom" }""", code = 500) + val store = FakeLastSeenNotificationStore(initial = "4") + val raiser = RecordingSystemNotificationRaiser() + + val result = runner(store, raiser).run() + + assertThat(result).isEqualTo(NotificationPollRunner.Result.RETRY) + assertThat(raiser.batches).isEmpty() + // Marker untouched on failure. + assertThat(store.lastSeenId()).isEqualTo("4") + } + + @Test + fun `preferences failure falls back to default-notify`() = runTest(dispatcher) { + enqueue( + """{ "data": [ { "id": "5", "type": "follow", "subject": "e" } ], + "pagination": { "hasMore": false } }""", + ) + val store = FakeLastSeenNotificationStore(initial = "3") + val raiser = RecordingSystemNotificationRaiser() + val prefs = prefsRepo( + ApiResult.Failure(com.interlinedlist.android.core.common.result.AppError.Unknown("x")), + ) + + runner(store, raiser, prefs).run() + + // Preferences unavailable -> default notify -> "5" is posted. + assertThat(raiser.postedIds).containsExactly("5") + assertThat(store.lastSeenId()).isEqualTo("5") + } +} diff --git a/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationPushFilterTest.kt b/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationPushFilterTest.kt new file mode 100644 index 0000000..33e293e --- /dev/null +++ b/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/NotificationPushFilterTest.kt @@ -0,0 +1,105 @@ +package com.interlinedlist.android.feature.notifications.push + +import com.google.common.truth.Truth.assertThat +import com.interlinedlist.android.feature.notifications.domain.Notification +import com.interlinedlist.android.feature.notifications.domain.NotificationChannel +import com.interlinedlist.android.feature.notifications.domain.NotificationPreference +import com.interlinedlist.android.feature.notifications.domain.NotificationType +import org.junit.Test + +class NotificationPushFilterTest { + + private fun notif(id: String, type: NotificationType) = Notification( + id = id, + type = type, + actor = null, + subject = "s", + body = null, + createdAt = null, + read = false, + target = null, + ) + + private fun pref(key: String, push: Boolean) = NotificationPreference( + key = key, + label = key, + description = "", + channels = mapOf( + NotificationChannel.PUSH to push, + NotificationChannel.IN_APP to true, + ), + ) + + @Test + fun `push-disabled event is filtered out`() { + val filter = NotificationPushFilter(listOf(pref("follow", push = false))) + + assertThat(filter.shouldNotify(notif("1", NotificationType.FOLLOW))).isFalse() + } + + @Test + fun `push-enabled event is kept`() { + val filter = NotificationPushFilter(listOf(pref("follow", push = true))) + + assertThat(filter.shouldNotify(notif("1", NotificationType.FOLLOW))).isTrue() + } + + @Test + fun `key matching is case-insensitive`() { + val filter = NotificationPushFilter(listOf(pref("FoLLoW", push = false))) + + assertThat(filter.shouldNotify(notif("1", NotificationType.FOLLOW))).isFalse() + } + + @Test + fun `unmapped event defaults to notify`() { + // No preference for the DIG/like category at all. + val filter = NotificationPushFilter(listOf(pref("follow", push = false))) + + assertThat(filter.shouldNotify(notif("1", NotificationType.LIKE))).isTrue() + } + + @Test + fun `empty preferences default to notify`() { + val filter = NotificationPushFilter(emptyList()) + + assertThat(filter.shouldNotify(notif("1", NotificationType.MENTION))).isTrue() + } + + @Test + fun `event without a push channel is treated as allowed`() { + val emailOnly = NotificationPreference( + key = "follow", + label = "follow", + description = "", + channels = mapOf(NotificationChannel.EMAIL to true), // no PUSH modelled + ) + val filter = NotificationPushFilter(listOf(emailOnly)) + + assertThat(filter.shouldNotify(notif("1", NotificationType.FOLLOW))).isTrue() + } + + @Test + fun `filter keeps only push-enabled notifications`() { + val filter = NotificationPushFilter( + listOf( + pref("follow", push = true), + pref("dig", push = false), + ), + ) + val input = listOf( + notif("1", NotificationType.FOLLOW), // kept + notif("2", NotificationType.LIKE), // dropped (dig disabled) + notif("3", NotificationType.MENTION), // kept (unmapped -> default notify) + ) + + assertThat(filter.filter(input).map { it.id }).containsExactly("1", "3").inOrder() + } + + @Test + fun `dig preference maps to the like notification type`() { + val filter = NotificationPushFilter(listOf(pref("dig", push = false))) + + assertThat(filter.shouldNotify(notif("1", NotificationType.LIKE))).isFalse() + } +} diff --git a/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/RecordingSystemNotificationRaiser.kt b/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/RecordingSystemNotificationRaiser.kt new file mode 100644 index 0000000..af9166e --- /dev/null +++ b/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/push/RecordingSystemNotificationRaiser.kt @@ -0,0 +1,17 @@ +package com.interlinedlist.android.feature.notifications.push + +import com.interlinedlist.android.feature.notifications.domain.Notification + +/** Records every [post] call so tests can assert which notifications were raised. */ +class RecordingSystemNotificationRaiser : SystemNotificationRaiser { + + /** Each element is the batch handed to one [post] call. */ + val batches = mutableListOf>() + + /** Flattened ids across all batches, in order. */ + val postedIds: List get() = batches.flatten().map { it.id } + + override fun post(items: List) { + batches += items + } +} diff --git a/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/ui/FakeNotificationsRepository.kt b/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/ui/FakeNotificationsRepository.kt index a585ccf..49d3fb9 100644 --- a/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/ui/FakeNotificationsRepository.kt +++ b/feature/notifications/src/test/kotlin/com/interlinedlist/android/feature/notifications/ui/FakeNotificationsRepository.kt @@ -18,12 +18,14 @@ class FakeNotificationsRepository : NotificationsRepository { private val notifications = MutableStateFlow>(emptyList()) + var fetchLatestResult: ApiResult> = ApiResult.Success(emptyList()) var refreshResult: ApiResult = ApiResult.Success(false) var loadMoreResult: ApiResult = ApiResult.Success(false) var markReadResult: ApiResult = ApiResult.Success(Unit) var markAllReadResult: ApiResult = ApiResult.Success(Unit) var dismissResult: ApiResult = ApiResult.Success(Unit) + var fetchLatestCount = 0 var refreshCount = 0 var loadMoreCount = 0 var markReadIds = mutableListOf() @@ -37,6 +39,11 @@ class FakeNotificationsRepository : NotificationsRepository { override fun observeUnreadCount(): Flow = notifications.map { list -> list.count { !it.read } } + override suspend fun fetchLatest(): ApiResult> { + fetchLatestCount++ + return fetchLatestResult + } + override suspend fun refresh(): ApiResult { refreshCount++ return refreshResult