Commit graph

178 commits

Author SHA1 Message Date
KhooLy
b330eb475c Add Nuvio remote collection sources 2026-07-14 22:24:25 +03:00
KhooLy
2e0f1baee6 Refresh Nuvio data on home entry 2026-07-14 22:11:56 +03:00
KhooLy
8a75354e7b Sync Nuvio collections live 2026-07-14 22:09:18 +03:00
KhooLy
5fb04a5a6f Improve Nuvio import parity 2026-07-14 22:07:43 +03:00
KhooLy
f3783c6fbf Open mobile addons directly 2026-07-14 22:07:42 +03:00
KhooLy
82855c5039 Keep mobile home controls accessible 2026-07-14 21:59:28 +03:00
KhooLy
7d431cb6cb Avoid capturing app state in callbacks 2026-07-14 21:08:15 +03:00
KhooLy
278f0f1f9b Avoid capturing app value in callback 2026-07-14 20:42:54 +03:00
KhooLy
b5510e3b84 Import shared iOS bridge 2026-07-14 20:15:30 +03:00
KhooLy
03edaa138d Fix iOS detail bridge compilation 2026-07-14 19:48:12 +03:00
KhooLy
be954edd4b Fix Apple host compilation 2026-07-14 19:13:11 +03:00
KhooLy
e747ca3d0e Fix Apple streaming engine CI packaging 2026-07-14 18:41:00 +03:00
KhooLy
67a20b284e Fix Apple CI configuration 2026-07-14 18:23:43 +03:00
KhooLy
20476761a9 Migrate shared UI and Apple playback to KMP 2026-07-14 18:14:18 +03:00
KhooLy
092a92c1c8 Remove unused shared Koin startup 2026-07-14 16:58:54 +03:00
KhooLy
d61a97000a Improve player visuals and trailer loading 2026-07-14 16:58:39 +03:00
KhooLy
1bc683acbf Serialize concurrent Stremio addon requests 2026-07-14 16:58:21 +03:00
KhooLy
b0a68d6cee Preserve profile names during Nuvio import 2026-07-14 16:57:50 +03:00
KhooLy
cda0c17b0a Replace TorrServer namings with Native torrent engine 2026-07-14 16:57:20 +03:00
KhooLy
a9f6e65b36 Migrate shared Apple UI bridges to KMP 2026-07-14 16:55:16 +03:00
KhooLy
08da9029ae Show 3 lines of hero banner synopsis instead of 2 2026-07-14 15:30:36 +03:00
KhooLy
f47ddfa01c Fetch home bootstrap catalog rows concurrently
readHomeBootstrap awaited each addon catalog request sequentially,
causing 7-8s cold-start latency with multiple addons/genres. Applies
the same async/Semaphore(8)/awaitAll fan-out already used by
runDiscover for genre switches.
2026-07-14 15:25:35 +03:00
KhooLy
13da42567f Fix hero dots/synopsis spacing, catalog row arrow, bottom nav polish
Hero: pagination dots and each slide's title/badges/description were
both independently bottom-aligned in the same Box, so the dots sat
right on top of (or overlapping) the last line of the synopsis
instead of below it with a gap. Increased the slide content's bottom
inset from 20dp to 44dp so it stops short of the dots row.

Catalog rows: "View All" text replaced with a right-arrow icon,
matching the reference's icon-only affordance.

MobileBottomNav: dropped the top hairline divider and switched to a
flat pure-black background (was a near-black AMOLED-conditional
color), bumped icon size 26->28dp and vertical padding 10->16dp for
more breathing room - closer to the reference's cleaner, more open
spacing.

Compile-verified on shared commonMain, iosMain, and both Android
flavors.
2026-07-14 15:16:03 +03:00
KhooLy
9fb1fad5cb Simplify Home hero overlay bar and native bottom nav
Home hero: dropped the Play button (whole hero is already clickable),
made the age-rating badge plain text instead of a filled accent pill
so all metadata reads consistently, and merged the tabs row + bell/
avatar row into one - Home/Series/Movies on the left, notifications +
avatar on the right, both overlaid on the hero image together instead
of stacked as two separate rows.

MobileBottomNav (Android's native bottom bar): removed the Settings
tab entirely - it was actually rendering as a profile-avatar-with-
quick-switch-menu, which is now redundant with the Home hero's avatar
(wired to the same Settings destination). Bottom nav is Home/Discover/
Calendar/Library only. Also dropped the text labels and the selected-
state background pill per direction - just icons now, filled variant
when selected vs outline otherwise, matching the "make icon filled
when clicked, that's it" ask exactly.

Compile-verified on shared commonMain, iosMain, and both Android
flavors.
2026-07-14 15:10:56 +03:00
KhooLy
48f2488ee0 Overlay Home top bar/tabs on hero, center hero content, drop bottom-nav Profile
The bell+avatar row and Home/Series/Movies tabs were rendered as a
separate solid-background section stacked above the hero - reference
image has them overlaid directly on the hero image, scrolling away
together with it. Moved FluxaHomeOverlayBar into the same Box as
FluxaHomeHero with a top-down scrim gradient for legibility over
varying artwork, instead of a fixed opaque header.

FluxaNavigationBar (the shared bottom nav, used on iOS since Android
renders its own native MobileBottomNav instead) was iterating every
FluxaDestination including ProfileList/AddonStore/Auth, none of which
belong in a persistent tab row - they're already reached other ways
(the new Home avatar, Settings' manage-addons button, the login flow).
Introduced FluxaBottomNavDestinations restricted to the six real
top-level screens.

Hero content is now center-aligned (logo, title fallback, badges,
description) instead of left-aligned, description line-height
tightened, and runtime is now shown in the badge row for movies -
CatalogItemUiModel didn't carry runtimeLabel before, only Detail's
model did, despite Meta.runtime being available all along.

Compile-verified on shared commonMain, iosMain, and both Android
flavors.
2026-07-14 15:02:59 +03:00
KhooLy
46323a5043 Add Home top bar with real content filter, notifications list screen
No logo/wordmark anywhere (per explicit direction) - just a
notification bell and the active profile's avatar top-right, with a
Home/Series/Movies tab row underneath.

The tabs aren't decorative: HomeViewModel already had a working
currentFilter/setFilter("all"/"movie"/"series") mechanism used
internally for billboard filtering, just never exposed through the
shared UI layer. Added CatalogAction.FilterChanged and
CatalogHomeDataSource.setFilter() to wire it through properly -
AndroidCatalogHomeDataSource now threads currentFilter into
orderHomeCategories() too, which already supported a filter param
that was previously always called with the "all" default.

Notification bell opens a new NotificationsScreen, modeled as an
overlay on FluxaAppState (same pattern as the Detail overlay - a
showNotifications flag, openNotifications()/closeNotifications(), no
new FluxaDestination so it doesn't leak into the enum-driven bottom
nav). It reuses CalendarUiState/CalendarDataSource directly - no new
data source - and groups the existing flat release list into Coming
Soon / New / Later sections using kotlinx-datetime (added as a new
shared dependency; date arithmetic on ISO date strings needs real
calendar math, not string comparison, and there's no JVM-only
java.time available in commonMain). Tapping a notification reuses the
existing CalendarAction.ItemSelected handling to open Detail.

Compile-verified on shared commonMain, iosMain, and both Android
flavors. Visual result and date-bucketing logic unverified against a
running app.
2026-07-14 14:54:39 +03:00
KhooLy
816b9063aa Polish Detail screen, redesign Home hero, add screen transitions
Detail screen fixes from review:
- Resume button content is now actually centered (outer Column lacked
  horizontalAlignment).
- Download buttons (primary + per-episode) get a download icon.
- Season selector is now a visible pill with a dropdown-arrow icon
  instead of plain unstyled text, and opens a ModalBottomSheet listing
  seasons instead of an inline expand - easier to tell it's tappable
  and consistent with the "bottom sheet, not dropdown" request.
- Episode thumbnails are bigger (was fixed 120dp, now split evenly
  with the text column) and synopsis moved to a full-width line below
  the thumbnail+title row instead of being squeezed into the narrow
  text column beside the thumbnail.
- Hero renders the title logo (MetaDetail.logo, now exposed as
  DetailUiModel.logoUrl... already existed, just unused) when
  available, falling back to text title otherwise.

Home hero: same logo-over-text treatment, plus an age-rating badge,
season count, and a description line - all sourced from Meta fields
(description/ageRating/seasonsCount) that were already fetched but
not exposed on CatalogItemUiModel before now.

Screen transitions: FluxaApp's main content switch (destination
changes, opening/closing Detail, entering profile edit) was an
instant when-block swap with zero animation. Wrapped it in
AnimatedContent keyed on a computed screen identity, with a fade+scale
enter and fade exit - the "no animation when navigating" gap.
Per-screen composables switched from Modifier.weight(1f) (needs
ColumnScope, no longer valid once nested inside AnimatedContent's
content lambda) to Modifier.fillMaxSize(), with weight(1f) moved to
the AnimatedContent itself.

Not addressed, scoped out: HBO Max's top bar (app logo, notification
bell, profile avatar) and category tab row (Home/Series/Movies/...)
on the Home screen reference image - Fluxa's Home has no matching
data model (no per-category content-type filter, no notification
system) and the existing bottom FluxaNavigationBar already serves
destination-switching, so adding a duplicate top tab row felt like
UI without backing functionality rather than a real gap.

Compile-verified on shared commonMain, iosMain, and both Android
flavors. Visual result unverified - no way to render Compose UI in
this environment.
2026-07-14 14:38:22 +03:00
KhooLy
00172e9320 Redesign shared Detail screen to match streaming-app reference layouts
Full-bleed tall hero (poster/backdrop) with title, type eyebrow, and
metadata (age rating badge, year, season count or runtime) overlaid
directly on the image via a bottom gradient scrim, matching a
Netflix/HBO Max-style detail layout instead of the earlier flat
sectioned one.

Adds a proper close/back affordance and share icon to the shared
Detail screen for the first time - it previously had no way to
dismiss itself from within the UI. Threaded onBack through
FluxaApp/FluxaAppHost/AppRoutesHost: the in-Home overlay now clears
itself locally (FluxaAppState.clearDetail()), while the standalone
mobile route pops the nav stack via navigateBackSafely - reusing the
same mobileDetailScreen-null guard pattern already established for
onDetailNavigationEvent, so the overlay and route cases don't collide.

Play becomes Resume when there's saved progress (shows "Resume S1 E2"
using the resume episode's season/number when resolvable), with a
Restart button underneath that dispatches the new
DetailAction.Play(fromStart = true) to explicitly bypass resume -
DetailNavigationLogic.forPlay/forStream now take a fromStart flag
that forces progress to 0 regardless of saved position.

Extends DetailUiModel with castNames and ageRating (from MetaDetail,
already fetched, just not exposed to the shared UI model before) and
DetailEpisodeUiModel with runtimeLabel (from Video.episodeRuntime).

Compile-verified on shared commonMain, iosMain, and both Android
flavors. Visual result not verified against the reference images in a
running app - no way to render Compose UI in this environment.
2026-07-14 14:23:28 +03:00
KhooLy
909906fab3 Build real Stremio login for iOS Auth, Keychain-backed session storage
Fixes AppleAuthDataSource: continueWithNuvio/continueWithStremio/
continueWithoutAccount were incorrectly routed through Swift
notifications - Android handles all three as pure local UI-state
transitions (no network), so this matches that instead of adding
unnecessary indirection.

submit() for the Credentials stage now posts a real request that
FluxaAppleAuthStartup.swift picks up: FluxaAppleStremioAuthClient
mirrors StremioService.kt's api.strem.io/api/login and /api/register
endpoints via URLSession, and the resulting session (id/email/authKey)
is persisted through FluxaAppleAuthStore using the Keychain generic-
password API (kSecClassGenericPassword, accessible-after-first-unlock),
not UserDefaults - this is the first credential-bearing data iOS
persists, unlike the existing UserDefaults-backed local stores.

Nuvio login/import is out of scope here: Android's version pulls
profile/addons/library/progress/history/collections through
NuvioAccountImportCoordinator, which needs library and addon-store
integration iOS doesn't have yet. The Nuvio stage transition works
(continueWithNuvio), but submitting Nuvio credentials currently has no
Swift listener - same "reachable but inert" scope already established
for AddonStore.

The error-state message on failed Stremio login is a hardcoded English
string rather than routed through AppStrings, since calling Kotlin's
AppStrings.t() from pure Swift needs framework-export verification I
can't perform without Xcode.

Caveat: this Swift code (Keychain access, URLSession networking) is
unverified - no Xcode/macOS toolchain available in this environment.
Only the Kotlin side (AppleAuthDataSource, all Gradle targets) is
compile-checked. Needs a real Xcode build and device/simulator test
before shipping.
2026-07-14 13:56:30 +03:00
KhooLy
311968075d Unify TV and mobile Detail navigation decisions onto DetailNavigationLogic
Extracts the "which stream/episode plays, is it cs3, how much resume
progress" decision into a pure DetailNavigationLogic object in
commonMain. DetailStore (used by mobile's shared CMP Detail screen)
now delegates to it instead of carrying its own copy.

DetailRoute.kt (Android TV's legacy Detail route - mobile no longer
reaches this file after the earlier Detail route migration) now calls
the same DetailNavigationLogic functions instead of its own hand-
rolled duplicate of the same branching, so both platforms make this
decision through one code path. TvDetailScreenContent's rendering is
untouched - it still reads DetailViewModel directly, since it needs
richer domain data (MetaDetail/Video/Stream) than the lightweight
shared Detail UI models carry.

Preserves the movie-vs-series distinction from the original TV logic:
Play button on a movie always targets a null video id (no accidental
resume-progress carryover), matching the legacy behavior exactly.
2026-07-14 13:51:34 +03:00
KhooLy
f27bb23837 Fix Detail resume-progress and duplicate-navigation bugs from Phase 1
Two real bugs surfaced while scoping the TV Detail work:

1. DetailNavigationEvent didn't carry resume progress, so
   AppRoutesHost's mobile handler applied mobileDetailScreen's
   initialProgress unconditionally to whatever stream/episode got
   played - including a different episode than the one that was
   actually paused. DetailStore now computes progress the same way
   the legacy DetailRoute.kt always did: only resume if the target
   video id matches the saved one, otherwise start at 0. Fixed the
   underlying resume/lastVideoId resolution in AndroidDetailDataSource
   too, which wasn't honoring the DetailRequestUiModel's own
   lastVideoId/initialProgress overrides (screen.lastVideoId ??
   savedPlayback in the legacy code) - it only ever looked at
   savedPlayback.

2. onDetailNavigationEvent fired unconditionally on the shared
   FluxaAppHost instance, which is reused for both the in-Home Detail
   overlay and the standalone Screen.Detail route. Clicking Play in
   the overlay already had a working navigation path via
   onSharedPlayRequested (a separate, pre-existing callback) - Phase 1
   added a second, redundant navigateTo() call on top of it for every
   overlay Play/stream click. Guarded the handler to only act when
   mobileDetailScreen is non-null (i.e. actually in the standalone
   route), restoring the overlay to its original single-navigation
   behavior.
2026-07-14 13:46:26 +03:00
KhooLy
153bbd4452 Wire iOS Settings/AddonStore/Auth into AppleFluxaPlatformServices
AppleSettingsDataSource is a real, fully local implementation backed
by NSUserDefaults - all ~50 SettingsUiState fields (general,
appearance, playback, subtitles, advanced, addons, downloads, system,
TMDB account toggles) persist across launches and round-trip through
update methods immediately, with no backend dependency since Settings
is pure preference storage. Content-feed toggle/reorder actions and
sync-disconnect stay no-ops since they depend on addon catalog/sync
integration iOS doesn't have yet.

AppleAddonStoreDataSource and AppleAuthDataSource are compile-only
bridges: real UI-state handling (input text, form fields, detected
input type) plus NSNotificationCenter action dispatch following the
established Apple*DataSource pattern, but the notifications currently
have no Swift-side listener. Unlike Detail/Library/Discover/Calendar,
Android's AddonStore/Auth data sources call direct Kotlin services
(PluginManager, StremioRepository, AppContainer.authService) with no
Rust-headless or existing Swift-networking equivalent to bridge to -
building real login/addon-manifest networking for iOS is a separate,
larger effort involving actual security/product decisions (session
storage, Keychain use) that shouldn't be guessed at.

AppleFluxaPlatformServices now implements FluxaSettingsServices,
FluxaAddonStoreServices, FluxaAuthServices, making the shared
Settings/AddonStore/Auth CMP screens reachable via FluxaAppHost on
iOS. Compile-verified across shared (iosMain + commonMain) and both
Android flavors.
2026-07-14 13:32:59 +03:00
KhooLy
d169ecb9cb Fix pre-existing iOS compile errors in Discover/Library/Profile data sources
These three files were already broken in the working tree, written
against older shapes of DiscoverDataSource/LibraryDataSource/
ProfileDataSource before those contracts evolved (loadMore() added to
Discover; LibraryUiState restructured from a flat watchlist into
planned/completed/favorites/collections/downloadGroups; Profile's
settings fields split out into the separate SettingsDataSource/
SettingsUiState feature, leaving ProfileDataSource with pin/biometric/
save-profile methods only).

AppleDiscoverDataSource: added loadMore() posting the current filters
as a load-more notification.

AppleLibraryDataSource: updateJson() now decodes the current
LibraryUiState shape; added createCollection/renameCollection/
deleteCollection/cancelDownload following the existing notification-
dispatch pattern.

AppleProfileDataSource: rewritten against the current ProfileDataSource
interface (no more SettingsUiState) - single default Apple profile
backed by NSUserDefaults for name/avatar/pin/biometric flag, matching
the local-store pattern already used by FluxaAppleLibraryStore.swift.

:shared:compileKotlinIosSimulatorArm64 now builds clean.
2026-07-14 13:26:18 +03:00
KhooLy
a383bbe815 Fix AppleDetailDataSource for extended DetailDataSource interface
The Phase 1 DetailContracts extension (season/episode/streams/
downloads) added five new abstract members to DetailDataSource that
AppleDetailDataSource didn't implement, breaking the iOS build. Adds
NSNotificationCenter dispatch for each, following the same
postNotificationName pattern the existing loadDetail/toggleWatchlist
methods use, plus the missing runtimeLabel field in the JSON decode.
2026-07-14 13:24:08 +03:00
KhooLy
2c802982f7 Rewire Android TV Home screen onto shared CatalogHomeStore
TV's HomeScreen.kt now sources its row/billboard data from the same
CatalogHomeStore + AndroidCatalogHomeDataSource that mobile's shared
CMP Home already uses, instead of the TV-only rememberHomeCatalogState
/ rememberHomeBillboardState state holders. Row ordering, top-ten
flags, action-row detection, and hero fallback resolution now live in
AndroidCatalogHomeDataSource so both platforms share one source of
truth.

TvHomeRowSpec, HomeShelfRow, HomeHeroPanel, and TvMovieCard are
untouched and still render raw Meta (that's pre-existing TV behavior,
not a regression) - CatalogHomeDataSource.resolveMeta() bridges from
the lightweight shared CatalogItemUiModel back to Meta at this
boundary, the same pattern used for streams in
AndroidDetailDataSource.resolveStream(). All existing focus-
restoration logic (FocusRequester chains, scroll/focus persistence,
hero-follows-focus) is preserved verbatim - only the state source
changed.

Compile-verified on both mobile and tv flavors; focus/D-pad behavior
needs on-device verification, not testable in this environment.
2026-07-14 13:18:29 +03:00
KhooLy
16e8db04c1 Extend shared Catalog Home contract for TV: billboard, row metadata
Adds billboard/hero state, per-row cardLayout/isActionRow/topTenEnabled
flags, and an ItemFocused action to CatalogHomeUiState, keeping
CatalogItemUiModel itself unchanged (still no domain objects - Meta
stays out of anything a LazyRow/LazyColumn renders, since Stream/Meta
carry mutable fields that make Compose unable to skip recomposition).

AndroidCatalogHomeDataSource now populates these from HomeViewModel's
existing billboard/category state, and exposes resolveMeta(id, type)
as an Android-only boundary helper so navigation code can reconstruct
a full Meta for Screen.Detail/Screen.Player without it ever touching
the UI layer - same resolve-at-the-boundary pattern used for streams
in AndroidDetailDataSource.resolveStream().

Groundwork for migrating the Android TV Home screen off HomeViewModel
onto the same shared CatalogHomeStore mobile already uses; the TV
Compose screen itself is not yet rewired.
2026-07-14 13:14:03 +03:00
KhooLy
db10acd3b2 Route Screen.Detail through shared CMP Detail screen on Android mobile
Adds season/episode picker and sources list to the shared Detail
composable, switches FluxaAppState's selectedDetail to carry a
DetailRequestUiModel directly (instead of a full CatalogItemUiModel)
so the standalone Screen.Detail route can seed it without going
through a catalog item first, and threads a detailRequest +
onDetailNavigationEvent pair through FluxaAppHost.

AppRoutesHost now builds a DetailRequestUiModel from Screen.Detail and
hands the shared Detail screen's navigation decisions (play a
specific stream vs. go pick sources, based on cs3: id prefixes) back
to Screen.Player / Screen.Sources, reconstructing the full Stream
object via AndroidDetailDataSource.resolveStream() so headers/
subtitles aren't lost. TV still uses the legacy DetailRoute for now.
2026-07-14 13:06:39 +03:00
KhooLy
edf4a9d7a3 Extend shared Detail contract with season/episode/streams/downloads
Adds season/episode picking, stream listing/filtering, and download
actions to the shared Detail feature (DetailContracts, DetailStore),
and wires AndroidDetailDataSource to the existing DetailViewModel to
back them. Navigation decisions (play directly vs. go to source
selection, based on cs3: id prefixes) now live in DetailStore and are
exposed via a DetailNavigationEvent flow for the route layer to
consume.

First slice of migrating the standalone Detail route off the legacy
Android-only DetailScreen onto the shared CMP Detail screen.
2026-07-14 12:59:22 +03:00
KhooLy
0f588b67e7 Fix Playback icon, enlarge Settings row icons, fix bottom-bar overlap
Swaps the Playback icon from a circled play button to a video/on-demand
icon, and bumps every Settings row icon (nav rows, brand action rows) to a
larger size for better legibility.

Adds bottom content padding (120dp, matching Home/Discover) to the shared
Settings and Library screens so their content isn't hidden behind the
mobile bottom navigation bar, which neither screen accounted for.
2026-07-14 01:48:16 +03:00
KhooLy
e45b3acaa4 Add real brand logos to Settings account rows, fix Catalogs icon, drop MAL
Reuses the app's existing ic_stremio/ic_nuvio/ic_trakt/ic_simkl/ic_anilist
drawables as icon slots on the shared Settings account rows, matching the
pattern already used for the Auth screen's provider buttons. Swaps the
Catalogs nav icon from a single open book to a stacked-books icon.

Also removes MyAnimeList support from the shared Settings feature end to
end (contracts, store, screen, Android data source, host wiring) since the
product no longer supports it — only Stremio/Nuvio/Trakt/Simkl/AniList
remain.
2026-07-14 01:42:37 +03:00
KhooLy
339e525d34 Polish shared Settings screen: nav icons, profile avatar, drop restart button
Adds Material icons to each Settings hub nav row (Account, General,
Appearance, Playback, Catalogs, Add-ons, Downloads, Developer) and shows
the active profile's avatar next to the display name at the top of the hub.
Removes the "Restart app" action end-to-end (action, store handling,
FluxaAppHost plumbing) since it was a no-op in both native and shared code.
2026-07-14 01:33:22 +03:00
KhooLy
c983e6702f Migrate Library/Watchlist screen to shared Compose Multiplatform (full parity)
Extends the shared Library feature from a bare watchlist grid to the full
native surface: Planned/Completed/Favorites/Downloads/Collections sections,
a movie/series/anime type filter, user collection create/rename/delete, and
offline download folders with per-episode progress and cancel.

AndroidLibraryDataSource adapts HomeViewModel's watchlist/likedItems/
libraryUiState (Trakt/MAL/Simkl planned & completed) and OfflineDownloadManager
to the shared contract, reusing the existing toOfflineDownloadGroups()/
formatDownloadBytes() helpers rather than reimplementing them. One known
simplification: user collections are basic title-only entries (create/
rename/delete) without the native app's deeper folder/catalog-source editor
and JSON import/export, which is a materially separate sub-feature.

Mobile now routes Screen.Watchlist through the shared Library destination;
Android TV keeps its native WatchlistScreen since TV migration is deferred.
2026-07-14 01:21:58 +03:00
KhooLy
5c7aa912d4 Migrate Settings screen to shared Compose Multiplatform (full parity)
Adds a comprehensive Settings feature under shared/commonMain covering all
9 native categories: Account (Stremio/Nuvio/Trakt/MAL/Simkl/AniList/TMDB/
notifications), General, Appearance (+ Home/Detail sub-pages), Playback
(+ Subtitles/Advanced sub-pages), Content (hero/home/top-10 catalog feed
toggles and reordering), Addons, Downloads, and System/Developer.

AndroidSettingsDataSource adapts existing UserProfile fields, HomeViewModel's
addon/CS3 feed discovery, OfflineDownloadManager, and LastMediaDebugInfoStore
to the shared contract with no new business logic, reusing the existing
domain.discovery.MetadataFeeds helpers for feed selection/ordering. All UI
strings reuse the existing i18n keys already shipped for the native mobile
settings screen rather than introducing new ones.

This replaces the old minimal ProfileSettingsScreen stub (2 toggles) that
previously backed FluxaDestination.Settings. Mobile now routes Screen.Settings
through the shared screen; Android TV keeps its native SettingsScreen since
TV migration is deferred.
2026-07-14 01:08:56 +03:00
KhooLy
609bd0ff15 Migrate Profile list/switch/edit to shared Compose Multiplatform
Adds ProfileListScreen (avatar grid, manage mode, PIN unlock overlay with
biometric fast-path) and ProfileEditScreen (name, avatar upload, PIN,
biometric toggle, delete) under shared/commonMain, extending the existing
ProfileContracts/Store stub rather than replacing it. AndroidProfileDataSource
gains PIN-gated profile switching, save/delete, all backed by the existing
ProfileManager/PinHasher — no new business logic. The avatar image picker and
BiometricPrompt stay Android-side and are bubbled into the shared screen via
plain callbacks. Mobile now routes Screen.Profiles through the shared
ProfileList destination; Android TV keeps native ProfileScreen/ProfileEditScreen
since TV migration is deferred.
2026-07-14 00:38:57 +03:00
KhooLy
7fdf667f7e Migrate Login/Welcome/Nuvio auth flow to shared Compose Multiplatform
Adds AuthContracts/Store/Screen under shared/commonMain covering the
credentials (login/signup/Stremio), Nuvio sign-in, and Nuvio import
stages that previously lived only in MobileLoginScreen/MobileNuvioLoginScreen.
AndroidAuthDataSource adapts the existing StremioService/NuvioAccountImportCoordinator/
ProfileManager logic to the shared contract, with no new business logic.
Mobile now routes Screen.Welcome/Screen.Login through the shared screen;
Android TV keeps its native LoginScreen/WelcomeScreen since TV migration
is deferred.
2026-07-14 00:23:46 +03:00
KhooLy
5125c3446a Migrate AddonStore screen to shared Compose Multiplatform
Adds AddonStoreContracts/Store/Screen under shared/commonMain following
the established Discover/Library pattern, wires it into FluxaApp/FluxaAppHost
as a new destination, and adds AndroidAddonStoreDataSource which adapts the
existing PluginManager/StremioRepository/AddonActions logic to the shared
contract. Mobile now routes Screen.AddonStore through the shared CMP screen;
Android TV keeps the native AddonStoreScreen for now since TV migration is
deferred.
2026-07-14 00:11:04 +03:00
KhooLy
696bf49b76 Make Discover filter row scrollable, cap pill label width
Type/Catalog/Genre pills sat in a plain Row with no scroll container
and unconstrained label width, so a long catalog name would grow its
pill wide enough to push Genre off-screen with no way to reach it.
Added horizontalScroll to the row (matches Nuvio's own filter row) and
capped each pill's label at 140dp so one long name can no longer
dominate the whole row even when scrollable.
2026-07-13 23:28:37 +03:00
KhooLy
83c3e0f3c7 Show skeleton on actual Discover filter changes, not just first load
Changing type/catalog/genre didn't clear the previous results, so the
old (now-mismatched) results stayed visible for the entire fetch
duration instead of the skeleton grid — results only ever got replaced
once the new fetch completed. Only clear on a genuine filter change
(compare against the previous DiscoverFiltersUiModel) so a plain tab
revisit with unchanged filters still silently keeps showing cached
results while it revalidates in the background, per the earlier fix.
2026-07-13 23:14:53 +03:00
KhooLy
f14b0a383d Add Discover infinite scroll, fix bottom nav overlapping content
Discover only ever showed one page of results with no way to load
more on scroll. The backend piece (per-catalog page fetch with skip+
genre) already existed in Rust (dispatch_discover_page/FetchDiscoverPage)
but was never wired to an Android effect handler at all, and its
results-append was gated behind the internal multi-catalog-merge flow
that a manual load-more wouldn't go through.

- Rust: made the results.extend() in the fetchDiscoverPage completion
  unconditional (was only running when continuing the internal merge
  chain); the recursive dispatch_next_initial_page call stays gated.
- Kotlin: added the missing "fetchDiscoverPage" case (reuses the
  existing fetchCatalogPage handler, identical payload shape), and
  HomeViewModel.loadMoreDiscoverResults() which dispatches
  discoverPageRequested with skip = current result count and folds
  the returned page into both _headlessDiscoverResults and
  _headlessDiscoverResultSources (the latter needed since new items
  otherwise had no addon source mapped, breaking playback routing).
- Shared: DiscoverAction.LoadMore end to end (contract, store,
  AndroidDiscoverDataSource resolving the selected catalog's
  transportUrl/id from state, DiscoverScreen triggering it via a
  LazyGridState scroll-near-end check).

Also fixed the mobile bottom nav bar overlapping Discover/Search
content — their grids only had bottom=20dp contentPadding instead of
the 120dp already used elsewhere to clear the floating nav bar.
2026-07-13 23:08:33 +03:00
KhooLy
713cd42ae3 Cache catalog fetch results (memory, 10min TTL), same pattern as the addon list
getAddonCatalogResult had no caching at all, unlike getUserAddons which
already uses RepositoryMemoryCache. Every Discover filter change (type/
catalog/genre) or revisit re-fetched from the addon over the network
with zero speedup, even for a combination already fetched moments
earlier. Reuses the same RepositoryMemoryCache (10min TTL, already
injected into this class) keyed by transportUrl+type+id+skip+genre+
search. Doesn't fix a slow addon's first-load latency, but repeat
visits to the same catalog+genre combo are now instant.
2026-07-13 22:55:55 +03:00