Full Code of thomaskioko/tv-maniac for AI

main fd3340db192b cached
1716 files
9.6 MB
2.7M tokens
1 requests
Copy disabled (too large) Download .txt
Showing preview only (10,705K chars total). Download the full file to get everything.
Repository: thomaskioko/tv-maniac
Branch: main
Commit: fd3340db192b
Files: 1716
Total size: 9.6 MB

Directory structure:
gitextract_xytv9ppo/

├── .editorconfig
├── .geminiignore
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.yml
│   │   ├── config.yml
│   │   └── feature_request.yml
│   ├── actions/
│   │   ├── setup-android-release/
│   │   │   └── action.yml
│   │   ├── setup-gradle/
│   │   │   └── action.yml
│   │   ├── setup-ios/
│   │   │   └── action.yml
│   │   └── setup-ios-release/
│   │       └── action.yml
│   ├── release.yml
│   ├── renovate.json
│   └── workflows/
│       ├── baseline-profile.yml
│       ├── beta-release.yml
│       ├── ci.yml
│       ├── compare-screenshot.yml
│       ├── daily-build.yml
│       ├── nightly-integration-tests.yml
│       ├── promote-release.yml
│       ├── release.yml
│       └── store-screenshot.yml
├── .gitignore
├── .idea/
│   ├── codeStyles/
│   │   ├── Project.xml
│   │   └── codeStyleConfig.xml
│   └── dictionaries/
│       └── project.xml
├── .ruby-version
├── .swiftformat
├── .swiftlint.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── GEMINI.md
├── Gemfile
├── LICENSE
├── README.md
├── android-designsystem/
│   ├── build.gradle.kts
│   └── src/
│       ├── debug/
│       │   └── res/
│       │       └── values/
│       │           └── strings.xml
│       ├── main/
│       │   ├── kotlin/
│       │   │   └── com/
│       │   │       └── thomaskioko/
│       │   │           └── tvmaniac/
│       │   │               └── compose/
│       │   │                   ├── components/
│       │   │                   │   ├── Background.kt
│       │   │                   │   ├── BadgeChip.kt
│       │   │                   │   ├── Buttons.kt
│       │   │                   │   ├── Card.kt
│       │   │                   │   ├── Chip.kt
│       │   │                   │   ├── Dialogs.kt
│       │   │                   │   ├── EmptyLayout.kt
│       │   │                   │   ├── ErrorLayout.kt
│       │   │                   │   ├── FilterChipSection.kt
│       │   │                   │   ├── GradientScrim.kt
│       │   │                   │   ├── Image.kt
│       │   │                   │   ├── NavigationBar.kt
│       │   │                   │   ├── NotificationRationaleContent.kt
│       │   │                   │   ├── PosterPlaceholder.kt
│       │   │                   │   ├── ProgressIndicator.kt
│       │   │                   │   ├── ScanlineOverlay.kt
│       │   │                   │   ├── SearchTextField.kt
│       │   │                   │   ├── SegmentedProgressBar.kt
│       │   │                   │   ├── SheetDragHandle.kt
│       │   │                   │   ├── ShowLinearProgressIndicator.kt
│       │   │                   │   ├── Snackbar.kt
│       │   │                   │   ├── Text.kt
│       │   │                   │   ├── TextTitlePill.kt
│       │   │                   │   ├── TopBar.kt
│       │   │                   │   ├── TvManiacBottomSheet.kt
│       │   │                   │   └── TvManiacPreviewWrapperProvider.kt
│       │   │                   ├── extensions/
│       │   │                   │   ├── GradientExtensions.kt
│       │   │                   │   ├── LazyListExtensions.kt
│       │   │                   │   ├── PaddingValuesExtentions.kt
│       │   │                   │   └── ScrimExtentions.kt
│       │   │                   ├── theme/
│       │   │                   │   ├── Background.kt
│       │   │                   │   ├── Colors.kt
│       │   │                   │   ├── Shape.kt
│       │   │                   │   ├── Theme.kt
│       │   │                   │   └── Type.kt
│       │   │                   └── util/
│       │   │                       ├── AutoAdvanceLocal.kt
│       │   │                       └── DynamicTheming.kt
│       │   └── res/
│       │       └── values/
│       │           └── strings.xml
│       └── test/
│           └── kotlin/
│               └── com/
│                   └── thomaskioko/
│                       └── tvmaniac/
│                           └── compose/
│                               └── roborazzi/
│                                   ├── NotificationRationaleContentScreenshotTest.kt
│                                   └── TvManiacSnackBarScreenshotTest.kt
├── api/
│   ├── tmdb/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── tmdb/
│   │   │                               └── api/
│   │   │                                   ├── TmdbConfig.kt
│   │   │                                   ├── TmdbSeasonDetailsNetworkDataSource.kt
│   │   │                                   ├── TmdbShowDetailsNetworkDataSource.kt
│   │   │                                   ├── TmdbShowsNetworkDataSource.kt
│   │   │                                   └── model/
│   │   │                                       ├── CreditsResponse.kt
│   │   │                                       ├── EpisodesResponse.kt
│   │   │                                       ├── GenreResponse.kt
│   │   │                                       ├── ImagesResponse.kt
│   │   │                                       ├── LastEpisodeToAirResponse.kt
│   │   │                                       ├── NetworksResponse.kt
│   │   │                                       ├── NextEpisodeToAirResponse.kt
│   │   │                                       ├── SeasonsResponse.kt
│   │   │                                       ├── TmdbGenreResult.kt
│   │   │                                       ├── TmdbSeasonDetailsResponse.kt
│   │   │                                       ├── TmdbShowDetailsResponse.kt
│   │   │                                       ├── TmdbShowResponse.kt
│   │   │                                       ├── VideosResponse.kt
│   │   │                                       └── WatchProvidersResult.kt
│   │   └── implementation/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           ├── androidMain/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── tmdb/
│   │           │                       └── implementation/
│   │           │                           └── TmdbPlatformBindingContainer.kt
│   │           ├── commonMain/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── tmdb/
│   │           │                       └── implementation/
│   │           │                           ├── DefaultTmdbSeasonDetailsNetworkDataSource.kt
│   │           │                           ├── DefaultTmdbShowDetailsNetworkDataSource.kt
│   │           │                           ├── DefaultTmdbShowsNetworkDataSource.kt
│   │           │                           ├── TmdbBindingContainer.kt
│   │           │                           └── TmdbClient.kt
│   │           └── iosMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── tmdb/
│   │                                   └── implementation/
│   │                                       └── TmdbPlatformBindingContainer.kt
│   └── trakt/
│       ├── api/
│       │   ├── build.gradle.kts
│       │   └── src/
│       │       └── commonMain/
│       │           └── kotlin/
│       │               └── com/
│       │                   └── thomaskioko/
│       │                       └── tvmaniac/
│       │                           └── trakt/
│       │                               └── api/
│       │                                   ├── TraktCalendarRemoteDataSource.kt
│       │                                   ├── TraktConfig.kt
│       │                                   ├── TraktEpisodeHistoryRemoteDataSource.kt
│       │                                   ├── TraktListRemoteDataSource.kt
│       │                                   ├── TraktShowsRemoteDataSource.kt
│       │                                   ├── TraktSyncRemoteDataSource.kt
│       │                                   ├── TraktTokenRemoteDataSource.kt
│       │                                   ├── TraktUserRemoteDataSource.kt
│       │                                   └── model/
│       │                                       ├── AccessTokenBody.kt
│       │                                       ├── RefreshAccessTokenBody.kt
│       │                                       ├── TraktAccessRefreshTokenResponse.kt
│       │                                       ├── TraktAccessTokenResponse.kt
│       │                                       ├── TraktAddShowRequest.kt
│       │                                       ├── TraktAddShowToListResponse.kt
│       │                                       ├── TraktCalendarResponse.kt
│       │                                       ├── TraktCreateListRequest.kt
│       │                                       ├── TraktCreateListResponse.kt
│       │                                       ├── TraktFollowedShowResponse.kt
│       │                                       ├── TraktGenreResponse.kt
│       │                                       ├── TraktLastActivitiesResponse.kt
│       │                                       ├── TraktNextEpisodeResponse.kt
│       │                                       ├── TraktPeopleResponse.kt
│       │                                       ├── TraktPersonalListsResponse.kt
│       │                                       ├── TraktRemoveShowFromListResponse.kt
│       │                                       ├── TraktSeasonEpisodesResponse.kt
│       │                                       ├── TraktSeasonsResponse.kt
│       │                                       ├── TraktShowsResponse.kt
│       │                                       ├── TraktSyncModels.kt
│       │                                       ├── TraktUserResponse.kt
│       │                                       ├── TraktUserStatsResponse.kt
│       │                                       ├── TraktVideosResponse.kt
│       │                                       └── TraktWatchedProgressResponse.kt
│       └── implementation/
│           ├── build.gradle.kts
│           └── src/
│               ├── androidMain/
│               │   └── kotlin/
│               │       └── com/
│               │           └── thomaskioko/
│               │               └── trakt/
│               │                   └── service/
│               │                       └── implementation/
│               │                           └── TraktPlatformBindingContainer.kt
│               ├── commonMain/
│               │   └── kotlin/
│               │       └── com/
│               │           └── thomaskioko/
│               │               └── trakt/
│               │                   └── service/
│               │                       └── implementation/
│               │                           ├── TraktAuthPlugin.kt
│               │                           ├── TraktBindingContainer.kt
│               │                           ├── TraktHttpClient.kt
│               │                           └── api/
│               │                               ├── DefaultTraktCalendarRemoteDataSource.kt
│               │                               ├── DefaultTraktEpisodeRemoteDataSource.kt
│               │                               ├── DefaultTraktListRemoteDataSource.kt
│               │                               ├── DefaultTraktShowsRemoteDataSource.kt
│               │                               ├── DefaultTraktSyncRemoteDataSource.kt
│               │                               ├── DefaultTraktTokenRemoteDataSource.kt
│               │                               └── DefaultTraktUserRemoteDataSource.kt
│               ├── commonTest/
│               │   └── kotlin/
│               │       └── com/
│               │           └── thomaskioko/
│               │               └── trakt/
│               │                   └── service/
│               │                       └── implementation/
│               │                           └── TraktAuthGuardPluginTest.kt
│               ├── iosMain/
│               │   └── kotlin/
│               │       └── com/
│               │           └── thomaskioko/
│               │               └── trakt/
│               │                   └── service/
│               │                       └── implementation/
│               │                           └── TraktPlatformBindingContainer.kt
│               └── jvmTest/
│                   ├── kotlin/
│                   │   └── com/
│                   │       └── thomaskioko/
│                   │           └── trakt/
│                   │               └── service/
│                   │                   └── implementation/
│                   │                       ├── TestResourceLoader.jvm.kt
│                   │                       └── api/
│                   │                           └── DefaultTraktListRemoteDataSourceTest.kt
│                   └── resources/
│                       ├── trakt_add_show_response.json
│                       ├── trakt_error_response.json
│                       └── trakt_user_response.json
├── app/
│   ├── benchmark-rules.pro
│   ├── build.gradle.kts
│   ├── lint-baseline.xml
│   ├── proguard-rules.pro
│   └── src/
│       ├── androidTest/
│       │   └── kotlin/
│       │       └── com/
│       │           └── thomaskioko/
│       │               └── tvmaniac/
│       │                   └── app/
│       │                       └── test/
│       │                           └── runner/
│       │                               └── TvManiacInstrumentationRunner.kt
│       ├── debug/
│       │   ├── AndroidManifest.xml
│       │   ├── kotlin/
│       │   │   └── com/
│       │   │       └── thomaskioko/
│       │   │           └── tvmaniac/
│       │   │               └── app/
│       │   │                   └── debug/
│       │   │                       ├── DebugNotificationIconProvider.kt
│       │   │                       ├── DebugNotificationInitializer.kt
│       │   │                       └── di/
│       │   │                           └── DebugNotificationInitializerBindingContainer.kt
│       │   └── res/
│       │       └── drawable/
│       │           ├── ic_app_launcher.xml
│       │           ├── ic_debug_bug.xml
│       │           └── ic_launcher_foreground.xml
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   ├── generated/
│       │   │   └── baselineProfiles/
│       │   │       ├── baseline-prof.txt
│       │   │       └── startup-prof.txt
│       │   ├── kotlin/
│       │   │   └── com/
│       │   │       └── thomaskioko/
│       │   │           └── tvmaniac/
│       │   │               └── app/
│       │   │                   ├── MainActivity.kt
│       │   │                   ├── TvManicApplication.kt
│       │   │                   ├── di/
│       │   │                   │   ├── ActivityGraph.kt
│       │   │                   │   └── ApplicationGraph.kt
│       │   │                   └── util/
│       │   │                       ├── AppNotificationIconProvider.kt
│       │   │                       └── TvManiacWorkerFactory.kt
│       │   └── res/
│       │       ├── drawable/
│       │       │   ├── ic_app_launcher.xml
│       │       │   ├── ic_launcher_background.xml
│       │       │   ├── ic_launcher_foreground.xml
│       │       │   └── ic_launcher_monochrome.xml
│       │       ├── mipmap-anydpi-v26/
│       │       │   ├── ic_launcher.xml
│       │       │   └── ic_launcher_round.xml
│       │       ├── values/
│       │       │   ├── colors.xml
│       │       │   └── themes.xml
│       │       ├── values-night/
│       │       │   ├── colors.xml
│       │       │   └── themes.xml
│       │       └── xml/
│       │           ├── backup_rules.xml
│       │           └── data_extraction_rules.xml
│       ├── sharedTest/
│       │   └── kotlin/
│       │       └── com/
│       │           └── thomaskioko/
│       │               └── tvmaniac/
│       │                   └── app/
│       │                       └── test/
│       │                           ├── BaseAppFlowTest.kt
│       │                           ├── TestAppComponent.kt
│       │                           ├── TvManiacTestApplication.kt
│       │                           └── compose/
│       │                               ├── TvManiacTestActivity.kt
│       │                               ├── flows/
│       │                               │   ├── calendar/
│       │                               │   │   └── CalendarFlowTest.kt
│       │                               │   ├── discover/
│       │                               │   │   ├── DiscoverToSeasonDetailsFlowTest.kt
│       │                               │   │   └── DiscoverToShowDetailsFollowFlowTest.kt
│       │                               │   ├── library/
│       │                               │   │   └── LibraryFlowTest.kt
│       │                               │   ├── search/
│       │                               │   │   └── SearchFlowTest.kt
│       │                               │   ├── seasons/
│       │                               │   │   └── SeasonFlowTest.kt
│       │                               │   ├── settings/
│       │                               │   │   └── SettingsFlowTest.kt
│       │                               │   ├── sheet/
│       │                               │   │   └── EpisodeSheetFlowTest.kt
│       │                               │   ├── showdetails/
│       │                               │   │   └── ShowDetailsFeaturesFlowTest.kt
│       │                               │   ├── upnext/
│       │                               │   │   └── UpNextFlowTests.kt
│       │                               │   └── userlists/
│       │                               │       └── UserListFlowTests.kt
│       │                               ├── journey/
│       │                               │   ├── AuthenticatedUserJourneyTest.kt
│       │                               │   └── UnauthenticatedUserJourneyTest.kt
│       │                               ├── robot/
│       │                               │   ├── CalendarRobot.kt
│       │                               │   ├── DiscoverRobot.kt
│       │                               │   ├── EpisodeSheetRobot.kt
│       │                               │   ├── HomeRobot.kt
│       │                               │   ├── LibraryRobot.kt
│       │                               │   ├── ProfileRobot.kt
│       │                               │   ├── ProgressRobot.kt
│       │                               │   ├── RootRobot.kt
│       │                               │   ├── SearchRobot.kt
│       │                               │   ├── SeasonDetailsRobot.kt
│       │                               │   ├── SettingsRobot.kt
│       │                               │   └── ShowDetailsRobot.kt
│       │                               └── stubs/
│       │                                   └── Scenarios.kt
│       └── test/
│           └── kotlin/
│               └── com/
│                   └── thomaskioko/
│                       └── tvmaniac/
│                           └── app/
│                               └── test/
│                                   └── graph/
│                                       ├── GraphFactories.kt
│                                       └── NavigationRouteTest.kt
├── benchmark/
│   ├── build.gradle.kts
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           └── kotlin/
│               └── com/
│                   └── thomaskioko/
│                       └── tvmaniac/
│                           └── benchmark/
│                               ├── Common.kt
│                               ├── baselineprofile/
│                               │   └── BaselineProfileGenerator.kt
│                               └── benchmark/
│                                   └── StartupBenchmarks.kt
├── build.gradle.kts
├── cliff.toml
├── compose-stability.conf
├── core/
│   ├── appconfig/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── appconfig/
│   │   │                               └── ApplicationInfo.kt
│   │   └── implementation/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           ├── androidMain/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── appconfig/
│   │           │                       └── AndroidAppConfigBindingContainer.kt
│   │           ├── commonMain/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── appconfig/
│   │           │                       ├── DefaultTmdbConfig.kt
│   │           │                       └── DefaultTraktConfig.kt
│   │           └── iosMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── appconfig/
│   │                                   └── IosAppConfigBindingContainer.kt
│   ├── base/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       ├── androidMain/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── core/
│   │       │                       └── base/
│   │       │                           └── di/
│   │       │                               └── BaseAndroidBindingContainer.kt
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── core/
│   │                               └── base/
│   │                                   ├── ActivityScope.kt
│   │                                   ├── AppInitializers.kt
│   │                                   ├── Initializer.kt
│   │                                   ├── Qualifiers.kt
│   │                                   ├── di/
│   │                                   │   ├── BaseBindingContainer.kt
│   │                                   │   └── InitializerMultibindings.kt
│   │                                   ├── extensions/
│   │                                   │   ├── Combine.kt
│   │                                   │   ├── DecomposeUtils.kt
│   │                                   │   ├── Lazy.kt
│   │                                   │   └── ParallelUtils.kt
│   │                                   ├── interactor/
│   │                                   │   └── Interactor.kt
│   │                                   └── model/
│   │                                       └── AppCoroutineDispatchers.kt
│   ├── connectivity/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── core/
│   │   │                               └── connectivity/
│   │   │                                   └── api/
│   │   │                                       └── InternetConnectionChecker.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── androidMain/
│   │   │       │   ├── AndroidManifest.xml
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── core/
│   │   │       │                       └── connectivity/
│   │   │       │                           └── implementation/
│   │   │       │                               └── PlatformInternetConnectionChecker.android.kt
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── core/
│   │   │       │                       └── connectivity/
│   │   │       │                           └── implementation/
│   │   │       │                               └── PlatformInternetConnectionChecker.kt
│   │   │       ├── iosMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── core/
│   │   │       │                       └── connectivity/
│   │   │       │                           └── implementation/
│   │   │       │                               └── PlatformInternetConnectionChecker.ios.kt
│   │   │       └── jvmMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── core/
│   │   │                               └── connectivity/
│   │   │                                   └── implementation/
│   │   │                                       └── PlatformInternetConnectionChecker.jvm.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── core/
│   │                                   └── connectivity/
│   │                                       └── testing/
│   │                                           └── FakeInternetConnectionChecker.kt
│   ├── imageloading/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── imageloading/
│   │   │                               └── api/
│   │   │                                   └── ImageQualityProvider.kt
│   │   └── implementation/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── main/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── imageloading/
│   │                                   └── implementation/
│   │                                       ├── CoilImageLoaderFactory.kt
│   │                                       ├── CoilImageLoaderInitializer.kt
│   │                                       ├── DefaultImageQualityProvider.kt
│   │                                       ├── di/
│   │                                       │   ├── CoilImageLoaderInitializerBindingContainer.kt
│   │                                       │   └── ImageLoadingBindingContainer.kt
│   │                                       └── interceptors/
│   │                                           └── TmdbInterceptor.kt
│   ├── integration/
│   │   ├── infra/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── androidHostTest/
│   │   │       │   ├── AndroidManifest.xml
│   │   │       │   ├── kotlin/
│   │   │       │   │   └── com/
│   │   │       │   │       └── thomaskioko/
│   │   │       │   │           └── tvmaniac/
│   │   │       │   │               └── testing/
│   │   │       │   │                   └── integration/
│   │   │       │   │                       ├── EndpointsCatalogTest.kt
│   │   │       │   │                       ├── FixtureLoaderTest.kt
│   │   │       │   │                       └── MockEngineHandlerTest.kt
│   │   │       │   └── resources/
│   │   │       │       └── fixtures/
│   │   │       │           └── test/
│   │   │       │               └── hello.json
│   │   │       ├── androidMain/
│   │   │       │   ├── kotlin/
│   │   │       │   │   └── com/
│   │   │       │   │       └── thomaskioko/
│   │   │       │   │           └── tvmaniac/
│   │   │       │   │               └── testing/
│   │   │       │   │                   └── integration/
│   │   │       │   │                       ├── Endpoints.kt
│   │   │       │   │                       ├── MockEngineHandler.kt
│   │   │       │   │                       ├── SearchStubs.kt
│   │   │       │   │                       ├── ShowFixtures.kt
│   │   │       │   │                       ├── bindings/
│   │   │       │   │                       │   ├── TestAuthBindingContainer.kt
│   │   │       │   │                       │   ├── TestConnectivityBindingContainer.kt
│   │   │       │   │                       │   ├── TestDateTimeBindingContainer.kt
│   │   │       │   │                       │   ├── TestDispatcherBindingContainer.kt
│   │   │       │   │                       │   ├── TestImageLoaderBindingContainer.kt
│   │   │       │   │                       │   ├── TestInitializerBindingContainer.kt
│   │   │       │   │                       │   ├── TestLoggerBindingContainer.kt
│   │   │       │   │                       │   ├── TestNotificationBindingContainer.kt
│   │   │       │   │                       │   ├── TestTmdbBindingContainer.kt
│   │   │       │   │                       │   ├── TestTraktAuthManagerBindingContainer.kt
│   │   │       │   │                       │   ├── TestTraktBindingContainer.kt
│   │   │       │   │                       │   └── TestWorkerBindingContainer.kt
│   │   │       │   │                       └── util/
│   │   │       │   │                           └── FixtureLoader.kt
│   │   │       │   └── resources/
│   │   │       │       └── fixtures/
│   │   │       │           ├── empty_array.json
│   │   │       │           ├── tmdb/
│   │   │       │           │   ├── credits/
│   │   │       │           │   │   ├── error.json
│   │   │       │           │   │   └── success.json
│   │   │       │           │   ├── details/
│   │   │       │           │   │   ├── error.json
│   │   │       │           │   │   └── success.json
│   │   │       │           │   ├── discover/
│   │   │       │           │   │   ├── error.json
│   │   │       │           │   │   └── success.json
│   │   │       │           │   └── watchproviders/
│   │   │       │           │       ├── error.json
│   │   │       │           │       └── success.json
│   │   │       │           └── trakt/
│   │   │       │               ├── calendar/
│   │   │       │               │   ├── error.json
│   │   │       │               │   └── success.json
│   │   │       │               ├── episodes/
│   │   │       │               │   ├── season1/
│   │   │       │               │   │   ├── error.json
│   │   │       │               │   │   └── success.json
│   │   │       │               │   └── season2/
│   │   │       │               │       ├── error.json
│   │   │       │               │       └── success.json
│   │   │       │               ├── genres/
│   │   │       │               │   ├── error.json
│   │   │       │               │   └── success.json
│   │   │       │               ├── search/
│   │   │       │               │   ├── error.json
│   │   │       │               │   └── success.json
│   │   │       │               ├── seasons/
│   │   │       │               │   ├── error.json
│   │   │       │               │   └── success.json
│   │   │       │               ├── shows/
│   │   │       │               │   ├── details/
│   │   │       │               │   │   ├── error.json
│   │   │       │               │   │   └── success.json
│   │   │       │               │   ├── favorite/
│   │   │       │               │   │   ├── error.json
│   │   │       │               │   │   └── success.json
│   │   │       │               │   ├── people/
│   │   │       │               │   │   ├── error.json
│   │   │       │               │   │   └── success.json
│   │   │       │               │   ├── popular/
│   │   │       │               │   │   ├── error.json
│   │   │       │               │   │   └── success.json
│   │   │       │               │   ├── progress/
│   │   │       │               │   │   ├── refreshed/
│   │   │       │               │   │   │   ├── error.json
│   │   │       │               │   │   │   └── success.json
│   │   │       │               │   │   └── watched/
│   │   │       │               │   │       ├── error.json
│   │   │       │               │   │       └── success.json
│   │   │       │               │   ├── related/
│   │   │       │               │   │   ├── error.json
│   │   │       │               │   │   └── success.json
│   │   │       │               │   ├── trending/
│   │   │       │               │   │   ├── error.json
│   │   │       │               │   │   └── success.json
│   │   │       │               │   └── videos/
│   │   │       │               │       ├── error.json
│   │   │       │               │       └── success.json
│   │   │       │               ├── sync/
│   │   │       │               │   ├── error.json
│   │   │       │               │   ├── history/
│   │   │       │               │   │   ├── error.json
│   │   │       │               │   │   └── success.json
│   │   │       │               │   └── success.json
│   │   │       │               └── users/
│   │   │       │                   ├── lists/
│   │   │       │                   │   ├── create/
│   │   │       │                   │   │   ├── error.json
│   │   │       │                   │   │   └── success.json
│   │   │       │                   │   ├── error.json
│   │   │       │                   │   ├── items/
│   │   │       │                   │   │   ├── add/
│   │   │       │                   │   │   │   ├── error.json
│   │   │       │                   │   │   │   └── success.json
│   │   │       │                   │   │   └── remove/
│   │   │       │                   │   │       ├── error.json
│   │   │       │                   │   │       └── success.json
│   │   │       │                   │   └── success.json
│   │   │       │                   ├── me/
│   │   │       │                   │   ├── error.json
│   │   │       │                   │   └── success.json
│   │   │       │                   ├── stats/
│   │   │       │                   │   ├── error.json
│   │   │       │                   │   └── success.json
│   │   │       │                   └── watchlist/
│   │   │       │                       ├── error.json
│   │   │       │                       └── success.json
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── testing/
│   │   │       │                       └── di/
│   │   │       │                           ├── FakeAppConfigBindingContainer.kt
│   │   │       │                           └── TestScope.kt
│   │   │       ├── iosMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── testing/
│   │   │       │                       └── di/
│   │   │       │                           ├── FakeIosPlatformBindingContainer.kt
│   │   │       │                           ├── RunTestWithGraph.kt
│   │   │       │                           └── TestGraph.kt
│   │   │       ├── jvmAndIosMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── testing/
│   │   │       │                       └── di/
│   │   │       │                           └── FakeAppBindingContainer.kt
│   │   │       ├── jvmMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── testing/
│   │   │       │                       └── di/
│   │   │       │                           ├── RunTestWithGraph.kt
│   │   │       │                           ├── TestGraph.kt
│   │   │       │                           └── TestJvmPlatformBindingContainer.kt
│   │   │       └── jvmTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── testing/
│   │   │                               └── di/
│   │   │                                   └── TestJvmGraphTest.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── androidMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── testing/
│   │                                   └── integration/
│   │                                       └── ui/
│   │                                           ├── BaseRobot.kt
│   │                                           └── SystemDialogUtil.kt
│   ├── locale/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── locale/
│   │   │                               └── api/
│   │   │                                   ├── Language.kt
│   │   │                                   └── LocaleProvider.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── androidDeviceTest/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── locale/
│   │   │       │                       └── implementation/
│   │   │       │                           └── PlatformLocaleProviderAndroidTest.kt
│   │   │       ├── androidMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── locale/
│   │   │       │                       └── implementation/
│   │   │       │                           └── PlatformLocaleProvider.android.kt
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── locale/
│   │   │       │                       └── implementation/
│   │   │       │                           ├── DefaultLocaleProvider.kt
│   │   │       │                           └── PlatformLocaleProvider.kt
│   │   │       ├── commonTest/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── locale/
│   │   │       │                       └── implementation/
│   │   │       │                           └── PlatformLocaleProviderTest.kt
│   │   │       ├── iosMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── locale/
│   │   │       │                       └── implementation/
│   │   │       │                           └── PlatformLocaleProvider.ios.kt
│   │   │       ├── iosTest/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── locale/
│   │   │       │                       └── implementation/
│   │   │       │                           └── PlatformLocaleProviderIosTest.kt
│   │   │       ├── jvmMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── locale/
│   │   │       │                       └── implementation/
│   │   │       │                           └── PlatformLocaleProvider.jvm.kt
│   │   │       └── jvmTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── locale/
│   │   │                               └── implementation/
│   │   │                                   └── PlatformLocaleProviderJvmTest.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── locale/
│   │                                   └── testing/
│   │                                       └── FakeLocaleProvider.kt
│   ├── logger/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── core/
│   │   │       │                       └── logger/
│   │   │       │                           ├── CrashReporter.kt
│   │   │       │                           └── Logger.kt
│   │   │       └── iosMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── core/
│   │   │                               └── logger/
│   │   │                                   ├── CrashReportingBridge.kt
│   │   │                                   └── CrashReportingBridgeHolder.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── androidMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── core/
│   │   │       │                       └── logger/
│   │   │       │                           ├── AndroidCrashReporter.kt
│   │   │       │                           └── AndroidLoggerBindingContainer.kt
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── core/
│   │   │       │                       └── logger/
│   │   │       │                           ├── CompositeLogger.kt
│   │   │       │                           ├── FirebaseCrashLogger.kt
│   │   │       │                           ├── KermitLogger.kt
│   │   │       │                           └── LoggingInitializer.kt
│   │   │       └── iosMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── core/
│   │   │                               └── logger/
│   │   │                                   ├── IosCrashReporter.kt
│   │   │                                   ├── IosCrashReporterBindingContainer.kt
│   │   │                                   └── NoOpCrashReportingBridge.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── core/
│   │                                   └── logger/
│   │                                       └── fixture/
│   │                                           ├── FakeCrashReporter.kt
│   │                                           └── FakeLogger.kt
│   ├── network-util/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── core/
│   │   │       │                       └── networkutil/
│   │   │       │                           └── api/
│   │   │       │                               ├── ApiRateLimiter.kt
│   │   │       │                               ├── extensions/
│   │   │       │                               │   ├── ApiRateLimiterExtensions.kt
│   │   │       │                               │   ├── ApiResponseExtensions.kt
│   │   │       │                               │   ├── InternetConnectionPlugin.kt
│   │   │       │                               │   └── StoreExtensions.kt
│   │   │       │                               └── model/
│   │   │       │                                   ├── ApiExceptions.kt
│   │   │       │                                   ├── ApiResponse.kt
│   │   │       │                                   ├── AuthenticationException.kt
│   │   │       │                                   ├── HttpExceptions.kt
│   │   │       │                                   ├── NoInternetException.kt
│   │   │       │                                   ├── SyncError.kt
│   │   │       │                                   └── SyncException.kt
│   │   │       ├── commonTest/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── core/
│   │   │       │                       └── networkutil/
│   │   │       │                           └── api/
│   │   │       │                               └── model/
│   │   │       │                                   └── ThrowableToSyncErrorTest.kt
│   │   │       └── jvmTest/
│   │   │           ├── kotlin/
│   │   │           │   └── com/
│   │   │           │       └── thomaskioko/
│   │   │           │           └── tvmaniac/
│   │   │           │               └── core/
│   │   │           │                   └── networkutil/
│   │   │           │                       └── api/
│   │   │           │                           └── extensions/
│   │   │           │                               ├── ApiResponseExtensionsTest.kt
│   │   │           │                               ├── InternetConnectionPluginTest.kt
│   │   │           │                               └── TestResourceLoader.jvm.kt
│   │   │           └── resources/
│   │   │               ├── error_response.json
│   │   │               └── success_response.json
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── core/
│   │   │       │                       └── networkutil/
│   │   │       │                           └── ratelimit/
│   │   │       │                               └── AdaptiveApiRateLimiter.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── core/
│   │   │                               └── networkutil/
│   │   │                                   ├── model/
│   │   │                                   │   └── SyncErrorTest.kt
│   │   │                                   └── ratelimit/
│   │   │                                       └── AdaptiveApiRateLimiterTest.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── core/
│   │                                   └── networkutil/
│   │                                       └── testing/
│   │                                           └── FakeApiRateLimiter.kt
│   ├── notifications/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── androidMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── core/
│   │   │       │                       └── notifications/
│   │   │       │                           └── api/
│   │   │       │                               └── NotificationIconProvider.kt
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── core/
│   │   │                               └── notifications/
│   │   │                                   └── api/
│   │   │                                       ├── EpisodeNotification.kt
│   │   │                                       ├── NotificationChannel.kt
│   │   │                                       └── NotificationManager.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── androidMain/
│   │   │       │   ├── AndroidManifest.xml
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── core/
│   │   │       │                       └── notifications/
│   │   │       │                           └── implementation/
│   │   │       │                               ├── AndroidNotificationManager.kt
│   │   │       │                               ├── BootCompletedReceiver.kt
│   │   │       │                               ├── DebugNotificationManager.kt
│   │   │       │                               ├── EpisodeNotificationReceiver.kt
│   │   │       │                               ├── PendingNotificationsStore.kt
│   │   │       │                               └── model/
│   │   │       │                                   └── StoredNotification.kt
│   │   │       └── iosMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── core/
│   │   │                               └── notifications/
│   │   │                                   └── implementation/
│   │   │                                       └── IosNotificationManager.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── core/
│   │                                   └── notifications/
│   │                                       └── testing/
│   │                                           └── FakeNotificationManager.kt
│   ├── paging/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── core/
│   │                               └── paging/
│   │                                   ├── CommonPagingConfig.kt
│   │                                   ├── KeyedQueryPagingSource.kt
│   │                                   ├── OffsetQueryPagingSource.kt
│   │                                   ├── PaginatedRemoteMediator.kt
│   │                                   └── QueryPagingSource.kt
│   ├── screenshot-tests/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── main/
│   │           ├── AndroidManifest.xml
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── screenshottests/
│   │                               └── RoborazziScreenshotUtil.kt
│   ├── tasks/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── core/
│   │   │                               └── tasks/
│   │   │                                   └── api/
│   │   │                                       ├── BackgroundTaskScheduler.kt
│   │   │                                       ├── BackgroundWorker.kt
│   │   │                                       ├── PeriodicTaskRequest.kt
│   │   │                                       ├── TaskConstraints.kt
│   │   │                                       ├── WorkerFactory.kt
│   │   │                                       └── WorkerResult.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── androidMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── core/
│   │   │       │                       └── tasks/
│   │   │       │                           └── implementation/
│   │   │       │                               ├── AndroidTaskScheduler.kt
│   │   │       │                               ├── SchedulerDispatchWorker.kt
│   │   │       │                               └── di/
│   │   │       │                                   └── WorkManagerBindingContainer.kt
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── core/
│   │   │       │                       └── tasks/
│   │   │       │                           └── implementation/
│   │   │       │                               └── DefaultWorkerFactory.kt
│   │   │       └── iosMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── core/
│   │   │                               └── tasks/
│   │   │                                   └── implementation/
│   │   │                                       └── IosTaskScheduler.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── core/
│   │                                   └── tasks/
│   │                                       └── testing/
│   │                                           └── FakeBackgroundTaskScheduler.kt
│   ├── test-tags/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── testtags/
│   │                               ├── calendar/
│   │                               │   └── CalendarTestTags.kt
│   │                               ├── component/
│   │                               │   └── DesignComponentTestTags.kt
│   │                               ├── discover/
│   │                               │   └── DiscoverTestTags.kt
│   │                               ├── episodesheet/
│   │                               │   └── EpisodeSheetTestTags.kt
│   │                               ├── home/
│   │                               │   └── HomeTestTags.kt
│   │                               ├── library/
│   │                               │   └── LibraryTestTags.kt
│   │                               ├── moreshows/
│   │                               │   └── MoreShowsTestTags.kt
│   │                               ├── notifications/
│   │                               │   └── NotificationRationaleTestTags.kt
│   │                               ├── profile/
│   │                               │   └── ProfileTestTags.kt
│   │                               ├── progress/
│   │                               │   └── ProgressTestTags.kt
│   │                               ├── search/
│   │                               │   └── SearchTestTags.kt
│   │                               ├── seasondetails/
│   │                               │   └── SeasonDetailsTestTags.kt
│   │                               ├── settings/
│   │                               │   └── SettingsTestTags.kt
│   │                               ├── showdetails/
│   │                               │   └── ShowDetailsTestTags.kt
│   │                               └── upnext/
│   │                                   └── UpNextTestTags.kt
│   ├── util/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── util/
│   │   │                               └── api/
│   │   │                                   ├── AppUtils.kt
│   │   │                                   ├── DateTimeProvider.kt
│   │   │                                   ├── FormatterUtil.kt
│   │   │                                   └── ItemSyncer.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── androidMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── util/
│   │   │       │                       ├── AndroidAppUtils.kt
│   │   │       │                       └── AndroidFormatterUtil.kt
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── util/
│   │   │       │                       ├── DateTimeBindingContainer.kt
│   │   │       │                       └── DefaultDateTimeProvider.kt
│   │   │       ├── commonTest/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── util/
│   │   │       │                       └── DefaultDateTimeProviderTest.kt
│   │   │       ├── iosMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── util/
│   │   │       │                       ├── IosAppUtils.kt
│   │   │       │                       └── IosFormatterUtil.kt
│   │   │       ├── iosTest/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── util/
│   │   │       │                       └── IosFormatterUtilTest.kt
│   │   │       └── test/
│   │   │           └── java/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── util/
│   │   │                               └── AndroidFormatterUtilTest.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           ├── commonMain/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── util/
│   │           │                       └── testing/
│   │           │                           ├── FakeApplicationInfo.kt
│   │           │                           ├── FakeDateTimeProvider.kt
│   │           │                           ├── FakeFormatterUtil.kt
│   │           │                           └── FlakyTests.kt
│   │           └── jvmAndroidMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── util/
│   │                                   └── testing/
│   │                                       └── FlakyTestRule.kt
│   └── view/
│       ├── build.gradle.kts
│       └── src/
│           └── commonMain/
│               └── kotlin/
│                   └── com/
│                       └── thomaskioko/
│                           └── tvmaniac/
│                               └── core/
│                                   └── view/
│                                       ├── ErrorToStringMapper.kt
│                                       ├── InvokeStatus.kt
│                                       ├── ObservableLoadingCounter.kt
│                                       └── UiMessage.kt
├── data/
│   ├── calendar/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── calendar/
│   │   │                                   ├── CalendarDao.kt
│   │   │                                   ├── CalendarEntry.kt
│   │   │                                   └── CalendarRepository.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── calendar/
│   │   │                                   └── implementation/
│   │   │                                       ├── CalendarStore.kt
│   │   │                                       ├── DefaultCalendarDao.kt
│   │   │                                       └── DefaultCalendarRepository.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── data/
│   │                                   └── calendar/
│   │                                       └── testing/
│   │                                           └── FakeCalendarRepository.kt
│   ├── cast/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── cast/
│   │   │                                   └── api/
│   │   │                                       ├── CastDao.kt
│   │   │                                       └── CastRepository.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── cast/
│   │   │                                   └── implementation/
│   │   │                                       ├── DefaultCastDao.kt
│   │   │                                       ├── DefaultCastRepository.kt
│   │   │                                       ├── ShowCastResult.kt
│   │   │                                       └── ShowCastStore.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── data/
│   │                                   └── cast/
│   │                                       └── testing/
│   │                                           └── FakeCastRepository.kt
│   ├── database/
│   │   ├── sqldelight/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── androidMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── db/
│   │   │       │                       └── DatabasePlatformBindingContainer.kt
│   │   │       ├── commonMain/
│   │   │       │   └── sqldelight/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   ├── db/
│   │   │       │                   │   ├── Calendar.sq
│   │   │       │                   │   ├── Cast.sq
│   │   │       │                   │   ├── EpisodeImage.sq
│   │   │       │                   │   ├── Episodes.sq
│   │   │       │                   │   ├── FeaturedShows.sq
│   │   │       │                   │   ├── FollowedShows.sq
│   │   │       │                   │   ├── GenreShows.sq
│   │   │       │                   │   ├── Genres.sq
│   │   │       │                   │   ├── LastRequests.sq
│   │   │       │                   │   ├── Library.sq
│   │   │       │                   │   ├── NextEpisodes.sq
│   │   │       │                   │   ├── PopularShows.sq
│   │   │       │                   │   ├── RecommendedShows.sq
│   │   │       │                   │   ├── SeasonImages.sq
│   │   │       │                   │   ├── SeasonVideos.sq
│   │   │       │                   │   ├── Seasons.sq
│   │   │       │                   │   ├── ShowGenres.sq
│   │   │       │                   │   ├── ShowMetadata.sq
│   │   │       │                   │   ├── ShowsLastWatched.sq
│   │   │       │                   │   ├── ShowsNextToWatch.sq
│   │   │       │                   │   ├── SimilarShows.sq
│   │   │       │                   │   ├── Stats.sq
│   │   │       │                   │   ├── TopratedShows.sq
│   │   │       │                   │   ├── Trailers.sq
│   │   │       │                   │   ├── TraktGenres.sq
│   │   │       │                   │   ├── TraktLastActivity.sq
│   │   │       │                   │   ├── TraktListShows.sq
│   │   │       │                   │   ├── TraktLists.sq
│   │   │       │                   │   ├── TrendingShows.sq
│   │   │       │                   │   ├── TvShow.sq
│   │   │       │                   │   ├── UpcomingShows.sq
│   │   │       │                   │   ├── User.sq
│   │   │       │                   │   ├── WatchProviders.sq
│   │   │       │                   │   └── WatchedEpisodes.sq
│   │   │       │                   └── migrations/
│   │   │       │                       ├── 1.sqm
│   │   │       │                       ├── 10.sqm
│   │   │       │                       ├── 11.sqm
│   │   │       │                       ├── 12.sqm
│   │   │       │                       ├── 13.sqm
│   │   │       │                       ├── 14.sqm
│   │   │       │                       ├── 15.sqm
│   │   │       │                       ├── 16.sqm
│   │   │       │                       ├── 17.sqm
│   │   │       │                       ├── 18.sqm
│   │   │       │                       ├── 19.sqm
│   │   │       │                       ├── 2.sqm
│   │   │       │                       ├── 20.sqm
│   │   │       │                       ├── 21.sqm
│   │   │       │                       ├── 22.sqm
│   │   │       │                       ├── 23.sqm
│   │   │       │                       ├── 3.sqm
│   │   │       │                       ├── 4.sqm
│   │   │       │                       ├── 5.sqm
│   │   │       │                       ├── 6.sqm
│   │   │       │                       ├── 7.sqm
│   │   │       │                       ├── 8.sqm
│   │   │       │                       └── 9.sqm
│   │   │       ├── iosMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── db/
│   │   │       │                       └── DatabasePlatformBindingContainer.kt
│   │   │       └── jvmTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── db/
│   │   │                               ├── Migration22TraktListShowsTest.kt
│   │   │                               ├── Migration23DropParentFkTest.kt
│   │   │                               ├── SchemaCreateTest.kt
│   │   │                               └── util/
│   │   │                                   └── MigrationTestUtil.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           ├── androidMain/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── database/
│   │           │                       └── test/
│   │           │                           └── BaseDatabaseTest.android.kt
│   │           ├── commonMain/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── database/
│   │           │                       └── test/
│   │           │                           └── BaseDatabaseTest.kt
│   │           ├── iosMain/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── database/
│   │           │                       └── test/
│   │           │                           └── BaseDatabaseTest.ios.kt
│   │           └── jvmMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── database/
│   │                                   └── test/
│   │                                       └── BaseDatabaseTest.jvm.kt
│   ├── datastore/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── datastore/
│   │   │                               └── api/
│   │   │                                   ├── AppTheme.kt
│   │   │                                   ├── DatastoreRepository.kt
│   │   │                                   ├── ImageQuality.kt
│   │   │                                   └── ListStyle.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   ├── src/
│   │   │   │   ├── androidMain/
│   │   │   │   │   └── kotlin/
│   │   │   │   │       └── com/
│   │   │   │   │           └── thomaskioko/
│   │   │   │   │               └── tvmaniac/
│   │   │   │   │                   └── datastore/
│   │   │   │   │                       └── implementation/
│   │   │   │   │                           └── DataStorePlatformBindingContainer.kt
│   │   │   │   ├── commonMain/
│   │   │   │   │   └── kotlin/
│   │   │   │   │       └── com/
│   │   │   │   │           └── thomaskioko/
│   │   │   │   │               └── tvmaniac/
│   │   │   │   │                   └── datastore/
│   │   │   │   │                       └── implementation/
│   │   │   │   │                           ├── DataStoreHelper.kt
│   │   │   │   │                           └── DefaultDatastoreRepository.kt
│   │   │   │   ├── commonTest/
│   │   │   │   │   └── kotlin/
│   │   │   │   │       └── com/
│   │   │   │   │           └── thomaskioko/
│   │   │   │   │               └── tvmaniac/
│   │   │   │   │                   └── datastore/
│   │   │   │   │                       └── implemetation/
│   │   │   │   │                           └── DatastoreRepositoryTest.kt
│   │   │   │   └── iosMain/
│   │   │   │       └── kotlin/
│   │   │   │           └── com/
│   │   │   │               └── thomaskioko/
│   │   │   │                   └── tvmaniac/
│   │   │   │                       └── datastore/
│   │   │   │                           └── implementation/
│   │   │   │                               └── DataStorePlatformBindingContainer.kt
│   │   │   └── test.preferences_pb
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── datastore/
│   │                                   └── testing/
│   │                                       └── FakeDatastoreRepository.kt
│   ├── episode/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── episodes/
│   │   │                               └── api/
│   │   │                                   ├── EpisodeRepository.kt
│   │   │                                   ├── EpisodeWatchesDataSource.kt
│   │   │                                   ├── EpisodesDao.kt
│   │   │                                   ├── NextEpisodeDao.kt
│   │   │                                   ├── WatchedEpisodeDao.kt
│   │   │                                   ├── WatchedEpisodeEntry.kt
│   │   │                                   ├── WatchedEpisodeSyncRepository.kt
│   │   │                                   └── model/
│   │   │                                       ├── EpisodeExtensions.kt
│   │   │                                       ├── EpisodeWatchParams.kt
│   │   │                                       ├── LastWatchedEpisode.kt
│   │   │                                       ├── SeasonWatchProgress.kt
│   │   │                                       ├── ShowWatchProgress.kt
│   │   │                                       ├── UnwatchedEpisode.kt
│   │   │                                       ├── UpcomingEpisode.kt
│   │   │                                       ├── WatchProgress.kt
│   │   │                                       └── WatchedEpisode.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── episodes/
│   │   │       │                       └── implementation/
│   │   │       │                           ├── DefaultEpisodeRepository.kt
│   │   │       │                           ├── DefaultWatchedEpisodeSyncRepository.kt
│   │   │       │                           ├── EpisodeWatchesLastRequestStore.kt
│   │   │       │                           ├── TraktEpisodeWatchesDataSource.kt
│   │   │       │                           ├── UpcomingEpisodesStore.kt
│   │   │       │                           ├── dao/
│   │   │       │                           │   ├── DefaultEpisodesDao.kt
│   │   │       │                           │   ├── DefaultNextEpisodeDao.kt
│   │   │       │                           │   └── DefaultWatchedEpisodeDao.kt
│   │   │       │                           └── model/
│   │   │       │                               └── NextEpisodeKey.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── episodes/
│   │   │                               └── implementation/
│   │   │                                   ├── DefaultEpisodeRepositoryTest.kt
│   │   │                                   ├── DefaultEpisodesDaoTest.kt
│   │   │                                   ├── DefaultNextEpisodeDaoTest.kt
│   │   │                                   ├── DefaultWatchedEpisodeDaoTest.kt
│   │   │                                   ├── EpisodesCacheTest.kt
│   │   │                                   └── MockData.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── episodes/
│   │                                   └── testing/
│   │                                       ├── FakeEpisodeRepository.kt
│   │                                       ├── FakeEpisodeWatchesDataSource.kt
│   │                                       └── FakeWatchedEpisodeSyncRepository.kt
│   ├── featuredshows/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── featuredshows/
│   │   │                                   └── api/
│   │   │                                       ├── FeaturedShowsDao.kt
│   │   │                                       ├── FeaturedShowsRepository.kt
│   │   │                                       └── interactor/
│   │   │                                           └── FeaturedShowsInteractor.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── data/
│   │   │       │                       └── featuredshows/
│   │   │       │                           └── implementation/
│   │   │       │                               ├── DefaultFeaturedShowsDao.kt
│   │   │       │                               ├── DefaultFeaturedShowsRepository.kt
│   │   │       │                               └── FeaturedShowsStore.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── featuredshows/
│   │   │                                   └── implementation/
│   │   │                                       └── DefaultFeaturedShowsDaoTest.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── data/
│   │                                   └── featuredshows/
│   │                                       └── testing/
│   │                                           └── FakeFeaturedShowsRepository.kt
│   ├── followedshows/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── followedshows/
│   │   │                               └── api/
│   │   │                                   ├── FollowedShowEntry.kt
│   │   │                                   ├── FollowedShowsDao.kt
│   │   │                                   ├── FollowedShowsRepository.kt
│   │   │                                   └── PendingAction.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── followedshows/
│   │   │       │                       └── implementation/
│   │   │       │                           ├── DefaultFollowedShowsDao.kt
│   │   │       │                           └── DefaultFollowedShowsRepository.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── followedshows/
│   │   │                               └── implementation/
│   │   │                                   ├── DefaultFollowedShowsDaoTest.kt
│   │   │                                   └── DefaultFollowedShowsRepositoryTest.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── followedshows/
│   │                                   └── testing/
│   │                                       ├── FakeFollowedShowsDao.kt
│   │                                       └── FakeFollowedShowsRepository.kt
│   ├── genre/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── genre/
│   │   │                               ├── GenreDao.kt
│   │   │                               ├── GenreRepository.kt
│   │   │                               ├── ShowGenresEntity.kt
│   │   │                               ├── TraktGenreDao.kt
│   │   │                               └── model/
│   │   │                                   ├── GenreShowCategory.kt
│   │   │                                   ├── GenreShowsStoreKey.kt
│   │   │                                   ├── GenreWithShowsEntity.kt
│   │   │                                   └── TraktGenreEntity.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── genre/
│   │   │                               ├── DefaultGenreDao.kt
│   │   │                               ├── DefaultGenreRepository.kt
│   │   │                               ├── DefaultTraktGenreDao.kt
│   │   │                               ├── GenrePosterStore.kt
│   │   │                               ├── GenreShowsStore.kt
│   │   │                               ├── GenreStore.kt
│   │   │                               ├── ShowsByGenreIdStore.kt
│   │   │                               └── TraktGenresStore.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── genre/
│   │                                   └── FakeGenreRepository.kt
│   ├── library/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── library/
│   │   │                                   ├── LibraryDao.kt
│   │   │                                   ├── LibraryRepository.kt
│   │   │                                   └── model/
│   │   │                                       ├── LibraryItem.kt
│   │   │                                       ├── LibrarySortOption.kt
│   │   │                                       └── WatchProvider.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── library/
│   │   │                                   └── implementation/
│   │   │                                       ├── DefaultLibraryDao.kt
│   │   │                                       ├── DefaultLibraryRepository.kt
│   │   │                                       └── LibraryStore.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── data/
│   │                                   └── library/
│   │                                       └── testing/
│   │                                           └── FakeLibraryRepository.kt
│   ├── popularshows/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── popularshows/
│   │   │                                   └── api/
│   │   │                                       ├── PopularShowsDao.kt
│   │   │                                       ├── PopularShowsInteractor.kt
│   │   │                                       └── PopularShowsRepository.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── data/
│   │   │       │                       └── popularshows/
│   │   │       │                           └── implementation/
│   │   │       │                               ├── DefaultPopularShowsDao.kt
│   │   │       │                               ├── DefaultPopularShowsRepository.kt
│   │   │       │                               └── PopularShowsStore.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── popularshows/
│   │   │                                   └── implementation/
│   │   │                                       └── DefaultPopularShowsDaoTest.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── data/
│   │                                   └── popularshows/
│   │                                       └── testing/
│   │                                           └── FakePopularShowsRepository.kt
│   ├── recommendedshows/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── recommendedshows/
│   │   │                                   └── api/
│   │   │                                       ├── RecommendedShowsDao.kt
│   │   │                                       ├── RecommendedShowsParams.kt
│   │   │                                       └── RecommendedShowsRepository.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── recommendedshows/
│   │   │                                   └── implementation/
│   │   │                                       ├── DefaultRecommendedShowsDao.kt
│   │   │                                       ├── DefaultRecommendedShowsRepository.kt
│   │   │                                       ├── RecommendedShowResult.kt
│   │   │                                       └── RecommendedShowsStore.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── data/
│   │                                   └── recommendedshows/
│   │                                       └── testing/
│   │                                           └── FakeRecommendedShowsRepository.kt
│   ├── request-manager/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── resourcemanager/
│   │   │                               └── api/
│   │   │                                   ├── RequestManagerRepository.kt
│   │   │                                   └── RequestTypeConfig.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── resourcemanager/
│   │   │       │                       └── implementation/
│   │   │       │                           └── DefaultRequestManagerRepository.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── resourcemanager/
│   │   │                               └── implementation/
│   │   │                                   ├── CacheValidationTest.kt
│   │   │                                   └── DefaultRequestManagerRepositoryTest.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── requestmanager/
│   │                                   └── testing/
│   │                                       └── FakeRequestManagerRepository.kt
│   ├── search/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── search/
│   │   │                               └── api/
│   │   │                                   └── SearchRepository.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── search/
│   │   │                               └── implementation/
│   │   │                                   ├── DefaultSearchRepository.kt
│   │   │                                   ├── SearchShowResult.kt
│   │   │                                   └── SearchShowStore.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── search/
│   │                                   └── testing/
│   │                                       └── FakeSearchRepository.kt
│   ├── seasondetails/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── seasondetails/
│   │   │                               └── api/
│   │   │                                   ├── SeasonDetailsDao.kt
│   │   │                                   ├── SeasonDetailsParam.kt
│   │   │                                   ├── SeasonDetailsRepository.kt
│   │   │                                   └── model/
│   │   │                                       ├── ContinueTrackingResult.kt
│   │   │                                       ├── EpisodeDetails.kt
│   │   │                                       └── SeasonDetailsWithEpisodes.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── seasondetails/
│   │   │                               └── implementation/
│   │   │                                   ├── DefaultSeasonDetailsDao.kt
│   │   │                                   ├── DefaultSeasonDetailsRepository.kt
│   │   │                                   ├── SeasonDetailsResponse.kt
│   │   │                                   └── SeasonDetailsStore.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── seasondetails/
│   │                                   └── testing/
│   │                                       └── FakeSeasonDetailsRepository.kt
│   ├── seasons/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── seasons/
│   │   │                               └── api/
│   │   │                                   ├── FollowedShowSeason.kt
│   │   │                                   ├── SeasonsDao.kt
│   │   │                                   ├── SeasonsEpisodesSyncRepository.kt
│   │   │                                   └── SeasonsRepository.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── seasons/
│   │   │                               └── implementation/
│   │   │                                   ├── DefaultSeasonsDao.kt
│   │   │                                   ├── DefaultSeasonsEpisodesSyncRepository.kt
│   │   │                                   ├── DefaultSeasonsRepository.kt
│   │   │                                   └── SeasonsWithEpisodesStore.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── seasons/
│   │                                   └── testing/
│   │                                       └── FakeSeasonsRepository.kt
│   ├── showdetails/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── showdetails/
│   │   │                                   └── api/
│   │   │                                       ├── ShowDetailsDao.kt
│   │   │                                       └── ShowDetailsRepository.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── showdetails/
│   │   │                                   └── implementation/
│   │   │                                       ├── DefaultShowDetailsDao.kt
│   │   │                                       ├── DefaultShowDetailsRepository.kt
│   │   │                                       ├── ShowDetailsResponse.kt
│   │   │                                       └── ShowDetailsStore.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── data/
│   │                                   └── showdetails/
│   │                                       └── testing/
│   │                                           └── FakeShowDetailsRepository.kt
│   ├── shows/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── shows/
│   │   │       │                       └── api/
│   │   │       │                           ├── MergeShowUtil.kt
│   │   │       │                           ├── TvShowsDao.kt
│   │   │       │                           └── model/
│   │   │       │                               ├── Category.kt
│   │   │       │                               ├── ShowDefaults.kt
│   │   │       │                               └── ShowEntity.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── shows/
│   │   │                               └── api/
│   │   │                                   ├── MockData.kt
│   │   │                                   └── TvShowCacheTest.kt
│   │   └── implementation/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── shows/
│   │                                   └── implementation/
│   │                                       └── DefaultTvShowsDao.kt
│   ├── similar/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── similar/
│   │   │                               └── api/
│   │   │                                   ├── SimilarShowsDao.kt
│   │   │                                   └── SimilarShowsRepository.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── similar/
│   │   │                               └── implementation/
│   │   │                                   ├── DefaultSimilarShowsDao.kt
│   │   │                                   ├── DefaultSimilarShowsRepository.kt
│   │   │                                   ├── SimilarParams.kt
│   │   │                                   ├── SimilarShowResult.kt
│   │   │                                   └── SimilarShowStore.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── similar/
│   │                                   └── testing/
│   │                                       └── FakeSimilarShowsRepository.kt
│   ├── sync-activity/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── syncactivity/
│   │   │                               └── api/
│   │   │                                   ├── TraktActivityDao.kt
│   │   │                                   ├── TraktActivityRepository.kt
│   │   │                                   └── model/
│   │   │                                       ├── ActivityType.kt
│   │   │                                       └── TraktLastActivity.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── syncactivity/
│   │   │       │                       └── implementation/
│   │   │       │                           ├── DefaultTraktActivityDao.kt
│   │   │       │                           ├── DefaultTraktActivityRepository.kt
│   │   │       │                           └── TraktActivityStore.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── syncactivity/
│   │   │                               └── implementation/
│   │   │                                   └── DefaultTraktActivityDaoTest.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── syncactivity/
│   │                                   └── testing/
│   │                                       ├── FakeTraktActivityDao.kt
│   │                                       └── FakeTraktActivityRepository.kt
│   ├── topratedshows/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── topratedshows/
│   │   │                               └── data/
│   │   │                                   └── api/
│   │   │                                       ├── TopRatedShowsDao.kt
│   │   │                                       ├── TopRatedShowsInteractor.kt
│   │   │                                       └── TopRatedShowsRepository.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── toprated/
│   │   │       │                       └── data/
│   │   │       │                           └── implementation/
│   │   │       │                               ├── DefaultTopRatedShowsDao.kt
│   │   │       │                               ├── DefaultTopRatedShowsRepository.kt
│   │   │       │                               ├── TopRatedShowWithImages.kt
│   │   │       │                               └── TopRatedShowsStore.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── toprated/
│   │   │                               └── data/
│   │   │                                   └── implementation/
│   │   │                                       └── DefaultTopRatedShowsDaoTest.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── data/
│   │                                   └── topratedshows/
│   │                                       └── testing/
│   │                                           └── FakeTopRatedShowsRepository.kt
│   ├── trailers/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com.thomaskioko.tvmaniac.data.trailers.implementation/
│   │   │                   ├── TrailerDao.kt
│   │   │                   └── TrailerRepository.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── trailers/
│   │   │                                   └── implementation/
│   │   │                                       ├── DefaultTrailerDao.kt
│   │   │                                       ├── DefaultTrailerRepository.kt
│   │   │                                       └── TrailerStore.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── trailers/
│   │                                   └── testing/
│   │                                       ├── FakeTrailerRepository.kt
│   │                                       └── MockData.kt
│   ├── traktauth/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── traktauth/
│   │   │                               └── api/
│   │   │                                   ├── AuthError.kt
│   │   │                                   ├── AuthState.kt
│   │   │                                   ├── AuthStore.kt
│   │   │                                   ├── RefreshTokenResult.kt
│   │   │                                   ├── TokenRefreshResult.kt
│   │   │                                   ├── TraktAuthManager.kt
│   │   │                                   ├── TraktAuthRepository.kt
│   │   │                                   ├── TraktAuthState.kt
│   │   │                                   └── TraktRefreshTokenAction.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── androidMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── traktauth/
│   │   │       │                       └── implementation/
│   │   │       │                           ├── AndroidAuthStore.kt
│   │   │       │                           ├── AndroidTraktAuthManager.kt
│   │   │       │                           ├── TraktActivityResultContract.kt
│   │   │       │                           └── di/
│   │   │       │                               └── TraktAuthAndroidBindingContainer.kt
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── traktauth/
│   │   │       │                       └── implementation/
│   │   │       │                           ├── DefaultTraktAuthRepository.kt
│   │   │       │                           ├── DefaultTraktRefreshTokenAction.kt
│   │   │       │                           ├── TokenRefreshInitializer.kt
│   │   │       │                           ├── TokenRefreshWorker.kt
│   │   │       │                           └── di/
│   │   │       │                               └── TokenRefreshInitializerBindingContainer.kt
│   │   │       └── iosMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── traktauth/
│   │   │                               └── implementation/
│   │   │                                   ├── DefaultIOSTraktAuthManager.kt
│   │   │                                   └── IosAuthStore.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── traktauth/
│   │                                   └── testing/
│   │                                       ├── FakeAuthStore.kt
│   │                                       ├── FakeTraktAuthManager.kt
│   │                                       ├── FakeTraktAuthRepository.kt
│   │                                       └── FakeTraktRefreshTokenAction.kt
│   ├── traktlists/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── traktlists/
│   │   │                               └── api/
│   │   │                                   ├── TraktList.kt
│   │   │                                   ├── TraktListDao.kt
│   │   │                                   ├── TraktListEntity.kt
│   │   │                                   ├── TraktListRepository.kt
│   │   │                                   ├── TraktListShowDao.kt
│   │   │                                   └── TraktListShowEntry.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── traktlists/
│   │   │                               └── implementation/
│   │   │                                   ├── CreateTraktListStore.kt
│   │   │                                   ├── DefaultTraktListDao.kt
│   │   │                                   ├── DefaultTraktListRepository.kt
│   │   │                                   ├── DefaultTraktListShowDao.kt
│   │   │                                   └── TraktListsStore.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── traktlists/
│   │                                   └── testing/
│   │                                       └── FakeTraktListRepository.kt
│   ├── trendingshows/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── discover/
│   │   │                               └── api/
│   │   │                                   ├── TrendingShowsDao.kt
│   │   │                                   ├── TrendingShowsInteractor.kt
│   │   │                                   ├── TrendingShowsParams.kt
│   │   │                                   └── TrendingShowsRepository.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── discover/
│   │   │       │                       └── implementation/
│   │   │       │                           ├── DefaultTrendingShowsDao.kt
│   │   │       │                           ├── DefaultTrendingShowsRepository.kt
│   │   │       │                           ├── TrendingShowWithImages.kt
│   │   │       │                           └── TrendingShowsStore.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── discover/
│   │   │                               └── implementation/
│   │   │                                   └── DefaultTrendingShowsDaoTest.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── data/
│   │                                   └── trendingshows/
│   │                                       └── testing/
│   │                                           └── FakeTrendingShowsRepository.kt
│   ├── upcomingshows/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── upcomingshows/
│   │   │                                   └── api/
│   │   │                                       ├── UpcomingShowsDao.kt
│   │   │                                       ├── UpcomingShowsInteractor.kt
│   │   │                                       └── UpcomingShowsRepository.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── data/
│   │   │       │                       └── upcomingshows/
│   │   │       │                           └── implementation/
│   │   │       │                               ├── DefaultUpcomingShowsDao.kt
│   │   │       │                               ├── DefaultUpcomingShowsRepository.kt
│   │   │       │                               ├── UpcomingShowsStore.kt
│   │   │       │                               └── model/
│   │   │       │                                   ├── UpcomingParams.kt
│   │   │       │                                   └── UpcomingShowResult.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── upcomingshows/
│   │   │                                   └── implementation/
│   │   │                                       └── DefaultUpcomingShowsDaoTest.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── data/
│   │                                   └── upcomingshows/
│   │                                       └── testing/
│   │                                           └── FakeUpcomingShowsRepository.kt
│   ├── upnext/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── upnext/
│   │   │                               └── api/
│   │   │                                   ├── UpNextDao.kt
│   │   │                                   ├── UpNextRepository.kt
│   │   │                                   └── model/
│   │   │                                       └── NextEpisodeWithShow.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── upnext/
│   │   │       │                       └── implementation/
│   │   │       │                           ├── DefaultUpNextDao.kt
│   │   │       │                           ├── DefaultUpNextRepository.kt
│   │   │       │                           └── ShowUpNextStore.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── upnext/
│   │   │                               └── implementation/
│   │   │                                   ├── DefaultUpNextDaoTest.kt
│   │   │                                   └── DefaultUpNextRepositoryTest.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── upnext/
│   │                                   └── testing/
│   │                                       ├── FakeUpNextDao.kt
│   │                                       └── FakeUpNextRepository.kt
│   ├── user/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── user/
│   │   │                                   └── api/
│   │   │                                       ├── UserDao.kt
│   │   │                                       ├── UserRepository.kt
│   │   │                                       ├── UserStatsDao.kt
│   │   │                                       └── model/
│   │   │                                           ├── UserProfile.kt
│   │   │                                           ├── UserProfileStats.kt
│   │   │                                           └── UserWatchTime.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── data/
│   │   │       │                       └── user/
│   │   │       │                           └── implementation/
│   │   │       │                               ├── DefaultUserDao.kt
│   │   │       │                               ├── DefaultUserRepository.kt
│   │   │       │                               ├── DefaultUserStatsDao.kt
│   │   │       │                               ├── UserStatsStore.kt
│   │   │       │                               └── UserStore.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── user/
│   │   │                                   └── implementation/
│   │   │                                       ├── DefaultUserDaoTest.kt
│   │   │                                       └── DefaultUserStatsDaoTest.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── data/
│   │                                   └── user/
│   │                                       └── testing/
│   │                                           └── FakeUserRepository.kt
│   ├── watchlist/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── shows/
│   │   │                               └── api/
│   │   │                                   ├── WatchlistDao.kt
│   │   │                                   └── WatchlistRepository.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── watchlist/
│   │   │                               └── implementation/
│   │   │                                   ├── DefaultWatchlistDao.kt
│   │   │                                   └── DefaultWatchlistRepository.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── watchlist/
│   │                                   └── testing/
│   │                                       └── FakeWatchlistRepository.kt
│   └── watchproviders/
│       ├── api/
│       │   ├── build.gradle.kts
│       │   └── src/
│       │       └── commonMain/
│       │           └── kotlin/
│       │               └── com/
│       │                   └── thomaskioko/
│       │                       └── tvmaniac/
│       │                           └── data/
│       │                               └── watchproviders/
│       │                                   └── api/
│       │                                       ├── WatchProviderDao.kt
│       │                                       └── WatchProviderRepository.kt
│       ├── implementation/
│       │   ├── build.gradle.kts
│       │   └── src/
│       │       └── commonMain/
│       │           └── kotlin/
│       │               └── com/
│       │                   └── thomaskioko/
│       │                       └── tvmaniac/
│       │                           └── data/
│       │                               └── watchproviders/
│       │                                   └── implementation/
│       │                                       ├── DefaultWatchProviderDao.kt
│       │                                       ├── DefaultWatchProviderRepository.kt
│       │                                       └── WatchProvidersStore.kt
│       └── testing/
│           ├── build.gradle.kts
│           └── src/
│               └── commonMain/
│                   └── kotlin/
│                       └── com/
│                           └── thomaskioko/
│                               └── tvmaniac/
│                                   └── data/
│                                       └── watchproviders/
│                                           └── testing/
│                                               └── FakeWatchProviderRepository.kt
├── docs/
│   ├── architecture/
│   │   ├── README.md
│   │   ├── data-layer.md
│   │   ├── dependency-injection.md
│   │   ├── integration-testing.md
│   │   ├── journey-tests.md
│   │   ├── modularization.md
│   │   ├── navigation-codegen.md
│   │   ├── navigation.md
│   │   ├── presentation-layer.md
│   │   └── scopes.md
│   ├── privacy_policy.md
│   ├── setup.md
│   └── terms_conditions.md
├── domain/
│   ├── calendar/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       ├── commonMain/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── domain/
│   │       │                       └── calendar/
│   │       │                           ├── CalendarEpisodeFormatter.kt
│   │       │                           ├── CalendarWeekCalculator.kt
│   │       │                           ├── FetchCalendarInteractor.kt
│   │       │                           ├── ObserveCalendarInteractor.kt
│   │       │                           └── model/
│   │       │                               ├── DateLabel.kt
│   │       │                               ├── GroupedCalendarEntry.kt
│   │       │                               └── GroupedEpisodeEntry.kt
│   │       └── commonTest/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── calendar/
│   │                                   ├── CalendarWeekCalculatorTest.kt
│   │                                   └── ObserveCalendarInteractorTest.kt
│   ├── discover/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       ├── commonMain/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── domain/
│   │       │                       └── discover/
│   │       │                           └── DiscoverShowsInteractor.kt
│   │       └── commonTest/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── discover/
│   │                                   └── DiscoverShowsInteractorTest.kt
│   ├── episode/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       ├── commonMain/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── domain/
│   │       │                       └── episode/
│   │       │                           ├── MarkEpisodeUnwatchedInteractor.kt
│   │       │                           ├── MarkEpisodeWatchedInteractor.kt
│   │       │                           ├── ObserveEpisodeByIdInteractor.kt
│   │       │                           └── ObserveShowWatchProgressInteractor.kt
│   │       └── commonTest/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── episode/
│   │                                   └── MarkEpisodeWatchedInteractorTest.kt
│   ├── followedshows/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── followedshows/
│   │                                   └── UnfollowShowInteractor.kt
│   ├── genre/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── genre/
│   │                                   ├── FetchGenreContentInteractor.kt
│   │                                   └── GenreShowsInteractor.kt
│   ├── library/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── library/
│   │                                   ├── LibrarySyncWorker.kt
│   │                                   ├── ObserveLibraryInteractor.kt
│   │                                   ├── SyncLibraryInteractor.kt
│   │                                   ├── SyncTasksInitializer.kt
│   │                                   └── di/
│   │                                       └── SyncTasksInitializerBindingContainer.kt
│   ├── logout/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── logout/
│   │                                   └── LogoutInteractor.kt
│   ├── notifications/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       ├── commonMain/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── domain/
│   │       │                       └── notifications/
│   │       │                           ├── EpisodeNotificationWorker.kt
│   │       │                           ├── NotificationTasksInitializer.kt
│   │       │                           ├── di/
│   │       │                           │   └── NotificationTasksInitializerBindingContainer.kt
│   │       │                           └── interactor/
│   │       │                               ├── RefreshUpcomingSeasonDetailsInteractor.kt
│   │       │                               ├── ScheduleDebugEpisodeNotificationInteractor.kt
│   │       │                               ├── ScheduleEpisodeNotificationsInteractor.kt
│   │       │                               ├── SyncTraktCalendarInteractor.kt
│   │       │                               └── ToggleEpisodeNotificationsInteractor.kt
│   │       └── commonTest/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── notifications/
│   │                                   └── interactor/
│   │                                       └── ScheduleEpisodeNotificationsInteractorTest.kt
│   ├── recommendedshows/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── recommendedshows/
│   │                                   └── RecommendedShowsInteractor.kt
│   ├── seasondetails/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── seasondetails/
│   │                                   ├── FetchPreviousSeasonsInteractor.kt
│   │                                   ├── MarkSeasonUnwatchedInteractor.kt
│   │                                   ├── MarkSeasonWatchedInteractor.kt
│   │                                   ├── ObservableSeasonDetailsInteractor.kt
│   │                                   ├── ObserveSeasonWatchProgressInteractor.kt
│   │                                   ├── ObserveUnwatchedInPreviousSeasonsInteractor.kt
│   │                                   ├── SeasonDetailsInteractor.kt
│   │                                   └── model/
│   │                                       ├── SeasonCast.kt
│   │                                       ├── SeasonDetailsResult.kt
│   │                                       └── SeasonImages.kt
│   ├── settings/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── settings/
│   │                                   └── ObserveSettingsPreferencesInteractor.kt
│   ├── showdetails/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── showdetails/
│   │                                   ├── FollowShowInteractor.kt
│   │                                   ├── Mapper.kt
│   │                                   ├── ObservableShowDetailsInteractor.kt
│   │                                   ├── ShowContentSyncInteractor.kt
│   │                                   ├── ShowDetailsInteractor.kt
│   │                                   └── model/
│   │                                       └── ShowDetails.kt
│   ├── similarshows/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── similarshows/
│   │                                   └── SimilarShowsInteractor.kt
│   ├── theme/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── theme/
│   │                                   ├── ImageQuality.kt
│   │                                   └── Theme.kt
│   ├── traktlists/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── traktlists/
│   │                                   ├── CreateTraktListInteractor.kt
│   │                                   ├── ObserveTraktListsInteractor.kt
│   │                                   ├── SyncTraktListsInteractor.kt
│   │                                   └── ToggleShowInListInteractor.kt
│   ├── upnext/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       ├── commonMain/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── domain/
│   │       │                       └── upnext/
│   │       │                           ├── ObserveUpNextInteractor.kt
│   │       │                           ├── RefreshUpNextInteractor.kt
│   │       │                           ├── UpNextSyncWorker.kt
│   │       │                           ├── UpNextTasksInitializer.kt
│   │       │                           ├── di/
│   │       │                           │   └── UpNextTasksInitializerBindingContainer.kt
│   │       │                           └── model/
│   │       │                               ├── UpNextResult.kt
│   │       │                               └── UpNextSortOption.kt
│   │       └── commonTest/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── upnext/
│   │                                   └── ObserveUpNextInteractorTest.kt
│   ├── user/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── user/
│   │                                   ├── ObserveUserProfileInteractor.kt
│   │                                   ├── UpdateUserProfileData.kt
│   │                                   └── model/
│   │                                       ├── UserProfile.kt
│   │                                       └── UserStats.kt
│   ├── watchlist/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       ├── commonMain/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── domain/
│   │       │                       └── watchlist/
│   │       │                           ├── ObservableWatchlistInteractor.kt
│   │       │                           ├── ObserveUpNextSectionsInteractor.kt
│   │       │                           ├── ObserveWatchlistSectionsInteractor.kt
│   │       │                           ├── UpNextSectionsMapper.kt
│   │       │                           ├── WatchlistSyncInteractor.kt
│   │       │                           └── model/
│   │       │                               ├── EpisodeBadge.kt
│   │       │                               ├── UpNextSections.kt
│   │       │                               └── WatchlistSections.kt
│   │       └── commonTest/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── watchlist/
│   │                                   ├── ObservableWatchlistInteractorTest.kt
│   │                                   ├── ObserveUpNextSectionsInteractorTest.kt
│   │                                   ├── ObserveWatchlistSectionsInteractorTest.kt
│   │                                   └── UpNextSectionsMapperTest.kt
│   └── watchproviders/
│       ├── build.gradle.kts
│       └── src/
│           └── commonMain/
│               └── kotlin/
│                   └── com/
│                       └── thomaskioko/
│                           └── tvmaniac/
│                               └── domain/
│                                   └── watchproviders/
│                                       └── WatchProvidersInteractor.kt
├── fastlane/
│   ├── Appfile
│   ├── Fastfile
│   ├── Matchfile
│   ├── Pluginfile
│   └── README.md
├── features/
│   ├── calendar/
│   │   ├── nav/
│   │   │   └── build.gradle.kts
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── presentation/
│   │   │       │                       └── calendar/
│   │   │       │                           ├── CalendarAction.kt
│   │   │       │                           ├── CalendarPresenter.kt
│   │   │       │                           ├── CalendarState.kt
│   │   │       │                           ├── CalendarStateMapper.kt
│   │   │       │                           └── model/
│   │   │       │                               ├── CalendarDateGroup.kt
│   │   │       │                               └── CalendarEpisodeItem.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── presentation/
│   │   │                               └── calendar/
│   │   │                                   └── CalendarPresenterTest.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           ├── main/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── ui/
│   │           │                       └── calendar/
│   │           │                           └── CalendarScreen.kt
│   │           └── test/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── ui/
│   │                                   └── calendar/
│   │                                       └── roborrazi/
│   │                                           └── CalendarScreenshotTest.kt
│   ├── debug/
│   │   ├── nav/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── debug/
│   │   │                               └── nav/
│   │   │                                   └── DebugRoute.kt
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── debug/
│   │   │       │                       └── presenter/
│   │   │       │                           ├── DebugActions.kt
│   │   │       │                           ├── DebugPresenter.kt
│   │   │       │                           └── DebugState.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── debug/
│   │   │                               └── presenter/
│   │   │                                   └── DebugPresenterTest.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── main/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── debug/
│   │                                   └── ui/
│   │                                       └── DebugMenuScreen.kt
│   ├── discover/
│   │   ├── nav/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── discover/
│   │   │                               └── nav/
│   │   │                                   └── DiscoverNavigator.kt
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── discover/
│   │   │       │                       └── presenter/
│   │   │       │                           ├── DiscoverShowsAction.kt
│   │   │       │                           ├── DiscoverShowsMapper.kt
│   │   │       │                           ├── DiscoverShowsPresenter.kt
│   │   │       │                           ├── DiscoverViewState.kt
│   │   │       │                           ├── di/
│   │   │       │                           │   └── DefaultDiscoverNavigator.kt
│   │   │       │                           └── model/
│   │   │       │                               ├── DiscoverShow.kt
│   │   │       │                               └── NextEpisodeUiModel.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── discover/
│   │   │                               └── presenter/
│   │   │                                   └── DiscoverShowsPresenterTest.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       ├── lint-baseline.xml
│   │       └── src/
│   │           ├── main/
│   │           │   └── java/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── discover/
│   │           │                       └── ui/
│   │           │                           ├── DiscoverPreviewParameterProvider.kt
│   │           │                           ├── DiscoverScreen.kt
│   │           │                           └── component/
│   │           │                               ├── CircularIndicator.kt
│   │           │                               ├── DiscoverHeaderContent.kt
│   │           │                               ├── HorizontalRowContent.kt
│   │           │                               ├── NextEpisodeCard.kt
│   │           │                               └── NextEpisodesSection.kt
│   │           └── test/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── discover/
│   │                                   └── roborrazi/
│   │                                       └── DiscoverScreenshotTest.kt
│   ├── episode-sheet/
│   │   ├── nav/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── espisodedetails/
│   │   │                               └── nav/
│   │   │                                   └── model/
│   │   │                                       ├── EpisodeSheetConfig.kt
│   │   │                                       ├── ScreenSource.kt
│   │   │                                       └── SheetNavigatorExt.kt
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── presentation/
│   │   │       │                       └── episodedetail/
│   │   │       │                           ├── EpisodeSheetAction.kt
│   │   │       │                           ├── EpisodeSheetMapper.kt
│   │   │       │                           ├── EpisodeSheetPresenter.kt
│   │   │       │                           └── EpisodeSheetState.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── presentation/
│   │   │                               └── episodedetail/
│   │   │                                   └── EpisodeSheetPresenterTest.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           ├── main/
│   │           │   └── java/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── episodedetail/
│   │           │                       └── ui/
│   │           │                           ├── EpisodeDetailBottomSheet.kt
│   │           │                           └── EpisodeSheet.kt
│   │           └── test/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── episodedetail/
│   │                                   └── roborrazi/
│   │                                       └── EpisodeSheetScreenshotTest.kt
│   ├── genre-shows/
│   │   ├── nav/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── genreshows/
│   │   │                               └── nav/
│   │   │                                   ├── GenreShowsDestination.kt
│   │   │                                   └── GenreShowsRoute.kt
│   │   └── presenter/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── genreshows/
│   │                                   └── presenter/
│   │                                       └── di/
│   │                                           └── GenreShowsNavDestinationBinding.kt
│   ├── home/
│   │   ├── nav/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── home/
│   │   │                               └── nav/
│   │   │                                   ├── HomeRoute.kt
│   │   │                                   ├── HomeTabNavigator.kt
│   │   │                                   ├── TabChild.kt
│   │   │                                   ├── TabDestination.kt
│   │   │                                   └── di/
│   │   │                                       ├── TabDestinationMultibindings.kt
│   │   │                                       └── model/
│   │   │                                           └── HomeConfig.kt
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── presenter/
│   │   │       │                       └── home/
│   │   │       │                           ├── HomePresenter.kt
│   │   │       │                           └── di/
│   │   │       │                               └── DefaultHomeTabNavigator.kt
│   │   │       ├── commonTest/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── presenter/
│   │   │       │                       └── home/
│   │   │       │                           └── HomePresenterTest.kt
│   │   │       ├── iosTest/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── presenter/
│   │   │       │                       └── home/
│   │   │       │                           └── HomePresenterIosTest.kt
│   │   │       └── jvmTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── presenter/
│   │   │                               └── home/
│   │   │                                   └── HomePresenterJvmTest.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── main/
│   │               └── java/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── home/
│   │                                   └── ui/
│   │                                       └── HomeScreen.kt
│   ├── library/
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── presentation/
│   │   │                               └── library/
│   │   │                                   ├── LibraryAction.kt
│   │   │                                   ├── LibraryPresenter.kt
│   │   │                                   ├── LibraryState.kt
│   │   │                                   └── model/
│   │   │                                       ├── LibraryShowItem.kt
│   │   │                                       ├── LibrarySortOption.kt
│   │   │                                       └── ShowStatus.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── main/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── ui/
│   │                                   └── library/
│   │                                       ├── LibraryListItem.kt
│   │                                       ├── LibraryScreen.kt
│   │                                       ├── LibrarySearchbar.kt
│   │                                       ├── SortOptionsContent.kt
│   │                                       └── preview/
│   │                                           ├── LibraryListItemPreviewParameterProvider.kt
│   │                                           └── LibraryStatePreviewParameterProvider.kt
│   ├── more-shows/
│   │   ├── nav/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── moreshows/
│   │   │                               └── nav/
│   │   │                                   └── MoreShowsRoute.kt
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── moreshows/
│   │   │                               └── presentation/
│   │   │                                   ├── MoreShowsAction.kt
│   │   │                                   ├── MoreShowsPresenter.kt
│   │   │                                   ├── MoreShowsState.kt
│   │   │                                   └── TvShow.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           ├── main/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── moreshows/
│   │           │                       └── ui/
│   │           │                           ├── MoreShowsPreviewParameterProvider.kt
│   │           │                           └── MoreShowsScreen.kt
│   │           └── test/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── moreshows/
│   │                                   └── roborrazi/
│   │                                       └── MoreShowsScreenTest.kt
│   ├── profile/
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── profile/
│   │   │       │                       └── presenter/
│   │   │       │                           ├── ProfileAction.kt
│   │   │       │                           ├── ProfilePresenter.kt
│   │   │       │                           └── model/
│   │   │       │                               ├── ProfileInfo.kt
│   │   │       │                               ├── ProfileState.kt
│   │   │       │                               └── ProfileStats.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── presenter/
│   │   │                               └── profile/
│   │   │                                   └── ProfilePresenterTest.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           ├── main/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── profile/
│   │           │                       └── ui/
│   │           │                           ├── ProfilePreviewParameterProvider.kt
│   │           │                           ├── ProfileScreen.kt
│   │           │                           ├── StatsCardItem.kt
│   │           │                           └── UnauthenticatedContent.kt
│   │           └── test/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── profile/
│   │                                   └── roborazzi/
│   │                                       └── ProfileScreenTest.kt
│   ├── progress/
│   │   ├── nav/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── progress/
│   │   │                               └── nav/
│   │   │                                   └── scope/
│   │   │                                       └── ProgressChildScope.kt
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── presentation/
│   │   │                               └── progress/
│   │   │                                   ├── ProgressAction.kt
│   │   │                                   ├── ProgressChildGraph.kt
│   │   │                                   ├── ProgressPresenter.kt
│   │   │                                   └── ProgressState.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           ├── main/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── ui/
│   │           │                       └── progress/
│   │           │                           └── ProgressScreen.kt
│   │           └── test/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── ui/
│   │                                   └── progress/
│   │                                       └── roborrazi/
│   │                                           └── ProgressScreenshotTest.kt
│   ├── root/
│   │   ├── nav/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── root/
│   │   │                           ├── model/
│   │   │                           │   ├── DeepLinkDestination.kt
│   │   │                           │   ├── NotificationPermissionState.kt
│   │   │                           │   └── ThemeState.kt
│   │   │                           └── nav/
│   │   │                               └── NotificationRationale.kt
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── presenter/
│   │   │       │                       └── root/
│   │   │       │                           ├── DefaultRootPresenter.kt
│   │   │       │                           ├── RootPresenter.kt
│   │   │       │                           └── di/
│   │   │       │                               ├── DefaultNotificationRationale.kt
│   │   │       │                               ├── DefaultSheetNavigator.kt
│   │   │       │                               └── RootPresenterBindingContainer.kt
│   │   │       ├── commonTest/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── presenter/
│   │   │       │                       └── root/
│   │   │       │                           └── DefaultRootPresenterTest.kt
│   │   │       ├── iosTest/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── presenter/
│   │   │       │                       └── root/
│   │   │       │                           └── DefaultRootPresenterIosTest.kt
│   │   │       └── jvmTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── presenter/
│   │   │                               └── root/
│   │   │                                   └── DefaultRootPresenterJvmTest.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── main/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── app/
│   │                                   └── ui/
│   │                                       └── RootScreen.kt
│   ├── search/
│   │   ├── nav/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── search/
│   │   │                               └── nav/
│   │   │                                   └── SearchRoute.kt
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── search/
│   │   │       │                       └── presenter/
│   │   │       │                           ├── Mapper.kt
│   │   │       │                           ├── SearchShowAction.kt
│   │   │       │                           ├── SearchShowState.kt
│   │   │       │                           ├── SearchShowsPresenter.kt
│   │   │       │                           └── model/
│   │   │       │                               ├── CategoryItem.kt
│   │   │       │                               ├── GenreRowModel.kt
│   │   │       │                               ├── ShowGenre.kt
│   │   │       │                               └── ShowItem.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── presenter/
│   │   │                               └── search/
│   │   │                                   └── SearchShowsPresenterTest.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           ├── main/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── search/
│   │           │                       └── ui/
│   │           │                           ├── SearchPreviewParameterProvider.kt
│   │           │                           ├── SearchScreen.kt
│   │           │                           └── components/
│   │           │                               ├── HorizontalShowContentRow.kt
│   │           │                               └── SearchResultItem.kt
│   │           └── test/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── search/
│   │                                   └── roborrazi/
│   │                                       └── SearchScreenTest.kt
│   ├── season-details/
│   │   ├── nav/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── seasondetails/
│   │   │                               └── nav/
│   │   │                                   ├── SeasonDetailsRoute.kt
│   │   │                                   └── SeasonDetailsUiParam.kt
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── seasondetails/
│   │   │       │                       └── presenter/
│   │   │       │                           ├── Mapper.kt
│   │   │       │                           ├── SeasonDetailsAction.kt
│   │   │       │                           ├── SeasonDetailsModel.kt
│   │   │       │                           ├── SeasonDetailsPresenter.kt
│   │   │       │                           └── model/
│   │   │       │                               ├── Cast.kt
│   │   │       │                               ├── EpisodeDetailsModel.kt
│   │   │       │                               └── SeasonImagesModel.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── seasondetails/
│   │   │                               └── presenter/
│   │   │                                   ├── SeasonPresenterTest.kt
│   │   │                                   └── data/
│   │   │                                       └── MockData.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           ├── main/
│   │           │   └── java/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── seasondetails/
│   │           │                       └── ui/
│   │           │                           ├── SeasonDetailsScreen.kt
│   │           │                           ├── SeasonPreviewParameterProvider.kt
│   │           │                           └── components/
│   │           │                               ├── CollapsableContent.kt
│   │           │                               └── EpisodeItem.kt
│   │           └── test/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── seasondetails/
│   │                                   └── roborrazi/
│   │                                       └── SeasonScreenshotTest.kt
│   ├── settings/
│   │   ├── nav/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── settings/
│   │   │                               └── nav/
│   │   │                                   └── SettingsRoute.kt
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── settings/
│   │   │       │                       └── presenter/
│   │   │       │                           ├── SettingsActions.kt
│   │   │       │                           ├── SettingsPresenter.kt
│   │   │       │                           ├── SettingsState.kt
│   │   │       │                           └── ThemeModel.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── presenter/
│   │   │                               └── settings/
│   │   │                                   └── SettingsPresenterTest.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           ├── main/
│   │           │   ├── kotlin/
│   │           │   │   └── com/
│   │           │   │       └── thomaskioko/
│   │           │   │           └── tvmaniac/
│   │           │   │               └── settings/
│   │           │   │                   └── ui/
│   │           │   │                       ├── AboutSheetContent.kt
│   │           │   │                       ├── SettingsPreviewParameterProvider.kt
│   │           │   │                       ├── SettingsScreen.kt
│   │           │   │                       ├── ThemePreviewSwatch.kt
│   │           │   │                       └── ThemeSelectorSection.kt
│   │           │   └── res/
│   │           │       └── drawable/
│   │           │           └── ic_app_launcher.xml
│   │           └── test/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── seasondetails/
│   │                                   └── roborrazi/
│   │                                       └── SettingsScreenshotTest.kt
│   ├── show-details/
│   │   ├── nav/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── showdetails/
│   │   │                               └── nav/
│   │   │                                   ├── ShowDetailsRoute.kt
│   │   │                                   └── model/
│   │   │                                       ├── ShowDetailsParam.kt
│   │   │                                       └── ShowSeasonDetailsParam.kt
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── presenter/
│   │   │       │                       └── showdetails/
│   │   │       │                           ├── ShowDetailsAction.kt
│   │   │       │                           ├── ShowDetailsContent.kt
│   │   │       │                           ├── ShowDetailsMapper.kt
│   │   │       │                           ├── ShowDetailsPresenter.kt
│   │   │       │                           └── model/
│   │   │       │                               ├── CastModel.kt
│   │   │       │                               ├── ContinueTrackingEpisodeModel.kt
│   │   │       │                               ├── ProviderModel.kt
│   │   │       │                               ├── SeasonModel.kt
│   │   │       │                               ├── ShowDetailsModel.kt
│   │   │       │                               ├── ShowModel.kt
│   │   │       │                               ├── TrailerModel.kt
│   │   │       │                               └── TraktListModel.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── presenter/
│   │   │                               └── showdetails/
│   │   │                                   ├── MockData.kt
│   │   │                                   └── ShowDetailsPresenterTest.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           ├── main/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── showdetails/
│   │           │                       └── ui/
│   │           │                           ├── DetailPreviewParameterProvider.kt
│   │           │                           ├── ShowDetailScreen.kt
│   │           │                           └── components/
│   │           │                               ├── ContinueTrackingCard.kt
│   │           │                               ├── ContinueTrackingSection.kt
│   │           │                               ├── SeasonChipItem.kt
│   │           │                               ├── ShowListSheetContent.kt
│   │           │                               └── WatchProgressSection.kt
│   │           └── test/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── showdetails/
│   │                                   └── roborrazi/
│   │                                       ├── ShowDetailsScreenScreenshotTest.kt
│   │                                       └── ShowListSheetScreenshotTest.kt
│   ├── trailers/
│   │   ├── nav/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── trailers/
│   │   │                               └── nav/
│   │   │                                   └── TrailersRoute.kt
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── presenter/
│   │   │       │                       └── trailers/
│   │   │       │                           ├── Mapper.kt
│   │   │       │                           ├── TrailersAction.kt
│   │   │       │                           ├── TrailersPresenter.kt
│   │   │       │                           ├── TrailersState.kt
│   │   │       │                           └── model/
│   │   │       │                               └── Trailer.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── presenter/
│   │   │                               └── trailers/
│   │   │                                   └── TrailersPresenterTest.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── main/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── trailers/
│   │                                   └── ui/
│   │                                       ├── TrailerPreviewParameterProvider.kt
│   │                                       └── TrailersScreen.kt
│   ├── upnext/
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── presentation/
│   │   │       │                       └── upnext/
│   │   │       │                           ├── UpNextAction.kt
│   │   │       │                           ├── UpNextPresenter.kt
│   │   │       │                           ├── UpNextState.kt
│   │   │       │                           └── model/
│   │   │       │                               └── UpNextEpisodeUiModel.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── presentation/
│   │   │                               └── upnext/
│   │   │                                   └── UpNextPresenterTest.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           ├── main/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── ui/
│   │           │                       └── upnext/
│   │           │                           ├── UpNextListItem.kt
│   │           │                           ├── UpNextScreen.kt
│   │           │                           └── preview/
│   │           │                               └── UpNextPreviewParameterProvider.kt
│   │           └── test/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── ui/
│   │                                   └── upnext/
│   │                                       └── roborrazi/
│   │                                           └── UpNextScreenshotTest.kt
│   └── watchlist/
│       ├── presenter/
│       │   ├── build.gradle.kts
│       │   └── src/
│       │       ├── commonMain/
│       │       │   └── kotlin/
│       │       │       └── com/
│       │       │           └── thomaskioko/
│       │       │               └── tvmaniac/
│       │       │                   └── watchlist/
│       │       │                       └── presenter/
│       │       │                           ├── Mapper.kt
│       │       │                           ├── WatchlistAction.kt
│       │       │                           ├── WatchlistPresenter.kt
│       │       │                           ├── WatchlistState.kt
│       │       │                           └── model/
│       │       │                               ├── EpisodeBadge.kt
│       │       │                               ├── NextEpisodeItem.kt
│       │       │                               ├── SectionedEpisodes.kt
│       │       │                               ├── SectionedItems.kt
│       │       │                               ├── UpNextEpisodeItem.kt
│       │       │                               └── WatchlistItem.kt
│       │       └── commonTest/
│       │           └── kotlin/
│       │               └── com/
│       │                   └── thomaskioko/
│       │                       └── tvmaniac/
│       │                           ├── domain/
│       │                           │   └── watchlist/
│       │                           │       ├── MockData.kt
│       │                           │       └── WatchlistPresenterTest.kt
│       │                           └── watchlist/
│       │                               └── presenter/
│       │                                   └── FakeWatchlistPresenterBuilder.kt
│       └── ui/
│           ├── build.gradle.kts
│           └── src/
│               ├── main/
│               │   └── kotlin/
│               │       └── com/
│               │           └── thomaskioko/
│               │               └── tvmaniac/
│               │                   └── ui/
│               │                       └── library/
│               │                           ├── WatchListUpNextListItem.kt
│               │                           ├── WatchlistListItem.kt
│               │                           ├── WatchlistPreviewParameterProvider.kt
│               │                           ├── WatchlistScreen.kt
│               │                           └── component/
│               │                               └── Searchbar.kt
│               └── test/
│                   └── kotlin/
│                       └── com/
│                           └── thomaskioko/
│                               └── tvmaniac/
│                                   └── watchlist/
│                                       └── roborrazi/
│                                           └── WatchlistScreenTest.kt
├── gradle/
│   ├── gradle-daemon-jvm.properties
│   ├── libs.versions.toml
│   ├── lint.xml
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── i18n/
│   ├── api/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── i18n/
│   │                               └── api/
│   │                                   └── Localizer.kt
│   ├── generator/
│   │   ├── build.gradle.kts
│   │   ├── lint-baseline.xml
│   │   └── src/
│   │       ├── androidMain/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── i18n/
│   │       │                       └── Resources.kt
│   │       ├── commonMain/
│   │       │   └── moko-resources/
│   │       │       ├── base/
│   │       │       │   ├── plurals.xml
│   │       │       │   └── strings.xml
│   │       │       ├── de/
│   │       │       │   ├── plurals.xml
│   │       │       │   └── strings.xml
│   │       │       └── fr/
│   │       │           ├── plurals.xml
│   │       │           └── strings.xml
│   │       ├── commonTest/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── i18n/
│   │       │                       └── generator/
│   │       │                           └── ResourceTest.kt
│   │       └── iosMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── i18n/
│   │                               └── Resources.kt
│   ├── implementation/
│   │   ├── build.gradle.kts
│   │   ├── lint-baseline.xml
│   │   └── src/
│   │       ├── androidHostTest/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── i18n/
│   │       │                       └── util/
│   │       │                           └── BaseResourceTests.kt
│   │       ├── androidMain/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── i18n/
│   │       │                       └── PlatformLocalizer.android.kt
│   │       ├── commonMain/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── i18n/
│   │       │                       ├── LocalizedErrorToStringMapper.kt
│   │       │                       ├── MokoLocaleInitializer.kt
│   │       │                       ├── MokoResourcesLocalizer.kt
│   │       │                       ├── PlatformLocalizer.kt
│   │       │                       └── di/
│   │       │                           └── MokoLocaleInitializerBindingContainer.kt
│   │       ├── commonTest/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── i18n/
│   │       │                       ├── LocalizedStringTest.kt
│   │       │                       ├── MokoLocalizerTest.kt
│   │       │                       └── util/
│   │       │                           └── BaseResourceTests.kt
│   │       ├── iosMain/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── i18n/
│   │       │                       └── PlatformLocalizer.ios.kt
│   │       ├── iosTest/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── i18n/
│   │       │                       └── util/
│   │       │                           └── BaseResourceTests.kt
│   │       ├── jvmMain/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── i18n/
│   │       │                       └── PlatformLocalizer.jvm.kt
│   │       └── jvmTest/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── i18n/
│   │                               └── util/
│   │                                   └── BaseResourceTests.kt
│   └── testing/
│       ├── build.gradle.kts
│       └── src/
│           ├── androidMain/
│           │   └── kotlin/
│           │       └── com/
│           │           └── thomaskioko/
│           │               └── tvmaniac/
│           │                   └── i18n/
│           │                       └── testing/
│           │                           └── util/
│           │                               ├── BaseLocalizerTest.android.kt
│           │                               └── StringDescExt.kt
│           ├── commonMain/
│           │   └── kotlin/
│           │       └── com/
│           │           └── thomaskioko/
│           │               └── tvmaniac/
│           │                   └── i18n/
│           │                       └── testing/
│           │                           ├── FakeLocalizer.kt
│           │                           └── util/
│           │                               ├── BaseLocalizerTest.kt
│           │                               └── StringDescExt.kt
│           ├── iosMain/
│           │   └── kotlin/
│           │       └── com/
│           │           └── thomaskioko/
│           │               └── tvmaniac/
│           │                   └── i18n/
│           │                       └── testing/
│           │                           └── util/
│           │                               ├── BaseLocalizerTest.ios.kt
│           │                               └── StringDescExt.ios.kt
│           └── jvmMain/
│               └── kotlin/
│                   └── com/
│                       └── thomaskioko/
│                           └── tvmaniac/
│                               └── i18n/
│                                   └── testing/
│                                       └── util/
│                                           ├── BaseLocalizerTest.jvm.kt
│                                           └── StringDescExt.jvm.kt
├── ios/
│   ├── .gitignore
│   ├── .swiftformat
│   ├── Config/
│   │   ├── Debug.xcconfig
│   │   └── Release.xcconfig
│   ├── Modules/
│   │   ├── CoreKit/
│   │   │   ├── Package.swift
│   │   │   └── Sources/
│   │   │       └── CoreKit/
│   │   │           ├── CoreLogger.swift
│   │   │           ├── DefaultDiagnosticLogger.swift
│   │   │           ├── DiagnosticLogger.swift
│   │   │           ├── FirebaseCrashlyticsBridge.swift
│   │   │           ├── ImageCacheManager.swift
│   │   │           ├── MemoryMonitor.swift
│   │   │           └── SystemMemory.swift
│   │   ├── SnapshotTestingLib/
│   │   │   ├── .gitignore
│   │   │   ├── .swiftpm/
│   │   │   │   └── xcode/
│   │   │   │       └── package.xcworkspace/
│   │   │   │           └── xcshareddata/
│   │   │   │               └── IDEWorkspaceChecks.plist
│   │   │   ├── Package.swift
│   │   │   └── Sources/
│   │   │       └── SnapshotTestingLib/
│   │   │           └── SnapshotTesting+Extensions.swift
│   │   ├── SwiftUIComponents/
│   │   │   ├── .gitignore
│   │   │   ├── .swiftpm/
│   │   │   │   └── xcode/
│   │   │   │       └── package.xcworkspace/
│   │   │   │           └── xcshareddata/
│   │   │   │               └── IDEWorkspaceChecks.plist
│   │   │   ├── Package.swift
│   │   │   ├── Sources/
│   │   │   │   └── SwiftUIComponents/
│   │   │   │       ├── Components/
│   │   │   │       │   ├── BorderTextView.swift
│   │   │   │       │   ├── BottomSheet/
│   │   │   │       │   │   └── EpisodeDetailSheetContent.swift
│   │   │   │       │   ├── Buttons/
│   │   │   │       │   │   ├── CircularButton.swift
│   │   │   │       │   │   ├── FilledImageButton.swift
│   │   │   │       │   │   ├── OutlinedButton.swift
│   │   │   │       │   │   ├── RoundedButton.swift
│   │   │   │       │   │   └── TvManiacButton.swift
│   │   │   │       │   ├── CarouselView.swift
│   │   │   │       │   ├── CastListView.swift
│   │   │   │       │   ├── ChevronTitle.swift
│   │   │   │       │   ├── ChipView.swift
│   │   │   │       │   ├── CircularIndicator.swift
│   │   │   │       │   ├── ContinueTracking/
│   │   │   │       │   │   ├── ContinueTrackingCard.swift
│   │   │   │       │   │   ├── ContinueTrackingSection.swift
│   │   │   │       │   │   └── SwiftContinueTrackingEpisode.swift
│   │   │   │       │   ├── EmptyUIView.swift
│   │   │   │       │   ├── Episode/
│   │   │   │       │   │   ├── EpisodeCollapsible.swift
│   │   │   │       │   │   ├── EpisodeItemView.swift
│   │   │   │       │   │   └── EpisodeListView.swift
│   │   │   │       │   ├── FilterChip.swift
│   │   │   │       │   ├── FilterChipSection.swift
│   │   │   │       │   ├── FlowLayout.swift
│   │   │   │       │   ├── FullScreenView.swift
│   │   │   │       │   ├── GlassButton.swift
│   │   │   │       │   ├── GlassToolbar.swift
│   │   │   │       │   ├── GridView.swift
│   │   │   │       │   ├── HorizontalItemListView.swift
│   │   │   │       │   ├── ImageGalleryContentView.swift
│   │   │   │       │   ├── Images/
│   │   │   │       │   │   ├── AvatarView.swift
│   │   │   │       │   │   ├── BackdropPosterCard.swift
│   │   │   │       │   │   ├── CastCardView.swift
│   │   │   │       │   │   ├── FeaturedContentPosterView.swift
│   │   │   │       │   │   ├── HeaderCoverArtWorkView.swift
│   │   │   │       │   │   ├── LazyResizableImage.swift
│   │   │   │       │   │   ├── PosterCardView.swift
│   │   │   │       │   │   ├── PosterItemView.swift
│   │   │   │       │   │   ├── PosterPlaceholder.swift
│   │   │   │       │   │   ├── ProviderItemView.swift
│   │   │   │       │   │   └── TransparentImageBackground.swift
│   │   │   │       │   ├── LibraryListItemView.swift
│   │   │   │       │   ├── LoadingIndicatorView.swift
│   │   │   │       │   ├── Models/
│   │   │   │       │   │   ├── DebugMenuItem.swift
│   │   │   │       │   │   ├── SettingsModels.swift
│   │   │   │       │   │   ├── ShowPosterImage.swift
│   │   │   │       │   │   ├── SwiftCalendarDateGroup.swift
│   │   │   │       │   │   ├── SwiftCast.swift
│   │   │   │       │   │   ├── SwiftGenreRow.swift
│   │   │   │       │   │   ├── SwiftGenres.swift
│   │   │   │       │   │   ├── SwiftLibraryItem.swift
│   │   │   │       │   │   ├── SwiftProfile.swift
│   │   │   │       │   │   ├── SwiftProviders.swift
│   │   │   │       │   │   ├── SwiftSearchShow.swift
│   │   │   │       │   │   ├── SwiftSeason.swift
│   │   │   │       │   │   ├── SwiftShow.swift
│   │   │   │       │   │   ├── SwiftShowGenre.swift
│   │   │   │       │   │   ├── SwiftTrailer.swift
│   │   │   │       │   │   └── SwiftTraktListItem.swift
│   │   │   │       │   ├── NavigationTopBar.swift
│   │   │   │       │   ├── NextEpisode/
│   │   │   │       │   │   ├── NextEpisodeCard.swift
│   │   │   │       │   │   ├── NextEpisodesSection.swift
│   │   │   │       │   │   ├── SwiftNextEpisode.swift
│   │   │   │       │   │   └── UpNextListItemView.swift
│   │   │   │       │   ├── NotificationRationaleSheet.swift
│   │   │   │       │   ├── OverviewBoxView.swift
│   │   │   │       │   ├── ParallaxView.swift
│   │   │   │       │   ├── ProviderListView.swift
│   │   │   │       │   ├── ScanlineOverlay.swift
│   │   │   │       │   ├── Search/
│   │   │   │       │   │   ├── SearchItemView.swift
│   │   │   │       │   │   ├── SearchResultListView.swift
│   │   │   │       │   │   └── ShowContent/
│   │   │  

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
root = true

[*]
charset = utf-8
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

# noinspection EditorConfigKeyCorrectness
[*.{kt,kts}]
ij_kotlin_allow_trailing_comma = true
ij_kotlin_allow_trailing_comma_on_call_site = true
ktlint_function_naming_ignore_when_annotated_with = Composable, Test
ktlint_standard_backing-property-naming = disabled
ktlint_standard_binary-expression-wrapping = disabled
ktlint_standard_chain-method-continuation = disabled
ktlint_standard_class-signature = disabled
ktlint_standard_condition-wrapping = disabled
ktlint_standard_function-expression-body = disabled
ktlint_standard_function-literal = disabled
ktlint_standard_function-type-modifier-spacing = disabled
ktlint_standard_multiline-loop = disabled
ktlint_standard_function-signature = disabled

================================================
FILE: .geminiignore
================================================
.gradle/
build/
.kotlin/
.idea/
.build/
ios/build/
ios/SourcePackages/
derived_data/
*.log
*.class
*.apk
*.ap_
*.dex
local.properties
.DS_Store
fastlane/test_output
fastlane/builds
node_modules/
dist/
target/


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: Bug Report
description: Report a reproducible bug in TvManiac.
title: "[Bug]: "
labels: ["bug"]
body:
  - type: markdown
    attributes:
      value: |
        Thanks for taking the time to file a bug report. Please fill out as much detail as you can. Issues without reproduction steps are hard to act on.

  - type: textarea
    id: description
    attributes:
      label: Description
      description: What happened, and what did you expect to happen instead?
      placeholder: A clear description of the bug.
    validations:
      required: true

  - type: textarea
    id: reproduction
    attributes:
      label: Steps to Reproduce
      description: Minimal, numbered steps that reliably reproduce the issue.
      placeholder: |
        1. Open the app.
        2. Navigate to...
        3. Tap...
        4. Observe the bug.
    validations:
      required: true

  - type: textarea
    id: expected
    attributes:
      label: Expected Behavior
      placeholder: What you expected to happen.
    validations:
      required: true

  - type: textarea
    id: actual
    attributes:
      label: Actual Behavior
      placeholder: What actually happened. Include error messages if any.
    validations:
      required: true

  - type: dropdown
    id: platform
    attributes:
      label: Platform
      options:
        - Android
        - iOS
        - Both
    validations:
      required: true

  - type: input
    id: device
    attributes:
      label: Device and OS Version
      description: e.g. Pixel 8 on Android 15, iPhone 15 Pro on iOS 18.2.
      placeholder: Device model and OS version.
    validations:
      required: true

  - type: input
    id: version
    attributes:
      label: App Version or Commit SHA
      description: Version from the About screen, or the commit SHA if running from source.
      placeholder: e.g. 1.4.0, or 3298cb27f.
    validations:
      required: true

  - type: textarea
    id: logs
    attributes:
      label: Logs, Screenshots, or Recordings
      description: Logcat output, Xcode console logs, screenshots, or screen recordings. Redact any API keys or personal data.
      render: shell

  - type: checkboxes
    id: checklist
    attributes:
      label: Checklist
      options:
        - label: I searched existing issues and did not find a duplicate.
          required: true
        - label: I redacted any secrets, tokens, or personal data from logs and screenshots.
          required: true


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yml
================================================
name: Feature Request
description: Suggest a new feature or improvement for TvManiac.
title: "[Feature]: "
labels: ["enhancement"]
body:
  - type: markdown
    attributes:
      value: |
        Thanks for suggesting an improvement. Please describe the problem first, then the proposed solution. Ideas grounded in a concrete use case are easier to evaluate.

  - type: textarea
    id: problem
    attributes:
      label: Problem Statement
      description: What problem are you trying to solve? What is missing or inconvenient today?
      placeholder: As a user, I want... so that...
    validations:
      required: true

  - type: textarea
    id: solution
    attributes:
      label: Proposed Solution
      description: Describe the behavior you would like to see. Include mockups or references if helpful.
    validations:
      required: true

  - type: textarea
    id: alternatives
    attributes:
      label: Alternatives Considered
      description: Other approaches you thought about and why they are less suitable.

  - type: dropdown
    id: platform
    attributes:
      label: Platform Scope
      options:
        - Android
        - iOS
        - Both
        - Not sure
    validations:
      required: true

  - type: textarea
    id: context
    attributes:
      label: Additional Context
      description: Any other context, links, or references that help motivate the feature.

  - type: checkboxes
    id: checklist
    attributes:
      label: Checklist
      options:
        - label: I searched existing issues and discussions and did not find a duplicate.
          required: true


================================================
FILE: .github/actions/setup-android-release/action.yml
================================================
name: 'Setup Android Release'
description: 'Common setup for Android release builds: Gradle, Ruby, google-services.json, and signing key decryption'

inputs:
  google-services-json:
    description: 'Base64-encoded google-services.json for release'
    required: false
  signing-encrypt-key:
    description: 'Encryption key for decrypting signing keystores'
    required: true

runs:
  using: 'composite'
  steps:
    - name: Setup Gradle Environment
      uses: ./.github/actions/setup-gradle

    - name: Setup Ruby
      uses: ruby/setup-ruby@v1
      with:
        bundler-cache: true

    - name: Install google-services.json
      shell: bash
      env:
        GOOGLE_SERVICES_JSON_RELEASE: ${{ inputs.google-services-json }}
      run: |
        if [ -n "$GOOGLE_SERVICES_JSON_RELEASE" ]; then
          mkdir -p app/src/release
          echo "$GOOGLE_SERVICES_JSON_RELEASE" | base64 --decode > app/src/release/google-services.json
        else
          echo "warning: google-services.json not provided - Firebase will be disabled"
        fi

    - name: Decrypt signing keys
      shell: bash
      env:
        SIGNING_ENCRYPT_KEY: ${{ inputs.signing-encrypt-key }}
      run: |
        openssl aes-256-cbc -d -pbkdf2 -in release/app-release.aes -out release/app-release.jks -k "$SIGNING_ENCRYPT_KEY"
        openssl aes-256-cbc -d -pbkdf2 -in release/play-service-account.aes -out release/play-service-account.json -k "$SIGNING_ENCRYPT_KEY"
        if [ -f "release/firebase-sa.aes" ]; then
          openssl aes-256-cbc -d -pbkdf2 -in release/firebase-sa.aes -out release/firebase-sa.json -k "$SIGNING_ENCRYPT_KEY"
        fi


================================================
FILE: .github/actions/setup-gradle/action.yml
================================================
name: 'Setup Gradle Environment'
description: 'Common setup for Gradle builds with JDK 21 and environment variables'

inputs:
  gradle-cache-disabled:
    description: 'Disable Gradle cache (default: false = cache enabled). Only set to true for baseline-profile workflow.'
    required: false
    default: 'false'
  gradle-cache-read-only:
    description: 'Make Gradle cache read-only (default: auto-detected from event type). PRs read cache but do not write.'
    required: false
    default: ${{ github.event_name == 'pull_request' }}

runs:
  using: 'composite'
  steps:
    - name: Set up JDK 21
      uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
      with:
        distribution: 'temurin'
        java-version: '21'

    - name: Install git-cliff
      uses: kenji-miyake/setup-git-cliff@2778609c643a39a2576c4bae2e493b855eb4aee8 # v2.0.1

    - name: Setup Gradle
      uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0
      with:
        cache-provider: basic
        cache-disabled: ${{ inputs.gradle-cache-disabled }}
        cache-read-only: ${{ inputs.gradle-cache-read-only }}


================================================
FILE: .github/actions/setup-ios/action.yml
================================================
name: 'Setup iOS Environment'
description: 'Common setup for iOS builds with Xcode, Ruby, SPM cache, and Gradle'

inputs:
  xcode-version:
    description: 'Xcode version to use'
    required: true
  spm-cache-path:
    description: 'Path to SPM dependencies for caching'
    required: false
    default: 'ios/SourcePackages'

runs:
  using: 'composite'
  steps:
    - name: 📀 Setup Xcode version
      uses: maxim-lobanov/setup-xcode@v1
      with:
        xcode-version: ${{ inputs.xcode-version }}

    - name: 📱 Verify Simulator Availability
      shell: bash
      run: |
        echo "Xcode: $(xcodebuild -version | head -1)"
        echo "Available iOS runtimes:"
        xcrun simctl list runtimes iOS
        echo "Available iPhone simulators:"
        xcrun simctl list devices available iPhone

    - name: 💎 Set up Ruby
      uses: ruby/setup-ruby@v1
      with:
        bundler-cache: true

    - name: Cache SPM dependencies
      uses: actions/cache@v5
      with:
        path: ${{ inputs.spm-cache-path }}
        key: ${{ runner.os }}-spm-${{ github.sha }}
        restore-keys: |
          ${{ runner.os }}-spm-

    - name: 🐘 Setup Gradle
      uses: ./.github/actions/setup-gradle


================================================
FILE: .github/actions/setup-ios-release/action.yml
================================================
name: 'Setup iOS Release'
description: 'Common setup for iOS release builds: Xcode, Ruby, SPM cache, Gradle, and GoogleService-Info.plist'

inputs:
  xcode-version:
    description: 'Xcode version to use'
    required: true
  google-service-info-plist:
    description: 'Base64-encoded GoogleService-Info.plist for release'
    required: false

runs:
  using: 'composite'
  steps:
    - name: Setup iOS Environment
      uses: ./.github/actions/setup-ios
      with:
        xcode-version: ${{ inputs.xcode-version }}

    - name: Install GoogleService-Info.plist
      shell: bash
      env:
        GOOGLE_SERVICE_INFO: ${{ inputs.google-service-info-plist }}
      run: |
        if [ -n "$GOOGLE_SERVICE_INFO" ]; then
          mkdir -p ios/ios/Firebase/Release
          echo "$GOOGLE_SERVICE_INFO" | base64 --decode > ios/ios/Firebase/Release/GoogleService-Info.plist
        else
          echo "warning: GoogleService-Info.plist not provided - Firebase will be disabled"
        fi


================================================
FILE: .github/release.yml
================================================
changelog:
  exclude:
    labels:
      - skip-changelog
    authors:
      - renovate[bot]
      - dependabot[bot]
  categories:
    - title: "Features"
      labels:
        - feature
        - enhancement
    - title: "Bug Fixes"
      labels:
        - bug
        - fix
    - title: "Dependencies"
      labels:
        - dependencies
    - title: "Other Changes"
      exclude:
        labels:
          - feature
          - enhancement
          - bug
          - fix
          - dependencies
          - skip-changelog


================================================
FILE: .github/renovate.json
================================================
{
    "$schema": "https://docs.renovatebot.com/renovate-schema.json",
    "extends": [
        "config:recommended",
        ":disableRateLimiting",
        ":semanticCommitsDisabled"
    ],
    "labels": [
        "renovate"
    ],
    "ignoreDeps": [
        "renovate/renovate"
    ],
    "ignorePaths": [
        "**/.ruby-version"
    ],
    "packageRules": [
        {
            "matchDatasources": [
                "maven"
            ],
            "registryUrls": [
                "https://repo.maven.apache.org/maven2",
                "https://dl.google.com/android/maven2",
                "https://plugins.gradle.org/m2"
            ]
        },
        {
            "groupName": "Compose",
            "matchPackageNames": [
                "/androidx.compose.runtime/",
                "/androidx.compose.ui/",
                "/androidx.compose.foundation/",
                "/androidx.compose.animation/",
                "/androidx.compose.material/",
                "/androidx.compose.material3/",
                "/org.jetbrains.compose$/",
                "/org.jetbrains.compose.runtime/",
                "/org.jetbrains.compose.ui/",
                "/org.jetbrains.compose.foundation/",
                "/org.jetbrains.compose.animation/",
                "/org.jetbrains.compose.material/",
                "/org.jetbrains.compose.material3/"
            ]
        },
        {
            "groupName": "Roborazzi",
            "matchPackageNames": [
                "/io.github.takahirom.roborazzi/"
            ]
        }
    ],
    "dependencyDashboard": true,
    "rebaseWhen": "conflicted",
    "configMigration": true,
    "commitMessagePrefix": "chore(deps):"
}


================================================
FILE: .github/workflows/baseline-profile.yml
================================================
name: Weekly Baseline Profile Generation

on:
  schedule:
    - cron: '30 0 * * 0,3,5' #Every Sunday, Wednesday, and Friday at 12:30AM

env:
  TMDB_API_KEY: ${{ secrets.TMDB_API_KEY }}
  TRAKT_CLIENT_ID: ${{ secrets.TRAKT_CLIENT_ID }}
  TRAKT_CLIENT_SECRET: ${{ secrets.TRAKT_CLIENT_SECRET }}
  TRAKT_REDIRECT_URI: ${{ secrets.TRAKT_REDIRECT_URI }}

jobs:
  baseline_profiles:
    name: "Generate Baseline Profiles"
    runs-on: ubuntu-latest

    permissions:
      contents: write

    timeout-minutes: 60

    steps:
      - name: Checkout
        uses: actions/checkout@v6

      - name: Enable KVM
        run: |
          echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
          sudo udevadm control --reload-rules
          sudo udevadm trigger --name-match=kvm

      - name: Setup Gradle Environment
        uses: ./.github/actions/setup-gradle
        with:
          gradle-cache-disabled: 'true'

      - name: Setup Android SDK
        uses: android-actions/setup-android@v4

      - name: Accept licenses
        run: yes | sdkmanager --licenses || trueMovr

      - name: Build app and benchmark
        run: ./gradlew assembleNonMinifiedRelease -Papp.debugOnly=false

      - name: Clear Gradle Managed Devices
        run: ./gradlew cleanManagedDevices

      - name: Generate Baseline Profile
        run: ./gradlew :app:generateBaselineProfile
          -Papp.debugOnly=false
          -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=baselineprofile
          -Pandroid.testoptions.manageddevices.emulator.gpu="swiftshader_indirect"
          -Pandroid.experimental.androidTest.numManagedDeviceShards=1
          --no-configuration-cache

      ## ToDo: Commit baseline profile changes to main.


================================================
FILE: .github/workflows/beta-release.yml
================================================
name: Beta Release

on:
  workflow_dispatch:
    inputs:
      skip_android:
        description: 'Skip Android build'
        type: boolean
        default: false
      skip_ios:
        description: 'Skip iOS build'
        type: boolean
        default: false

permissions:
  contents: write

env:
  XCODE_VERSION: 16.4
  TMDB_API_KEY: ${{ secrets.TMDB_API_KEY }}
  TRAKT_CLIENT_ID: ${{ secrets.TRAKT_CLIENT_ID }}
  TRAKT_CLIENT_SECRET: ${{ secrets.TRAKT_CLIENT_SECRET }}
  TRAKT_REDIRECT_URI: ${{ secrets.TRAKT_REDIRECT_URI }}

jobs:
  prepare:
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.bump.outputs.version }}
      build: ${{ steps.bump.outputs.build }}
      sha: ${{ steps.push.outputs.sha }}
    steps:
      - name: Checkout main
        uses: actions/checkout@v6
        with:
          ref: main
          fetch-depth: 0
          token: ${{ secrets.RELEASE_TOKEN }}

      - name: Setup Gradle Environment
        uses: ./.github/actions/setup-gradle

      - name: Configure git
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"

      - name: Bump beta version
        id: bump
        run: |
          ./gradlew bumpVersion -Ptype=beta
          version=$(grep 'VERSION_NUMBER' version.txt | sed 's/.*= *//')
          build=$(grep 'BUILD_NUMBER' version.txt | sed 's/.*= *//')
          echo "version=$version" >> "$GITHUB_OUTPUT"
          echo "build=$build" >> "$GITHUB_OUTPUT"
          echo "Version: $version, BUILD_NUMBER: $build"

      - name: Commit and push
        id: push
        run: |
          git add version.txt
          git commit -m "chore: bump beta build number to ${{ steps.bump.outputs.build }}"
          git push origin main
          echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"

  build-android:
    needs: prepare
    if: ${{ !inputs.skip_android }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          ref: ${{ needs.prepare.outputs.sha }}

      - name: Setup Android Release
        uses: ./.github/actions/setup-android-release
        with:
          google-services-json: ${{ secrets.GOOGLE_SERVICES_JSON_RELEASE }}
          signing-encrypt-key: ${{ secrets.SIGNING_ENCRYPT_KEY }}

      - name: Build release AAB and APK
        env:
          SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }}
          SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }}
        run: |
          ./gradlew :app:bundleRelease :app:assembleRelease \
            -Pandroidx.baselineprofile.skipgeneration=true \
            -Papp.debugOnly=false \
            -Papp.versionSuffix=-beta \
            -PreleaseStoreFile=release/app-release.jks \
            -PreleaseStorePassword="$SIGNING_STORE_PASSWORD" \
            -PreleaseKeyAlias=tvmaniac \
            -PreleaseKeyPassword="$SIGNING_KEY_PASSWORD" \
            --no-configuration-cache

      - name: Print build info
        run: |
          echo "Version: $(grep 'VERSION_NUMBER' version.txt | sed 's/.*= *//')-beta"
          echo "Build:   $(grep 'BUILD_NUMBER' version.txt | sed 's/.*= *//')"

      - name: Deploy to Play Store open testing track
        run: |
          export PLAY_STORE_SERVICE_ACCOUNT_JSON=$(cat release/play-service-account.json)
          bundle exec fastlane android deploy_play_store track:beta

      - name: Distribute to Firebase App Distribution
        env:
          FIREBASE_APP_ID: ${{ secrets.FIREBASE_APP_ID }}
        run: |
          if [ -n "$FIREBASE_APP_ID" ] && [ -f "release/firebase-sa.json" ]; then
            export FIREBASE_APP_DISTRIBUTION_SA=release/firebase-sa.json
            bundle exec fastlane android distribute_firebase
          else
            echo "warning: Firebase not configured — skipping distribution"
          fi

  build-ios:
    needs: prepare
    if: ${{ !inputs.skip_ios }}
    runs-on: macos-15
    timeout-minutes: 60
    env:
      FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT: 60
    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          ref: ${{ needs.prepare.outputs.sha }}

      - name: Setup iOS Release
        uses: ./.github/actions/setup-ios-release
        with:
          xcode-version: ${{ env.XCODE_VERSION }}
          google-service-info-plist: ${{ secrets.GOOGLE_SERVICE_INFO_PLIST_RELEASE }}

      - name: Build and upload to TestFlight
        env:
          MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }}
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
          APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
          APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
          APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
        run: bundle exec fastlane ios build_beta

      - name: Upload iOS artifacts
        uses: actions/upload-artifact@v7
        if: always()
        with:
          name: ios-internal
          path: |
            build/
            fastlane/logs


================================================
FILE: .github/workflows/ci.yml
================================================
name: build

on:
    push:
        branches: [ main ]
    pull_request:
        types: [ opened, synchronize ]
    workflow_call:

concurrency:
    group: ci-${{ github.ref }}-${{ github.head_ref }}-${{ github.workflow }}
    cancel-in-progress: ${{ github.event_name == 'pull_request' }}

env:
    XCODE_VERSION: 16.4
    TMDB_API_KEY: ${{ secrets.TMDB_API_KEY }}
    TRAKT_CLIENT_ID: ${{ secrets.TRAKT_CLIENT_ID }}
    TRAKT_CLIENT_SECRET: ${{ secrets.TRAKT_CLIENT_SECRET }}
    TRAKT_REDIRECT_URI: ${{ secrets.TRAKT_REDIRECT_URI }}

jobs:
    static-analysis:
        runs-on: ubuntu-latest
        steps:
            -   uses: actions/checkout@v6

            -   name: 🐘 Setup Gradle Environment
                uses: ./.github/actions/setup-gradle

            -   name: Run Spotless
                run: ./gradlew spotlessCheck -Papp.debugOnly=false

            -   name: Lint Project
                run: ./gradlew :app:lintRelease -Papp.debugOnly=false

            -   name: Upload Lint Report
                if: ${{ !cancelled() }}
                uses: actions/upload-artifact@v7
                with:
                    name: android-lint-report
                    path: build/reports/lint/
                    if-no-files-found: ignore

            -   name: Dependency Health
                run: ./gradlew buildHealth -Papp.debugOnly=false

            -   name: Upload Dependency Health Report
                if: ${{ !cancelled() }}
                uses: actions/upload-artifact@v7
                with:
                    name: dependency-health-report
                    path: '**/build/reports/dependency-analysis/build-health-report.txt'

    swift-lint:
        runs-on: ubuntu-latest
        steps:
            -   uses: actions/checkout@v6

            -   name: 🔍 Run SwiftLint
                uses: norio-nomura/action-swiftlint@3.2.1
                with:
                    args: --config .swiftlint.yml

    build-android:
        needs: [static-analysis]
        runs-on: ubuntu-latest
        env:
            GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

        steps:
            -   name: 🚚 Checkout Code
                uses: actions/checkout@v6

            -   name: 🐘 Setup Gradle Environment
                uses: ./.github/actions/setup-gradle

            -   name: 🔥 Install google-services.json
                env:
                    GOOGLE_SERVICES_JSON: ${{ secrets.GOOGLE_SERVICES_JSON }}
                run: |
                    if [ -n "$GOOGLE_SERVICES_JSON" ]; then
                      echo "$GOOGLE_SERVICES_JSON" | base64 --decode > app/google-services.json
                    else
                      echo "warning: GOOGLE_SERVICES_JSON secret not set — Firebase will be disabled"
                    fi

            -   name: 🤖 Build Android App
                run: |
                    ./gradlew :app:assembleRelease \
                      :app:bundleRelease \
                      -Pandroidx.baselineprofile.skipgeneration=true \
                      -Pcompose.enableCompilerMetrics=true \
                      -Pcompose.enableCompilerReports=true \
                      -Papp.debugOnly=false

            -   name: Upload Compose Compiler Metrics
                if: ${{ !cancelled() }}
                uses: actions/upload-artifact@v7
                with:
                    name: compose-compiler-metrics
                    path: |
                        **/build/compose_metrics/
                        **/build/compose_reports/


    linux-test:
        needs: [static-analysis]
        runs-on: ubuntu-latest
        steps:
            -   uses: actions/checkout@v6

            -   name: 🐘 Setup Gradle Environment
                uses: ./.github/actions/setup-gradle

            -   name: Run Linux Tests
                run: ./gradlew linuxTest -Papp.debugOnly=false

            -   name: Upload Test Reports
                uses: actions/upload-artifact@v7
                if: ${{ !cancelled() }}
                with:
                    name: linux-test-report
                    path: '**/build/reports/*'

#    ios-test:
#        needs: [static-analysis]
#        runs-on: macos-15
#        environment:
#            name: ${{ (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && 'Protected' || '' }}
#        steps:
#            -   uses: actions/checkout@v6
#
#            -   name: 🐘 Setup Gradle Environment
#                uses: ./.github/actions/setup-gradle
#
#            -   name: Run iOS Tests
#                run: ./gradlew iosTest -Papp.enableIos=true -Papp.debugOnly=false
#
#            -   name: Upload Test Reports
#                uses: actions/upload-artifact@v7
#                if: ${{ !cancelled() }}
#                with:
#                    name: ios-test-report
#                    path: '**/build/reports/*'

    build-ios:
        needs: [swift-lint]
        runs-on: macos-15
        timeout-minutes: 60
        env:
            FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT: 60

        steps:
            -   name: 🚚 Checkout project
                uses: actions/checkout@v6

            -   name: 📱 Setup iOS Environment
                uses: ./.github/actions/setup-ios
                with:
                    xcode-version: ${{ env.XCODE_VERSION }}

            -   name: 🔥 Install GoogleService-Info.plist
                env:
                    GOOGLE_SERVICE_INFO: ${{ secrets.GOOGLE_SERVICE_INFO_PLIST }}
                run: |
                    if [ -n "$GOOGLE_SERVICE_INFO" ]; then
                      mkdir -p ios/ios/Firebase/Debug
                      echo "$GOOGLE_SERVICE_INFO" | base64 --decode > ios/ios/Firebase/Debug/GoogleService-Info.plist
                    else
                      echo "warning: GOOGLE_SERVICE_INFO_PLIST secret not set — Firebase will be disabled"
                    fi

            -   name: 🧱 Build iOS App
                run: bundle exec fastlane build_tvmaniac

            -   name: Upload build artifacts
                uses: actions/upload-artifact@v7
                if: ${{ !cancelled() }}
                with:
                    name: ios-build
                    path: |
                        derived_data/Build/Products/
                        fastlane/logs

    ios-snapshot-test:
        needs: [swift-lint]
        runs-on: macos-15
        timeout-minutes: 60
        env:
            FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT: 60

        steps:
            -   name: 🚚 Checkout project
                uses: actions/checkout@v6

            -   name: 📱 Setup iOS Environment
                uses: ./.github/actions/setup-ios
                with:
                    xcode-version: ${{ env.XCODE_VERSION }}

            -   name: 📸 Run Snapshot Tests
                run: bundle exec fastlane snapshot_tests

            -   name: Upload test results
                uses: actions/upload-artifact@v7
                if: ${{ !cancelled() }}
                with:
                    name: snapshot-test-results
                    path: |
                        fastlane/test_output
                        fastlane/logs
                        derived_data/Logs/Test


================================================
FILE: .github/workflows/compare-screenshot.yml
================================================
name: Compare Screenshot

on:
  pull_request:

permissions: {}

env:
  TMDB_API_KEY: ${{ secrets.TMDB_API_KEY }}
  TRAKT_CLIENT_ID: ${{ secrets.TRAKT_CLIENT_ID }}
  TRAKT_CLIENT_SECRET: ${{ secrets.TRAKT_CLIENT_SECRET }}
  TRAKT_REDIRECT_URI: ${{ secrets.TRAKT_REDIRECT_URI }}

jobs:
  compare-screenshot-test:
    runs-on: ubuntu-latest
    timeout-minutes: 20

    permissions:
      contents: write # for pushing screenshot-diff to companion branch
      actions: write # for upload-artifact
      pull-requests: write # for creating a comment on pull requests

    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          fetch-depth: 0

      - name: Setup Gradle Environment
        uses: ./.github/actions/setup-gradle

      - name: Get base commit SHA
        id: get-base-commit
        run: |
          git fetch origin ${{ github.event.pull_request.base.ref }}
          BASE_SHA=$(git merge-base origin/${{ github.event.pull_request.base.ref }} HEAD)
          echo "base_sha=$BASE_SHA" >> "$GITHUB_OUTPUT"

      - uses: dawidd6/action-download-artifact@v21
        continue-on-error: true
        with:
          name: screenshot
          workflow: store-screenshot.yml
          commit: ${{ steps.get-base-commit.outputs.base_sha }}

      - name: Compare screenshot test
        run: ./gradlew compareRoborazziDebug --stacktrace

      - name: Check for screenshot diffs
        id: check-diffs
        run: |
          if find . -type f -name "*_compare.png" | grep -q .; then
            echo "has_diffs=true" >> "$GITHUB_OUTPUT"
          else
            echo "has_diffs=false" >> "$GITHUB_OUTPUT"
          fi

      - name: Upload screenshot diff
        if: ${{ !cancelled() && steps.check-diffs.outputs.has_diffs == 'true' }}
        uses: actions/upload-artifact@v7
        with:
          name: screenshot-diff
          path: '**/build/outputs/roborazzi'
          retention-days: 30

      - name: Upload diff reports
        if: ${{ !cancelled() && steps.check-diffs.outputs.has_diffs == 'true' }}
        uses: actions/upload-artifact@v7
        with:
          name: screenshot-diff-reports
          path: '**/build/reports'
          retention-days: 30

      - name: Upload diff test results
        if: ${{ !cancelled() && steps.check-diffs.outputs.has_diffs == 'true' }}
        uses: actions/upload-artifact@v7
        with:
          name: screenshot-diff-test-results
          path: '**/build/test-results'
          retention-days: 30

      - name: Push diffs to companion branch
        id: push-screenshot-diff
        if: steps.check-diffs.outputs.has_diffs == 'true'
        env:
          BRANCH_NAME: companion_${{ github.head_ref }}
        run: |
          git branch -D "$BRANCH_NAME" || true
          git checkout --orphan "$BRANCH_NAME"
          git rm -rf .

          files_to_add=$(find . -type f -name "*_compare.png")
          for file in $files_to_add; do
            if [[ "$file" =~ ^[a-zA-Z0-9_./-]+$ ]]; then
              git add "$file"
            fi
          done

          git config --global user.name ScreenshotBot
          git config --global user.email 41898282+github-actions[bot]@users.noreply.github.com
          git commit -m "Add screenshot diff"
          git push origin HEAD:"$BRANCH_NAME" -f

      - name: Generate diff report
        id: generate-diff-reports
        if: steps.check-diffs.outputs.has_diffs == 'true'
        env:
          BRANCH_NAME: companion_${{ github.head_ref }}
        shell: bash
        run: |
          files=$(find . -type f -name "*_compare.png" | grep "roborazzi/" | grep -E "^[a-zA-Z0-9_./-]+$")
          delimiter="$(openssl rand -hex 8)"
          {
            echo "reports<<${delimiter}"
            echo "## Roborazzi Screenshot Diff Report"
            echo "Comparing against base branch: ${{ github.event.pull_request.base.ref }}"
            echo ""
            echo "| File name | Image |"
            echo "|-------|-------|"
          } >> "$GITHUB_OUTPUT"

          for file in $files; do
            fileName=$(basename "$file" | sed -r 's/(.{20})/\1<br>/g')
            echo "| [$fileName](https://github.com/${{ github.repository }}/blob/$BRANCH_NAME/$file) | ![](https://github.com/${{ github.repository }}/blob/$BRANCH_NAME/$file?raw=true) |" >> "$GITHUB_OUTPUT"
          done
          echo "${delimiter}" >> "$GITHUB_OUTPUT"

      - name: Find existing comment
        uses: peter-evans/find-comment@v4
        id: fc
        if: steps.generate-diff-reports.outputs.reports != ''
        with:
          issue-number: ${{ github.event.pull_request.number }}
          comment-author: 'github-actions[bot]'
          body-includes: Roborazzi Screenshot Diff Report

      - name: Add or update comment on PR
        uses: peter-evans/create-or-update-comment@v5
        if: steps.generate-diff-reports.outputs.reports != ''
        with:
          comment-id: ${{ steps.fc.outputs.comment-id }}
          issue-number: ${{ github.event.pull_request.number }}
          body: ${{ steps.generate-diff-reports.outputs.reports }}
          edit-mode: replace

      - name: Cleanup outdated companion branches
        if: steps.check-diffs.outputs.has_diffs == 'true'
        run: |
          git fetch origin
          git branch -r --format="%(refname:lstrip=3)" | grep companion_ | while read -r branch; do
            last_commit_date_timestamp=$(git log -1 --format=%ct "origin/$branch")
            now_timestamp=$(date +%s)
            if [ $((now_timestamp - last_commit_date_timestamp)) -gt 2592000 ]; then
              echo "Deleting outdated branch: $branch"
              git push origin --delete "$branch"
            fi
          done


================================================
FILE: .github/workflows/daily-build.yml
================================================
name: Daily Build

on:
  workflow_dispatch:
  # Uncomment to enable scheduled daily builds
  # schedule:
  #   - cron: '0 6 * * 1-5'  # Weekdays at 6:00 AM UTC

permissions:
  contents: write

env:
  XCODE_VERSION: 16.4
  TMDB_API_KEY: ${{ secrets.TMDB_API_KEY }}
  TRAKT_CLIENT_ID: ${{ secrets.TRAKT_CLIENT_ID }}
  TRAKT_CLIENT_SECRET: ${{ secrets.TRAKT_CLIENT_SECRET }}
  TRAKT_REDIRECT_URI: ${{ secrets.TRAKT_REDIRECT_URI }}

jobs:
  prepare:
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.bump.outputs.version }}
      build: ${{ steps.bump.outputs.build }}
      sha: ${{ steps.push.outputs.sha }}
    steps:
      - name: Checkout main
        uses: actions/checkout@v6
        with:
          ref: main
          fetch-depth: 0
          token: ${{ secrets.RELEASE_TOKEN }}

      - name: Setup Gradle Environment
        uses: ./.github/actions/setup-gradle

      - name: Configure git
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"

      - name: Bump build number
        id: bump
        run: |
          ./gradlew bumpVersion -Ptype=beta
          version=$(grep 'VERSION_NUMBER' version.txt | sed 's/.*= *//')
          build=$(grep 'BUILD_NUMBER' version.txt | sed 's/.*= *//')
          echo "version=$version" >> "$GITHUB_OUTPUT"
          echo "build=$build" >> "$GITHUB_OUTPUT"
          echo "Version: $version, BUILD_NUMBER: $build"

      - name: Commit and push
        id: push
        run: |
          git add version.txt
          git commit -m "chore: bump daily build number to ${{ steps.bump.outputs.build }}"
          git push origin main
          echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"

  build-android:
    needs: prepare
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          ref: ${{ needs.prepare.outputs.sha }}

      - name: Setup Android Release
        uses: ./.github/actions/setup-android-release
        with:
          google-services-json: ${{ secrets.GOOGLE_SERVICES_JSON_RELEASE }}
          signing-encrypt-key: ${{ secrets.SIGNING_ENCRYPT_KEY }}

      - name: Build release AAB and APK
        env:
          SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }}
          SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }}
        run: |
          ./gradlew :app:bundleRelease :app:assembleRelease \
            -Pandroidx.baselineprofile.skipgeneration=true \
            -Papp.debugOnly=false \
            -Papp.versionSuffix=-dev \
            -PreleaseStoreFile=release/app-release.jks \
            -PreleaseStorePassword="$SIGNING_STORE_PASSWORD" \
            -PreleaseKeyAlias=tvmaniac \
            -PreleaseKeyPassword="$SIGNING_KEY_PASSWORD" \
            --no-configuration-cache

      - name: Print build info
        run: |
          echo "Version: $(grep 'VERSION_NUMBER' version.txt | sed 's/.*= *//')-dev"
          echo "Build:   $(grep 'BUILD_NUMBER' version.txt | sed 's/.*= *//')"

      - name: Deploy to Play Store internal track
        run: |
          export PLAY_STORE_SERVICE_ACCOUNT_JSON=$(cat release/play-service-account.json)
          bundle exec fastlane android deploy_play_store

      - name: Distribute to Firebase App Distribution
        env:
          FIREBASE_APP_ID: ${{ secrets.FIREBASE_APP_ID }}
        run: |
          if [ -n "$FIREBASE_APP_ID" ] && [ -f "release/firebase-sa.json" ]; then
            export FIREBASE_APP_DISTRIBUTION_SA=release/firebase-sa.json
            bundle exec fastlane android distribute_firebase
          else
            echo "warning: Firebase not configured — skipping distribution"
          fi

  build-ios:
    needs: prepare
    runs-on: macos-15
    timeout-minutes: 60
    env:
      FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT: 60
    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          ref: ${{ needs.prepare.outputs.sha }}

      - name: Setup iOS Release
        uses: ./.github/actions/setup-ios-release
        with:
          xcode-version: ${{ env.XCODE_VERSION }}
          google-service-info-plist: ${{ secrets.GOOGLE_SERVICE_INFO_PLIST_RELEASE }}

      - name: Build and upload to TestFlight
        env:
          MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }}
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
          APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
          APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
          APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
        run: bundle exec fastlane ios build_beta

      - name: Upload iOS artifacts
        uses: actions/upload-artifact@v7
        if: always()
        with:
          name: ios-daily
          path: |
            build/
            fastlane/logs


================================================
FILE: .github/workflows/nightly-integration-tests.yml
================================================
name: Nightly Integration Tests

on:
  schedule:
    - cron: '30 1 * * *'
  workflow_dispatch:
  workflow_call:

concurrency:
  group: nightly-integration-tests-${{ github.ref }}
  cancel-in-progress: false

env:
  TMDB_API_KEY: ${{ secrets.TMDB_API_KEY }}
  TRAKT_CLIENT_ID: ${{ secrets.TRAKT_CLIENT_ID }}
  TRAKT_CLIENT_SECRET: ${{ secrets.TRAKT_CLIENT_SECRET }}
  TRAKT_REDIRECT_URI: ${{ secrets.TRAKT_REDIRECT_URI }}

jobs:
  integration-tests:
    name: "Pixel 6 API 34 Integration Tests"
    runs-on: ubuntu-latest
    timeout-minutes: 90

    steps:
      - name: 🚚 Checkout
        uses: actions/checkout@v6

      - name: Enable KVM
        run: |
          echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
          sudo udevadm control --reload-rules
          sudo udevadm trigger --name-match=kvm

      - name: 🐘 Setup Gradle Environment
        uses: ./.github/actions/setup-gradle

      - name: Setup Android SDK
        uses: android-actions/setup-android@v4

      - name: Accept licenses
        run: yes | sdkmanager --licenses || true

      - name: 📦 Cache AVD
        uses: actions/cache@v5
        with:
          path: |
            ~/.android/avd/*
            ~/.android/adb*
            ~/.android/debug.keystore
          key: avd-pixel6Api34-${{ hashFiles('gradle/libs.versions.toml') }}
          restore-keys: |
            avd-pixel6Api34-

      - name: 🔥 Install google-services.json
        env:
          GOOGLE_SERVICES_JSON: ${{ secrets.GOOGLE_SERVICES_JSON }}
        run: |
          if [ -n "$GOOGLE_SERVICES_JSON" ]; then
            echo "$GOOGLE_SERVICES_JSON" | base64 --decode > app/google-services.json
          else
            echo "warning: GOOGLE_SERVICES_JSON secret not set — Firebase will be disabled"
          fi

      - name: 🤖 Run Integration Tests on Emulator
        run: |
          ./gradlew :app:pixel6Api34DebugAndroidTest \
            -Pandroid.testoptions.manageddevices.emulator.gpu="swiftshader_indirect" \
            --no-configuration-cache

      - name: 📊 Upload Test Reports
        if: ${{ !cancelled() }}
        uses: actions/upload-artifact@v7
        with:
          name: integration-test-reports
          path: |
            app/build/reports/androidTests/
            app/build/outputs/androidTest-results/
            app/build/outputs/managed_device_android_test_additional_output/
          if-no-files-found: ignore


================================================
FILE: .github/workflows/promote-release.yml
================================================
name: Promote Release

on:
  # schedule:
  #   - cron: '0 5 * * *'
  workflow_dispatch:
    inputs:
      android_rollout:
        description: 'Android rollout percentage override (e.g., 0.5 for 50%)'
        type: string
        required: false
      skip_ios:
        description: 'Skip iOS App Store submission'
        type: boolean
        default: false
      crash_free_threshold:
        description: 'Minimum crash-free rate (%) to allow promotion'
        type: string
        default: '99.0'

permissions:
  contents: read

env:
  TMDB_API_KEY: ${{ secrets.TMDB_API_KEY }}
  TRAKT_CLIENT_ID: ${{ secrets.TRAKT_CLIENT_ID }}
  TRAKT_CLIENT_SECRET: ${{ secrets.TRAKT_CLIENT_SECRET }}
  TRAKT_REDIRECT_URI: ${{ secrets.TRAKT_REDIRECT_URI }}

jobs:
  check-rollout:
    runs-on: ubuntu-latest
    outputs:
      should_promote: ${{ steps.check.outputs.should_promote }}
      next_rollout: ${{ steps.check.outputs.next_rollout }}
      current_rollout: ${{ steps.check.outputs.current_rollout }}
      release_tag: ${{ steps.check.outputs.release_tag }}
      days_since_release: ${{ steps.check.outputs.days_since_release }}
    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          fetch-depth: 0

      - name: Determine rollout state
        id: check
        run: |
          if [ -n "${{ inputs.android_rollout }}" ]; then
            echo "should_promote=true" >> "$GITHUB_OUTPUT"
            echo "next_rollout=${{ inputs.android_rollout }}" >> "$GITHUB_OUTPUT"
            echo "current_rollout=manual" >> "$GITHUB_OUTPUT"
            echo "days_since_release=manual" >> "$GITHUB_OUTPUT"
            latest_tag=$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-version:refname | head -1)
            echo "release_tag=$latest_tag" >> "$GITHUB_OUTPUT"
            exit 0
          fi

          latest_tag=$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-version:refname | head -1)
          if [ -z "$latest_tag" ]; then
            echo "No release tags found"
            echo "should_promote=false" >> "$GITHUB_OUTPUT"
            exit 0
          fi

          tag_epoch=$(git log -1 --format=%ct "$latest_tag")
          now_epoch=$(date +%s)
          days_since=$(( (now_epoch - tag_epoch) / 86400 ))

          # Rollout schedule: Day 0=0.1%, Day 1=1%, Day 3=10%, Day 5=50%, Day 7=100%
          if [ "$days_since" -ge 7 ]; then
            next_rollout="1.0"
          elif [ "$days_since" -ge 5 ]; then
            next_rollout="0.5"
          elif [ "$days_since" -ge 3 ]; then
            next_rollout="0.1"
          elif [ "$days_since" -ge 1 ]; then
            next_rollout="0.01"
          else
            echo "Day 0: release just deployed at 0.1%, no promotion needed yet"
            echo "should_promote=false" >> "$GITHUB_OUTPUT"
            exit 0
          fi

          echo "should_promote=true" >> "$GITHUB_OUTPUT"
          echo "next_rollout=$next_rollout" >> "$GITHUB_OUTPUT"
          echo "days_since_release=$days_since" >> "$GITHUB_OUTPUT"
          echo "release_tag=$latest_tag" >> "$GITHUB_OUTPUT"
          echo "Release: $latest_tag, Days since: $days_since, Next rollout: $next_rollout"

  check-vitals:
    needs: check-rollout
    if: ${{ needs.check-rollout.outputs.should_promote == 'true' }}
    runs-on: ubuntu-latest
    outputs:
      passed: ${{ steps.vitals.outputs.passed }}
    steps:
      - name: Checkout
        uses: actions/checkout@v6

      - name: Decrypt Play Store service account
        env:
          SIGNING_ENCRYPT_KEY: ${{ secrets.SIGNING_ENCRYPT_KEY }}
        run: |
          openssl aes-256-cbc -d -pbkdf2 \
            -in release/play-service-account.aes \
            -out release/play-service-account.json \
            -k "$SIGNING_ENCRYPT_KEY"

      - name: Check Play Vitals crash-free Rate
        id: vitals
        run: |
          THRESHOLD="${{ inputs.crash_free_threshold }}"
          THRESHOLD="${THRESHOLD:-99.0}"
          APP_PACKAGE="com.thomaskioko.tvmaniac"

          if ! gcloud auth activate-service-account --key-file=release/play-service-account.json 2>/dev/null; then
            echo "::warning::Could not authenticate — skipping vitals check"
            echo "passed=true" >> "$GITHUB_OUTPUT"
            exit 0
          fi

          ACCESS_TOKEN=$(gcloud auth print-access-token 2>/dev/null || true)
          if [ -z "$ACCESS_TOKEN" ]; then
            echo "::warning::Could not obtain access token — skipping vitals check"
            echo "passed=true" >> "$GITHUB_OUTPUT"
            exit 0
          fi

          END_DATE=$(date -u +%Y-%m-%d)
          START_DATE=$(date -u -d "7 days ago" +%Y-%m-%d)

          RESPONSE=$(curl -sf \
            -H "Authorization: Bearer $ACCESS_TOKEN" \
            -H "Content-Type: application/json" \
            -X POST \
            "https://playdeveloperreporting.googleapis.com/v1beta1/apps/${APP_PACKAGE}/crashRateMetricSet:query" \
            -d "{
              \"timelineSpec\": {
                \"aggregationPeriod\": \"DAILY\",
                \"startTime\": {
                  \"year\": $(date -u -d "$START_DATE" +%Y),
                  \"month\": $(date -u -d "$START_DATE" +%-m),
                  \"day\": $(date -u -d "$START_DATE" +%-d)
                },
                \"endTime\": {
                  \"year\": $(date -u -d "$END_DATE" +%Y),
                  \"month\": $(date -u -d "$END_DATE" +%-m),
                  \"day\": $(date -u -d "$END_DATE" +%-d)
                }
              },
              \"metrics\": [\"crashRate\"],
              \"dimensions\": []
            }" 2>/dev/null) || true

          if [ -z "$RESPONSE" ]; then
            echo "::warning::Play Vitals API unavailable — skipping crash check"
            echo "::warning::To enable: activate the Play Developer Reporting API and grant the service account 'View app information' permission"
            echo "passed=true" >> "$GITHUB_OUTPUT"
            exit 0
          fi

          CRASH_RATE=$(echo "$RESPONSE" | jq -r '[.rows[].metrics[0].decimalValue.value // "0"] | last' 2>/dev/null || echo "")

          if [ -z "$CRASH_RATE" ] || [ "$CRASH_RATE" = "null" ]; then
            echo "::warning::Could not parse crash rate — skipping check"
            echo "passed=true" >> "$GITHUB_OUTPUT"
            exit 0
          fi

          CRASH_FREE=$(echo "scale=2; (1 - $CRASH_RATE) * 100" | bc -l)
          echo "Crash-free rate: ${CRASH_FREE}% (threshold: ${THRESHOLD}%)"
          echo "crash_free_rate=${CRASH_FREE}" >> "$GITHUB_OUTPUT"

          if (( $(echo "$CRASH_FREE < $THRESHOLD" | bc -l) )); then
            echo "::error::Crash-free rate ${CRASH_FREE}% is below threshold ${THRESHOLD}% — blocking promotion"
            echo "passed=false" >> "$GITHUB_OUTPUT"
          else
            echo "passed=true" >> "$GITHUB_OUTPUT"
          fi

  promote-android:
    needs: [check-rollout, check-vitals]
    if: ${{ needs.check-rollout.outputs.should_promote == 'true' && needs.check-vitals.outputs.passed == 'true' }}
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: Checkout
        uses: actions/checkout@v6

      - name: Setup Ruby
        uses: ruby/setup-ruby@v1
        with:
          bundler-cache: true

      - name: Decrypt Play Store service account
        env:
          SIGNING_ENCRYPT_KEY: ${{ secrets.SIGNING_ENCRYPT_KEY }}
        run: |
          openssl aes-256-cbc -d -pbkdf2 -in release/play-service-account.aes -out release/play-service-account.json -k "$SIGNING_ENCRYPT_KEY"

      - name: Promote Android release
        run: |
          export PLAY_STORE_SERVICE_ACCOUNT_JSON=$(cat release/play-service-account.json)
          bundle exec fastlane android promote \
            from:production \
            to:production \
            rollout:${{ needs.check-rollout.outputs.next_rollout }}

      - name: Summary
        run: |
          echo "### Android Promotion" >> $GITHUB_STEP_SUMMARY
          echo "- **Release**: ${{ needs.check-rollout.outputs.release_tag }}" >> $GITHUB_STEP_SUMMARY
          echo "- **Days since release**: ${{ needs.check-rollout.outputs.days_since_release }}" >> $GITHUB_STEP_SUMMARY
          echo "- **Rollout**: ${{ needs.check-rollout.outputs.next_rollout }}" >> $GITHUB_STEP_SUMMARY
          echo "- **Vitals**: crash-free ${{ needs.check-vitals.outputs.crash_free_rate || 'N/A' }}%" >> $GITHUB_STEP_SUMMARY

  promote-ios:
    needs: [check-rollout, check-vitals]
    if: ${{ needs.check-rollout.outputs.should_promote == 'true' && !inputs.skip_ios }}
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: Checkout
        uses: actions/checkout@v6

      - name: Setup Ruby
        uses: ruby/setup-ruby@v1
        with:
          bundler-cache: true

      - name: Submit to App Store
        env:
          APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
          APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
          APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
          IOS_SUBMIT_FOR_REVIEW: "true"
        run: bundle exec fastlane ios deploy_app_store

      - name: Summary
        run: |
          echo "### iOS Promotion" >> $GITHUB_STEP_SUMMARY
          echo "- **Release**: ${{ needs.check-rollout.outputs.release_tag }}" >> $GITHUB_STEP_SUMMARY
          echo "- **Action**: Submitted for App Store review" >> $GITHUB_STEP_SUMMARY


================================================
FILE: .github/workflows/release.yml
================================================
name: Release

on:
  push:
    tags:
      - 'v[0-9]+.[0-9]+.[0-9]+'

permissions:
  contents: write

env:
  XCODE_VERSION: 16.4
  TMDB_API_KEY: ${{ secrets.TMDB_API_KEY }}
  TRAKT_CLIENT_ID: ${{ secrets.TRAKT_CLIENT_ID }}
  TRAKT_CLIENT_SECRET: ${{ secrets.TRAKT_CLIENT_SECRET }}
  TRAKT_REDIRECT_URI: ${{ secrets.TRAKT_REDIRECT_URI }}

jobs:
  prepare:
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.info.outputs.version }}
      changelog: ${{ steps.changelog.outputs.body }}
    steps:
      - name: Checkout tag
        uses: actions/checkout@v6
        with:
          fetch-depth: 0

      - name: Setup Gradle Environment
        uses: ./.github/actions/setup-gradle

      - name: Read version info
        id: info
        run: |
          version=$(grep 'VERSION_NUMBER' version.txt | sed 's/.*= *//')
          build=$(grep 'BUILD_NUMBER' version.txt | sed 's/.*= *//')
          echo "version=$version" >> "$GITHUB_OUTPUT"
          echo "build=$build" >> "$GITHUB_OUTPUT"
          echo "Version: $version, Build: $build, Tag: ${{ github.ref_name }}"

      - name: Validate tag matches version.txt
        run: |
          tag="${{ github.ref_name }}"
          version=$(grep 'VERSION_NUMBER' version.txt | sed 's/.*= *//')
          expected_tag="v${version}"
          if [ "$tag" != "$expected_tag" ]; then
            echo "::error::Tag '$tag' does not match version.txt version '$expected_tag'"
            exit 1
          fi

      - name: Generate release notes
        id: changelog
        run: |
          body=$(git cliff --current --strip header 2>/dev/null || echo "Release ${{ github.ref_name }}")
          {
            echo 'body<<CHANGELOG_EOF'
            echo "$body"
            echo 'CHANGELOG_EOF'
          } >> "$GITHUB_OUTPUT"

  build-android:
    needs: prepare
    runs-on: ubuntu-latest
    steps:
      - name: Checkout tag
        uses: actions/checkout@v6
        with:
          fetch-depth: 0

      - name: Setup Android Release
        uses: ./.github/actions/setup-android-release
        with:
          google-services-json: ${{ secrets.GOOGLE_SERVICES_JSON_RELEASE }}
          signing-encrypt-key: ${{ secrets.SIGNING_ENCRYPT_KEY }}

      - name: Build release AAB and APK
        env:
          SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }}
          SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }}
        run: |
          ./gradlew :app:bundleRelease :app:assembleRelease \
            -Pandroidx.baselineprofile.skipgeneration=true \
            -Papp.debugOnly=false \
            -Papp.versionSuffix= \
            -PreleaseStoreFile=release/app-release.jks \
            -PreleaseStorePassword="$SIGNING_STORE_PASSWORD" \
            -PreleaseKeyAlias=tvmaniac \
            -PreleaseKeyPassword="$SIGNING_KEY_PASSWORD" \
            --no-configuration-cache

      - name: Write release notes
        env:
          RELEASE_CHANGELOG: ${{ needs.prepare.outputs.changelog }}
        run: |
          mkdir -p fastlane/metadata/android/en-US/changelogs
          echo "$RELEASE_CHANGELOG" > fastlane/metadata/android/en-US/changelogs/default.txt

      - name: Deploy to Play Store production (0.1% rollout)
        run: |
          export PLAY_STORE_SERVICE_ACCOUNT_JSON=$(cat release/play-service-account.json)
          bundle exec fastlane android deploy_production rollout:0.001

      - name: Distribute to Firebase App Distribution
        env:
          FIREBASE_APP_ID: ${{ secrets.FIREBASE_APP_ID }}
        run: |
          if [ -n "$FIREBASE_APP_ID" ] && [ -f "release/firebase-sa.json" ]; then
            export FIREBASE_APP_DISTRIBUTION_SA=release/firebase-sa.json
            bundle exec fastlane android distribute_firebase
          else
            echo "warning: Firebase not configured - skipping distribution"
          fi

      - name: Upload Android artifacts
        uses: actions/upload-artifact@v7
        with:
          name: android-release
          path: |
            app/build/outputs/bundle/release/*.aab
            app/build/outputs/apk/release/*.apk

  build-ios:
    needs: prepare
    runs-on: macos-15
    timeout-minutes: 60
    env:
      FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT: 60
    steps:
      - name: Checkout tag
        uses: actions/checkout@v6

      - name: Setup iOS Release
        uses: ./.github/actions/setup-ios-release
        with:
          xcode-version: ${{ env.XCODE_VERSION }}
          google-service-info-plist: ${{ secrets.GOOGLE_SERVICE_INFO_PLIST_RELEASE }}

      - name: Build and upload to TestFlight
        env:
          MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }}
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
          APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
          APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
          APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
        run: bundle exec fastlane ios build_release

      - name: Upload iOS artifacts
        uses: actions/upload-artifact@v7
        if: always()
        with:
          name: ios-release
          path: |
            build/
            fastlane/logs

  create-github-release:
    needs: [prepare, build-android, build-ios]
    if: ${{ always() && (needs.build-android.result == 'success' || needs.build-ios.result == 'success') }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v6

      - name: Download Android artifacts
        if: ${{ needs.build-android.result == 'success' }}
        uses: actions/download-artifact@v8
        with:
          name: android-release
          path: artifacts/android

      - name: Create GitHub Release
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          tag="${{ github.ref_name }}"
          version="${tag#v}"

          release_args=(
            "$tag"
            --title "Release $version"
            --notes "${{ needs.prepare.outputs.changelog }}"
            --draft
          )

          if [ -d "artifacts/android" ]; then
            apk=$(find artifacts/android -name "*.apk" -type f | head -1)
            if [ -n "$apk" ]; then
              release_args+=("$apk")
            fi
          fi

          gh release create "${release_args[@]}"


================================================
FILE: .github/workflows/store-screenshot.yml
================================================
name: Store Screenshot

on:
  push:
    branches:
      - main

permissions: {}

env:
  TMDB_API_KEY: ${{ secrets.TMDB_API_KEY }}
  TRAKT_CLIENT_ID: ${{ secrets.TRAKT_CLIENT_ID }}
  TRAKT_CLIENT_SECRET: ${{ secrets.TRAKT_CLIENT_SECRET }}
  TRAKT_REDIRECT_URI: ${{ secrets.TRAKT_REDIRECT_URI }}

jobs:
  store-screenshot-test:
    runs-on: ubuntu-latest
    timeout-minutes: 20

    permissions:
      contents: read # for clone
      actions: write # for upload-artifact

    steps:
      - name: Checkout
        uses: actions/checkout@v6

      - name: Setup Gradle Environment
        uses: ./.github/actions/setup-gradle

      - name: Record screenshot
        id: record-test
        run: |
          # Use --rerun-tasks to disable cache for test task
          ./gradlew recordRoborazziDebug --stacktrace --rerun-tasks

      - uses: actions/upload-artifact@v7
        if: ${{ always() }}
        with:
          name: screenshot
          path: |
            **/build/outputs/roborazzi
          retention-days: 30

      - uses: actions/upload-artifact@v7
        if: ${{ always() }}
        with:
          name: screenshot-reports
          path: |
            **/build/reports
          retention-days: 30

      - uses: actions/upload-artifact@v7
        if: ${{ always() }}
        with:
          name: screenshot-test-results
          path: |
            **/build/test-results
          retention-days: 30


================================================
FILE: .gitignore
================================================
# Built application files
*.apk
*.ap_

# Files for the ART/Dalvik VM
*.dex

# Java class files
*.class

# Generated files
bin/
gen/
out/

# Gradle files
.gradle/
build/
.kotlin/
/build

# Local configuration file (sdk path, etc)
local.properties

# Proguard folder generated by Eclipse
proguard/

# Log Files
*.log

# Android Studio Navigation editor temp files
.navigation/

# Android Studio captures folder
captures/

# Android Studio
*.iws
*.ipr
*.iml
.gradle
/local.properties
.DS_Store
/build
/captures
.externalNativeBuild
.cxx

/.idea/*
!/.idea/codeStyles
!/.idea/dictionaries

# Eclipse project files
.classpath
.project

## Playgrounds
timeline.xctimeline
playground.xcworkspace

## Shared
/shared/domain/settings/implementation/*.preferences_pb

*.xcworkspacedata
*.xcuserstate
xcschememanagement.plist
*.xcbkptlist

## User settings
xcuserdata/

## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
DerivedData/
*.moved-aside
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3

*.xcuserstate

###### FastLane #######
fastlane/report.xml
fastlane/Preview.html
fastlane/test_output
fastlane/builds
/ios/fastlane/report.xml
ios/build
ios/SourcePackages/

derived_data/
*.xcresult

/.junie/guidelines.md
/tasks
CLAUDE.md
.claude/

# Configuration files with secrets
**/dev.yaml
**/prod.yaml
!**/*.yaml.template
**/.build/
**/Package.resolved

# Firebase
google-services.json
GoogleService-Info.plist

# Release signing
release/app-release.jks
release/play-service-account.json
release/firebase-sa.json
release/signing.properties


================================================
FILE: .idea/codeStyles/Project.xml
================================================
<component name="ProjectCodeStyleConfiguration">
  <code_scheme name="Project" version="173">
    <JavaCodeStyleSettings>
      <option name="IMPORT_LAYOUT_TABLE">
        <value>
          <package name="" withSubpackages="true" static="false" module="true" />
          <package name="android" withSubpackages="true" static="true" />
          <package name="androidx" withSubpackages="true" static="true" />
          <package name="com" withSubpackages="true" static="true" />
          <package name="junit" withSubpackages="true" static="true" />
          <package name="net" withSubpackages="true" static="true" />
          <package name="org" withSubpackages="true" static="true" />
          <package name="java" withSubpackages="true" static="true" />
          <package name="javax" withSubpackages="true" static="true" />
          <package name="" withSubpackages="true" static="true" />
          <emptyLine />
          <package name="android" withSubpackages="true" static="false" />
          <emptyLine />
          <package name="androidx" withSubpackages="true" static="false" />
          <emptyLine />
          <package name="com" withSubpackages="true" static="false" />
          <emptyLine />
          <package name="junit" withSubpackages="true" static="false" />
          <emptyLine />
          <package name="net" withSubpackages="true" static="false" />
          <emptyLine />
          <package name="org" withSubpackages="true" static="false" />
          <emptyLine />
          <package name="java" withSubpackages="true" static="false" />
          <emptyLine />
          <package name="javax" withSubpackages="true" static="false" />
          <emptyLine />
          <package name="" withSubpackages="true" static="false" />
          <emptyLine />
        </value>
      </option>
    </JavaCodeStyleSettings>
    <JetCodeStyleSettings>
      <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
    </JetCodeStyleSettings>
    <codeStyleSettings language="XML">
      <option name="FORCE_REARRANGE_MODE" value="1" />
      <indentOptions>
        <option name="CONTINUATION_INDENT_SIZE" value="4" />
      </indentOptions>
      <arrangement>
        <rules>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>xmlns:android</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>^$</XML_NAMESPACE>
                </AND>
              </match>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>xmlns:.*</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>^$</XML_NAMESPACE>
                </AND>
              </match>
              <order>BY_NAME</order>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>.*:id</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
                </AND>
              </match>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>.*:name</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
                </AND>
              </match>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>name</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>^$</XML_NAMESPACE>
                </AND>
              </match>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>style</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>^$</XML_NAMESPACE>
                </AND>
              </match>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>.*</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>^$</XML_NAMESPACE>
                </AND>
              </match>
              <order>BY_NAME</order>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>.*</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
                </AND>
              </match>
              <order>ANDROID_ATTRIBUTE_ORDER</order>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>.*</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>.*</XML_NAMESPACE>
                </AND>
              </match>
              <order>BY_NAME</order>
            </rule>
          </section>
        </rules>
      </arrangement>
    </codeStyleSettings>
    <codeStyleSettings language="kotlin">
      <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
    </codeStyleSettings>
  </code_scheme>
</component>

================================================
FILE: .idea/codeStyles/codeStyleConfig.xml
================================================
<component name="ProjectCodeStyleConfiguration">
  <state>
    <option name="PREFERRED_PROJECT_CODE_STYLE" value="tv-maniac" />
  </state>
</component>

================================================
FILE: .idea/dictionaries/project.xml
================================================
<component name="ProjectDictionaryState">
  <dictionary name="project">
    <words>
      <w>tmdb</w>
      <w>trakt</w>
    </words>
  </dictionary>
</component>


================================================
FILE: .ruby-version
================================================
3.3.0


================================================
FILE: .swiftformat
================================================
--indent 4
--exclude ios/build,ios/Modules/*/.build,**/.build,**/build,**/ios-framework/build,ios/SourcePackages,ios/derived_data,**/SourcePackages,**/derived_data,**/checkouts


================================================
FILE: .swiftlint.yml
================================================
cyclomatic_complexity:
    warning: 15
    error: 20

function_body_length:
    warning: 75
    error: 100

disabled_rules:
    - todo
    - trailing_whitespace
    - trailing_comma
    - trailing_newline
    - identifier_name
    - type_name
    - multiple_closures_with_trailing_closure
    - type_body_length
    - line_length
    - opening_brace
    - leading_whitespace
    - void_function_in_ternary

opt_in_rules:
    - sorted_imports


# Files & Folder not to check
excluded:
    - .build
    - "**/build"
    - "**/.build"
    - "**/checkouts"
    - "**/SourcePackages"
    - derived_data
    - DerivedData
    - ios/Modules/SnapshotTestingLib
    - ios/build
    - vendor/bundle
    - ios-framework/build


================================================
FILE: CHANGELOG.md
================================================
# Changelog

All notable changes to this project will be documented in this file.

## [0.1.2] - 2026-03-25

### Bug Fixes

- **Android**: Fix SnackBar shown in error state.
- **auth**: Update Trakt token endpoint and client authentication method
- update Firebase distribution path to use release APK
- fix linting
### CI/CD

- Ignore iOS tests in watchlist module
### Features

- **i18n**: Add new strings for marking episodes
- **ios**: Configure background processing and fetch modes
- enable experimental R8 optimized resource shrinking
- enable mapping file upload for Firebase Crashlytics in release build
- Add query to get the latest season for followed shows
- Add episode notification scheduling tasks
- Add 'Auto' image quality setting and optimize image loading
- Add `season_numbers` to `TvShow` SQL queries
- Refactor theme implementation in settings
### Miscellaneous

- update release workflow to use release-specific secrets and add signing files to .gitignore
- add encrypted signing keys


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to TvManiac

TvManiac is a personal learning playground for Kotlin Multiplatform development. Contributions that fix bugs,
improve documentation, or demonstrate KMP patterns are welcome.

## Prerequisites

| Tool | Version |
|---|---|
| JDK | 21 (Zulu recommended: https://www.azul.com/downloads/?package=jdk#zulu) |
| Android Studio | Latest stable or canary |
| Xcode | 16.4 (iOS contributions) |
| KMM Plugin | Latest compatible with the Kotlin version in `gradle/libs.versions.toml` |

CocoaPods is not used. The iOS app consumes the shared KMP framework as an XCFramework built by Gradle.

## Cloning and Initial Setup

```bash
git clone https://github.com/thomaskioko/tv-maniac.git
cd tv-maniac
./scripts/install-git-hooks.sh
```

The git hooks run Spotless formatting checks before each commit. A commit is blocked if formatting fails.
Fix formatting with `./gradlew spotlessApply` and then re-commit.

### API Keys

The app requires TMDB and Trakt credentials at build time. See [docs/setup.md](docs/setup.md) for how to
obtain them. Create `local.properties` in the project root:

```properties
TMDB_API_KEY=your_tmdb_api_key
TRAKT_CLIENT_ID=your_trakt_client_id
TRAKT_CLIENT_SECRET=your_trakt_client_secret
TRAKT_REDIRECT_URI=tvmaniac://callback
```

This file is gitignored. Do not commit it.

## Building

**Android (debug):**

```bash
./gradlew :app:assembleDebug
```

For a faster local build that skips the iOS XCFramework:

```bash
./gradlew assembleDebug -Ptvmaniac.debugOnly=true
```

**iOS:**

Open `ios/tv-maniac.xcodeproj` in Xcode 16.4, select a simulator or device, and run.

## Running Tests

```bash
# JVM unit tests (fast, no device needed)
./gradlew jvmTest

# Android connected tests (requires a running emulator or device)
./gradlew connectedAndroidTest

# iOS simulator tests
./gradlew iosSimulatorArm64Test -Papp.enableIos=true
```

## Code Style

**Kotlin:**

- 2-space indentation, 140-character line length.
- Use `ImmutableList` and `toImmutableList()` from `kotlinx.collections.immutable` for state classes.
- No try-catch blocks that silently swallow errors. Propagate exceptions up to the presentation layer.
- Fakes, not mocks. Each `data/*/testing` module provides fake implementations for tests.
- Test names follow the pattern: `should X given Y`. Do not include function names in test names.
- Spotless enforces formatting. Run `./gradlew spotlessApply` before pushing.

**Swift:**

- Follow the Swift API Design Guidelines.
- Format with SwiftFormat. Check with `fastlane ios check_swift_format` before pushing.
- Do not add business logic to SwiftUI views. Views consume shared KMP presenters only.

**General:**

- No comments in code unless the intent cannot be expressed through naming.
- Always specify access modifiers on all Kotlin declarations.
- Business logic belongs in shared KMP code (`domain/*`, `data/*`), never in platform UI layers.

## Branching and Pull Requests

1. Fork the repository and create your branch from `main`.
2. Keep branches focused on a single concern. Large refactors should be discussed in an issue first.
3. Ensure CI passes: Spotless, unit tests, and lint checks must all be green.
4. Include a clear description in your PR: what changed, why, and how to test it.
5. Reference any related issue with `Closes #123` or `Relates to #123` in the PR body.

PRs that add new architectural patterns should also update the relevant file under `docs/architecture/`.

## Architecture Overview

Before contributing, read the architecture docs so your change fits the existing patterns:

- [Modularization](docs/architecture/modularization.md)
- [Presentation Layer](docs/architecture/presentation-layer.md)
- [Data Layer](docs/architecture/data-layer.md)
- [Navigation](docs/architecture/navigation.md)
- [Dependency Injection](docs/architecture/dependency-injection.md)

## Filing Bugs and Questions

Open an issue on GitHub. Use the bug report template for reproducible defects and the feature request
template for new ideas. For open-ended questions, open a GitHub issue with the `question` label.


================================================
FILE: GEMINI.md
================================================
# TV Maniac Agent Rules

## Project Overview
TV Maniac is a Kotlin Multiplatform (KMP) project for tracking TV shows. It follows a highly modularized Clean Architecture with a strict separation of concerns.

## Architecture & Tech Stack
- **KMP**: Shared business logic, state management, and data layer.
- **Clean Architecture**: Organized into `data`, `domain`, `presenter`, and `ui` layers.
- **Metro**: A custom compile-time Dependency Injection (DI) system.
- **Decompose**: Used for shared navigation and lifecycle management.
- **Store 5**: Used for data fetching (Fetcher) and caching (SourceOfTruth).
- **SQLDelight**: Local persistence.
- **UI**: Jetpack Compose for Android, SwiftUI for iOS.

## Core Mandates & Conventions

### 1. Modularization & Dependencies
- **Strict API/Implementation Split**: Most modules are split into `:api` and `:implementation` (or implicit).
- **Rule**: Modules MUST depend on `api` modules of other features, never `implementation` (except for entry points like `:app` and `:ios-framework`).
- **Feature Structure**: Features follow a 3-module split:
  - `nav`: Contains routes, parameters, and navigation-related DI.
  - `presenter`: Contains business logic, state management (MVI), and domain interactors.
  - `ui`: Contains platform-specific UI (Compose).

### 2. Dependency Injection (Metro)
- Use `@DependencyGraph`, `@GraphExtension`, and `@BindingContainer` for DI.
- **Naming**:
  - DI interfaces MUST use the `*Graph` suffix.
  - Binding providers MUST use the `*BindingContainer` suffix.
- **Scopes**:
  - `AppScope`: Singleton/Application lifetime.
  - `ActivityScope`: Activity lifetime.
  - `ScreenScope`: Decompose component lifetime.

### 3. Navigation (Decompose)
- Use `NavRoute` for standard navigation and `SheetConfig` for bottom sheets.
- **Annotations**:
  - `@NavScreen`: Annotate presenters for standard screens.
  - `@NavSheet`: Annotate presenters for bottom sheets.
  - `@TabScreen`: Annotate presenters for tab screens.
- **Codegen**: Navigation is largely handled via codegen based on these annotations.

### 4. Presentation Layer (MVI)
- **Presenters**:
  - MUST use `@AssistedInject` and have an `@AssistedFactory`.
  - MUST extend `ComponentContext` (via delegation).
  - MUST expose state as `Value<State>` (for Decompose) or `StateFlow<State>`.
  - MUST use a `dispatch(Action)` function for UI events.
- **Naming**: `*Presenter`, `*Action`, `*Content` (for state).
- **Standardized Helpers**:
  - Use `ObservableLoadingCounter` for tracking loading states.
  - Use `UiMessageManager` for managing transient UI messages (errors, snackbars).
  - Use `.collectStatus()` extension to handle flow statuses and pipe errors/loading to the above managers.

### 5. Data Layer (Store)
- Use `Store 5` for fetching and caching.
- `Fetcher`: For network requests (Ktor).
- `SourceOfTruth`: For local caching (SQLDelight).
- `Validator`: To determine if cached data is still valid.

### 6. Localization
- **Rule**: Never use hardcoded strings or platform-specific string resources in shared code.
- Use `Localizer` interface for shared string resolution.
- Use `MR` (MOKO Resources) for string and plural definitions.
- In Compose UI, use `resource.resolve(LocalContext.current)` or similar helpers.

### 7. Coding Style
- **Naming**: Use descriptive names. Suffix classes with their role (e.g., `Interactor`, `Repository`, `Presenter`).
- **Types**: Prefer explicit types for public APIs.
- **Immutability**: Use `kotlinx.collections.immutable` for state collections.

### 8. Testing
- **Prefer Fakes over Mocks**: Use hand-written fakes for repositories and interactors.
- **Testing Modules**: Fakes should reside in a `:testing` module corresponding to the feature or layer.
- **Navigator**: Use `TestNavigator` for asserting navigation events in presenter tests.
- **Turbine**: Use Turbine for testing flows.

### 9. Integration Testing
- **Location**: Integration tests reside in the `:core:integration` module.
- **TestGraph**: Use `TestGraph` to resolve dependencies. It provides a real dependency graph with specific components replaced by fakes (e.g., Network, DataStore).
- **Execution**:
    - Use `runTestWithGraph { graph -> ... }` to run tests. This helper handles setting up the `TestGraph` and the `TestDispatcher`.
    - Ensure `Lifecycle.destroy()` is called and `advanceUntilIdle()` is used when testing presenters to prevent coroutine leaks.
- **Platform Specifics**: Metro's graph factories are materialized per target. JVM and iOS variants of helpers must live in their respective source sets.
- **Android**: Use `androidHostTest` for running integration tests on the JVM while having access to Android resources if needed.

## Development Workflow
1. **Feature Addition**:
   - Define `Route` in `nav`.
   - Implement `Interactor` in `domain` (if needed).
   - Implement `Presenter` in `presenter` using `@AssistedInject` and `@NavScreen`.
   - Implement `Screen` in `ui` using `@ScreenUi`.
2. **Data Changes**:
   - Update SQLDelight `.sq` files.
   - Update `Store` configuration.
   - Update `Mapper` to convert database/network entities to domain/UI models.

## Verification
- Run `./gradlew lint` for Android linting.
- Run `./gradlew test` for unit tests.
- Ensure all DI graphs are valid by running a build.


================================================
FILE: Gemfile
================================================
source "https://rubygems.org"

gem "fastlane"
gem "xcode-install"
gem "xcpretty"

plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
eval_gemfile(plugins_path) if File.exist?(plugins_path)


================================================
FILE: LICENSE
================================================
 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

================================================
FILE: README.md
================================================
<p align="center">
<img src="art/TvManiacBanner.png" width="100%" />
</p>

# TvManiac

![Check](https://github.com/thomaskioko/tv-maniac/actions/workflows/ci.yml/badge.svg)
![kmp](https://img.shields.io/badge/multiplatform-%237F52FF.svg?style=for-the-badge&logo=kotlin&logoColor=white)
![compose](https://img.shields.io/badge/jetpack_compose-2bab6b.svg?style=for-the-badge&logo=android&logoColor=white)
![swiftui](https://img.shields.io/badge/swiftui-%23000000.svg?style=for-the-badge&logo=swift&logoColor=white)

[![Download APK](https://img.shields.io/github/v/release/thomaskioko/tv-maniac?label=Download%20APK&logo=android&style=for-the-badge)](https://github.com/thomaskioko/tv-maniac/releases/latest)

**TvManiac** is a personalized entertainment tracking and recommendation Multiplatform app (Android & iOS) for tracking TV Shows. By utilizing [Trakt](https://trakt.tv) and [TMDB](https://developer.themoviedb.org/docs), you can discover shows, manage your watchlist, track watch progress, and get personalized recommendations.

| Android | iOS |
|---|---|
| <video src="https://github.com/user-attachments/assets/90ec7924-7d50-40a4-bb0b-89d79aa9bbcd" width=350/> | <video src="https://github.com/user-attachments/assets/69f101b7-e100-4775-9893-6687e455560c" width=350/> |

> **Under Heavy Development**
>
> This is my playground for learning Kotlin Multiplatform. With that said, I'm sure it's filled with bugs crawling everywhere, and I'm probably doing a couple of things wrong. So a lot is changing, but that shouldn't stop you from checking it out.

## Install

Download the latest APK from [GitHub Releases](https://github.com/thomaskioko/tv-maniac/releases).

Join the open beta on [Google Play](https://play.google.com/store/apps/details?id=com.thomaskioko.tvmaniac&hl=en_US) or stay up to date with daily builds via Firebase:

[<img width=400 src="art/FirebaseAppDistribution.svg"/>](https://appdistribution.firebase.dev/i/564c934cc970634b)

---

## Getting Started

### Requirements

- [Zulu Java 21](https://www.azul.com/downloads/?package=jdk#zulu)
- Latest [Android Studio](https://developer.android.com/studio/preview)
- [KMM Plugin](https://kotlinlang.org/docs/multiplatform-mobile-setup.html)

### API Keys

The app requires TMDB and Trakt API credentials. See [docs/setup.md](docs/setup.md) for detailed instructions.

Create `local.properties` in the project root:

```properties
TMDB_API_KEY=your_tmdb_api_key
TRAKT_CLIENT_ID=your_trakt_client_id
TRAKT_CLIENT_SECRET=your_trakt_client_secret
TRAKT_REDIRECT_URI=tvmaniac://callback
```

### Setup & Build

```bash
./scripts/install-git-hooks.sh
```

**Android:**
```bash
./gradlew :app:assembleDebug
```

**iOS:**
Open `ios/tv-maniac.xcodeproj` in Xcode and run.

---

## Architecture

The project follows Clean Architecture with a modular design organized by feature and layer. Business logic and state management live in shared KMP code, while Android (Compose) and iOS (SwiftUI) contain only UI rendering.

For detailed documentation:

- [Modularization](docs/architecture/modularization.md)
- [Presentation Layer](docs/architecture/presentation-layer.md)
- [Data Layer](docs/architecture/data-layer.md)
- [Navigation](docs/architecture/navigation.md)
- [Dependency Injection](docs/architecture/dependency-injection.md)
- [Integration Testing](docs/architecture/integration-testing.md)

---

## Key Concepts

A few foundational libraries and patterns drive the architecture.

- **[Decompose](https://arkivanov.github.io/Decompose/)**. Shared navigation and lifecycle for KMP. The navigation stack, child components, and back handling all live in shared Kotlin code. Android (Compose) and iOS (SwiftUI) only render the active child. See [Navigation](docs/architecture/navigation.md).
- **[Metro](https://zacsweers.github.io/metro/latest/)**. Compile time dependency injection. There is no KSP processor and no runtime reflection. Modules expose interfaces from `api/` packages, implementations are bound with `@ContributesBinding`, and the full graph is assembled at the app entry point. See [Dependency Injection](docs/architecture/dependency-injection.md).
- **[Store pattern](https://store.mobilenativefoundation.org/)**. One fetch and cache pipeline per data type. A `Store` combines a `Fetcher` (network), a `SourceOfTruth` (SQLDelight DAO), and a `Validator` (cache freshness via `RequestManager`). Presenters never call the network or DAO directly. See [Data Layer](docs/architecture/data-layer.md).
- **Interactor and SubjectInteractor**. Thin orchestration in the domain layer. An `Interactor` runs a one shot action (mark watched, sign in). A `SubjectInteractor` exposes a continuous `Flow` of data (observe show details). Presenters compose these into screen state. See [Presentation Layer](docs/architecture/presentation-layer.md).

---

## Tech Stack

Architectural choices (Decompose, Metro, Store) are described in [Key Concepts](#key-concepts) above. The libraries below cover the rest of the shared and platform stack.

**Shared (KMP)**
- [Kotlin Coroutines](https://github.com/Kotlin/kotlinx.coroutines) - Concurrency
- [Ktor](https://ktor.io/) - Networking
- [SQLDelight](https://github.com/cashapp/sqldelight) - Local database
- [kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization) - JSON serialization
- [Multiplatform Paging](https://github.com/cashapp/multiplatform-paging) - Pagination

**Android**
- [Jetpack Compose](https://developer.android.com/jetpack/compose) - UI toolkit
- [Coil](https://coil-kt.github.io/coil/) - Image loading
- [AppAuth](https://openid.github.io/AppAuth-Android/) - OAuth authentication

**iOS**
- [SwiftUI](https://developer.apple.com/xcode/swiftui/) - UI framework
- [Nuke](https://github.com/kean/Nuke) - Image loading
- [OAuthSwift](https://github.com/OAuthSwift/OAuthSwift) - OAuth authentication

---

## Gradle Convention Plugins

Build configurations are managed by [app-gradle-plugins](https://github.com/thomaskioko/app-gradle-plugins), a set of custom Gradle convention plugins published to Maven Central. They handle Android/KMP module setup, versioning, release automation, and R8 optimization. For a deep dive into how they work, see [Publishing Gradle Convention Plugins](https://thomaskioko.me/posts/publishing_gradle_plugins/).

---

## References & Inspiration

- [Design Inspiration](https://dribbble.com/shots/7591814-HBO-Max-Companion-App-Animation)
- [Tivi](https://github.com/chrisbanes/tivi)
- [Compose Samples](https://github.com/android/compose-samples)

## License

```
Copyright 2021 Thomas Kioko

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```


================================================
FILE: android-designsystem/build.gradle.kts
================================================
plugins { alias(libs.plugins.app.android) }

scaffold {
    android {
        enableAndroidResources()

        useCompose()
        useRoborazzi()
    }

    optIn("androidx.compose.material3.ExperimentalMaterial3Api")
}

dependencies {
    api(libs.androidx.compose.ui.tooling)
    api(libs.androidx.compose.ui.tooling.preview)
    api(libs.androidx.compose.material3)
    api(libs.androidx.compose.ui.ui)
    api(libs.androidx.compose.material.icons)
    api(libs.androidx.compose.runtime)

    api(projects.domain.theme)
    implementation(projects.core.testTags)
    implementation(projects.i18n.generator)

    api(libs.coil.base)
    implementation(libs.androidx.annotation)
    implementation(libs.androidx.collections)
    implementation(libs.androidx.core.ktx)
    implementation(libs.androidx.compose.foundation)
    implementation(libs.androidx.compose.constraintlayout)
    implementation(libs.coil.coil)
    implementation(libs.coil.compose)
    implementation(libs.kenburns)
    implementation(libs.androidx.palette)
    implementation(libs.coroutines.jvm)
    implementation(libs.kotlinx.collections)

    testImplementation(libs.robolectric.annotations)
    testImplementation(projects.core.screenshotTests)
}


================================================
FILE: android-designsystem/src/debug/res/values/strings.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name" translatable="false">TvManiac</string>
</resources>


================================================
FILE: android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/Background.kt
================================================
package com.thomaskioko.tvmaniac.compose.components

import android.content.res.Configuration
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.material3.LocalAbsoluteTonalElevation
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.thomaskioko.tvmaniac.compose.theme.LocalBackgroundTheme

/**
 * The main background for the app. Uses [LocalBackgroundTheme] to set the color and tonal elevation
 * of a [Surface].
 *
 * @param modifier Modifier to be applied to the background.
 * @param content The background content.
 */
@Composable
public fun TvManiacBackground(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit,
) {
    val color = LocalBackgroundTheme.current.color
    val tonalElevation = LocalBackgroundTheme.current.tonalElevation

    Surface(
        color = if (color == Color.Unspecified) Color.Transparent else color,
        tonalElevation = if (tonalElevation == Dp.Unspecified) 0.dp else tonalElevation,
        modifier = modifier.fillMaxSize(),
    ) {
        CompositionLocalProvider(LocalAbsoluteTonalElevation provides 0.dp) { content() }
    }
}

@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, name = "Light Theme", showBackground = true)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, name = "Dark Theme", showBackground = true)
public annotation class ThemePreviews

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun BackgroundDefault() {
    Spacer(Modifier.size(100.dp))
}


================================================
FILE: android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/BadgeChip.kt
================================================
package com.thomaskioko.tvmaniac.compose.components

import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import com.thomaskioko.tvmaniac.compose.components.ThemePreviews
import com.thomaskioko.tvmaniac.compose.components.TvManiacPreviewWrapperProvider

@Composable
public fun PremiereBadge(
    modifier: Modifier = Modifier,
    text: String,
) {
    Surface(
        modifier = modifier,
        shape = RoundedCornerShape(4.dp),
        color = MaterialTheme.colorScheme.onSurface,
    ) {
        Text(
            text = text,
            style = MaterialTheme.typography.labelSmall,
            color = MaterialTheme.colorScheme.background,
            modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp),
        )
    }
}

@Composable
public fun NewBadge(
    modifier: Modifier = Modifier,
    text: String,
) {
    Surface(
        modifier = modifier,
        shape = RoundedCornerShape(4.dp),
        color = MaterialTheme.colorScheme.secondary,
    ) {
        Text(
            text = text,
            style = MaterialTheme.typography.labelSmall,
            color = MaterialTheme.colorScheme.onSecondary,
            modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp),
        )
    }
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun PremiereBadgePreview() {
    PremiereBadge(text = "Premiere")
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun NewBadgePreview() {
    NewBadge(text = "New")
}


================================================
FILE: android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/Buttons.kt
================================================
package com.thomaskioko.tvmaniac.compose.components

import androidx.compose.animation.Crossfade
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.LibraryAddCheck
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.luminance
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.thomaskioko.tvmaniac.compose.extensions.iconButtonBackgroundScrim
import com.thomaskioko.tvmaniac.compose.theme.TvManiacTheme
import com.thomaskioko.tvmaniac.domain.theme.Theme

@Composable
public fun FilledTextButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    shape: Shape = RectangleShape,
    buttonColors: ButtonColors = ButtonDefaults.textButtonColors(),
    content: @Composable RowScope.() -> Unit,
) {
    TextButton(
        onClick = onClick,
        modifier = modifier,
        enabled = enabled,
        colors = buttonColors,
        content = content,
        shape = shape,
    )
}

@Composable
public fun FilledVerticalIconButton(
    text: String,
    onClick: () -> Unit,
    imageVector: ImageVector,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    shape: Shape = RectangleShape,
    style: TextStyle = MaterialTheme.typography.bodyMedium,
    containerColor: Color = MaterialTheme.colorScheme.secondary,
    contentColor: Color = MaterialTheme.colorScheme.onSecondary,
) {
    TextButtonContent(
        onClick = onClick,
        modifier = modifier,
        enabled = enabled,
        containerColor = containerColor,
        shape = shape,
        content = {
            Column(
                modifier = Modifier
                    .sizeIn(minWidth = 120.dp),
                horizontalAlignment = Alignment.CenterHorizontally,
            ) {
                Icon(
                    imageVector = imageVector,
                    contentDescription = null,
                    tint = when {
                        enabled -> contentColor
                        else -> MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f)
                    },
                )

                Text(
                    modifier = Modifier.padding(top = 2.dp),
                    text = text,
                    style = style,
                    color = when {
                        enabled -> contentColor
                        else -> MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f)
                    },
                )
            }
        },
    )
}

@Composable
public fun FilledHorizontalIconButton(
    text: String,
    onClick: () -> Unit,
    imageVector: ImageVector,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    shape: Shape = RectangleShape,
    style: TextStyle = MaterialTheme.typography.bodyMedium,
    containerColor: Color = MaterialTheme.colorScheme.secondary,
) {
    TextButtonContent(
        onClick = onClick,
        modifier = modifier,
        enabled = enabled,
        containerColor = containerColor,
        shape = shape,
        content = {
            Row(
                modifier = Modifier
                    .sizeIn(minHeight = 32.dp, minWidth = 140.dp),
                horizontalArrangement = Arrangement.Center,
                verticalAlignment = Alignment.CenterVertically,
            ) {
                Icon(
                    imageVector = imageVector,
                    contentDescription = null,
                    tint = when {
                        enabled -> MaterialTheme.colorScheme.onSecondary
                        else -> MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f)
                    },
                )

                Spacer(modifier = Modifier.width(8.dp))

                Text(
                    text = text,
                    style = style,
                    color = when {
                        enabled -> MaterialTheme.colorScheme.onSecondary
                        else -> MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f)
                    },
                )
            }
        },
    )
}

@Composable
private fun TextButtonContent(
    onClick: () -> Unit,
    enabled: Boolean,
    containerColor: Color,
    shape: Shape,
    content: @Composable () -> Unit,
    modifier: Modifier = Modifier,
) {
    TextButton(
        onClick = onClick,
        modifier = modifier,
        enabled = enabled,
        colors = ButtonDefaults.buttonColors(
            contentColor = MaterialTheme.colorScheme.onBackground,
            containerColor = containerColor,
        ),
        shape = shape,
    ) {
        content()
    }
}

@Composable
public fun HorizontalOutlinedButton(
    text: String,
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    textPadding: Dp = 0.dp,
    shape: Shape = MaterialTheme.shapes.small,
    borderColor: Color = MaterialTheme.colorScheme.secondary,
    leadingIcon: @Composable (() -> Unit)? = null,
) {
    OutlinedButton(
        onClick = onClick,
        modifier = modifier.padding(2.dp),
        enabled = enabled,
        shape = shape,
        content = {
            if (leadingIcon != null) {
                Box(Modifier.sizeIn(maxHeight = ButtonDefaults.IconSize)) { leadingIcon() }
            }
            Box(
                Modifier.padding(
                    start = when {
                        leadingIcon != null -> ButtonDefaults.IconSpacing
                        else -> 0.dp
                    },
                ),
            ) {
                Text(
                    text = text,
                    style = MaterialTheme.typography.bodyMedium,
                    color = if (enabled) {
                        MaterialTheme.colorScheme.secondary
                    } else {
                        MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f)
                    },
                    modifier = Modifier.padding(textPadding),
                )
            }
        },
        colors = ButtonDefaults.outlinedButtonColors(
            contentColor = MaterialTheme.colorScheme.onSecondary,
        ),
        border = BorderStroke(
            width = 1.dp,
            color = when {
                enabled -> borderColor
                else -> MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f)
            },
        ),
    )
}

@Composable
public fun OutlinedVerticalIconButton(
    text: String,
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    shape: Shape = MaterialTheme.shapes.small,
    borderColor: Color = MaterialTheme.colorScheme.secondary,
    leadingIcon: @Composable (() -> Unit) = {},
) {
    OutlinedButton(
        onClick = onClick,
        modifier = modifier.widthIn(min = 140.dp),
        enabled = enabled,
        shape = shape,
        content = {
            Column(
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.Center,
            ) {
                leadingIcon()

                Text(
                    text = text,
                    style = MaterialTheme.typography.bodyMedium,
                    color = when {
                        enabled -> MaterialTheme.colorScheme.secondary
                        else -> MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f)
                    },
                    modifier = Modifier.padding(top = 2.dp),
                )
            }
        },
        colors = ButtonDefaults.outlinedButtonColors(
            contentColor = MaterialTheme.colorScheme.onSecondary,
        ),
        border = BorderStroke(
            width = 1.dp,
            color = when {
                enabled -> borderColor
                else -> MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f)
            },
        ),
    )
}

@Composable
public fun OutlinedVerticalIconButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    shape: Shape = MaterialTheme.shapes.small,
    borderColor: Color = MaterialTheme.colorScheme.secondary,
    text: @Composable (() -> Unit) = {},
    leadingIcon: @Composable (() -> Unit) = {},
) {
    OutlinedButton(
        onClick = onClick,
        modifier = modifier.widthIn(min = 140.dp),
        enabled = enabled,
        shape = shape,
        content = {
            Column(
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.Center,
            ) {
                leadingIcon()

                text()
            }
        },
        colors = ButtonDefaults.outlinedButtonColors(
            contentColor = MaterialTheme.colorScheme.onSecondary,
        ),
        border = BorderStroke(
            width = 1.dp,
            color = when {
                enabled -> borderColor
                else -> MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f)
            },
        ),
    )
}

@Composable
public fun ScrimButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    show: Boolean = false,
    color: Color = MaterialTheme.colorScheme.surface,
    alpha: Float = 0.4f,
    content: @Composable () -> Unit,
) {
    val isLight = color.luminance() > 0.5
    val scrimEnabled = !show
    if (scrimEnabled) {
        val appTheme = if (isLight) Theme.LIGHT_THEME else Theme.DARK_THEME
        TvManiacTheme(appTheme = appTheme) {
            IconButton(
                onClick = onClick,
                modifier = modifier.iconButtonBackgroundScrim(enabled = true, alpha = alpha),
            ) {
                content()
            }
        }
    } else {
        IconButton(
            onClick = onClick,
            modifier = modifier.iconButtonBackgroundScrim(enabled = false, alpha = alpha),
        ) {
            content()
        }
    }
}

@Composable
public fun RefreshButton(
    isRefreshing: Boolean,
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit,
) {
    Crossfade(isRefreshing, label = "ActionButtonCrossfade") { targetRefreshing ->
        if (targetRefreshing) {
            AutoSizedCircularProgressIndicator(
                modifier = modifier,
            )
        } else {
            content()
        }
    }
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun FilledTextButtonPreview() {
    FilledTextButton(
        onClick = {},
        enabled = false,
        buttonColors = ButtonDefaults.buttonColors(
            contentColor = MaterialTheme.colorScheme.onBackground,
            containerColor = MaterialTheme.colorScheme.secondary,
        ),
        modifier = Modifier
            .fillMaxWidth()
            .padding(2.dp)
            .background(color = MaterialTheme.colorScheme.secondary),
    ) {
        Text(
            text = "Horror",
            style = MaterialTheme.typography.bodyMedium,
            color = MaterialTheme.colorScheme.onSecondary,
        )
    }
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun FilledIconButtonPreview(@PreviewParameter(ButtonPreviewParamProvider::class) isEnable: Boolean) {
    FilledVerticalIconButton(
        onClick = {},
        enabled = isEnable,
        text = "Track",
        imageVector = Icons.Default.LibraryAddCheck,
    )
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun FilledHorizontalIconButtonPreview(@PreviewParameter(ButtonPreviewParamProvider::class) isEnable: Boolean) {
    FilledHorizontalIconButton(
        onClick = {},
        enabled = isEnable,
        text = "Add To Library",
        imageVector = Icons.Default.LibraryAddCheck,
    )
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun TvManiacAlphaTextButtonPreview() {
    FilledTextButton(
        onClick = {},
        enabled = false,
        buttonColors = ButtonDefaults.buttonColors(
            contentColor = MaterialTheme.colorScheme.onSecondary,
            containerColor = MaterialTheme.colorScheme.secondary.copy(alpha = 0.08f),
        ),
    ) {
        Text(
            text = "Horror",
            style = MaterialTheme.typography.bodyMedium,
            color = MaterialTheme.colorScheme.secondary,
        )
    }
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun TvManiacOutlinedButtonPreview() {
    OutlinedVerticalIconButton(
        onClick = {},
        enabled = true,
        leadingIcon = {
            Image(
                imageVector = Icons.Filled.LibraryAddCheck,
                contentDescription = null,
                colorFilter = ColorFilter.tint(
                    MaterialTheme.colorScheme.secondary.copy(
                        alpha = 0.8F,
                    ),
                ),
            )
        },
        text = "Following",
    )
}

private class ButtonPreviewParamProvider : PreviewParameterProvider<Boolean> {
    override val values: Sequence<Boolean> = sequenceOf(
        true,
        false,
    )
}


================================================
FILE: android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/Card.kt
================================================
package com.thomaskioko.tvmaniac.compose.components

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Bookmarks
import androidx.compose.material.icons.outlined.Person
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.thomaskioko.tvmaniac.i18n.MR.strings.cd_show_poster

@Composable
public fun PosterCard(
    imageUrl: String?,
    modifier: Modifier = Modifier,
    onClick: () -> Unit = {},
    title: String? = null,
    imageWidth: Dp = 120.dp,
    aspectRatio: Float = 2 / 3f,
    contentScale: ContentScale = ContentScale.Crop,
    shape: Shape = RectangleShape,
    isInLibrary: Boolean = false,
    libraryImageOverlay: ImageVector = Icons.Filled.Bookmarks,
) {
    PosterCard(
        onClick = onClick,
        modifier = modifier,
        shape = shape,
        imageWidth = imageWidth,
        content = {
            Box {
                PosterPlaceholder(
                    modifier = Modifier
                        .fillMaxWidth()
                        .aspectRatio(aspectRatio)
                        .align(Alignment.Center),
                    title = title,
                )

                AsyncImageComposable(
                    model = imageUrl,
                    contentScale = contentScale,
                    contentDescription = title?.let {
                        stringResource(
                            cd_show_poster.resourceId,
                            title,
                        )
                    },
                    modifier = Modifier
                        .fillMaxWidth()
                        .aspectRatio(aspectRatio),
                )

                if (isInLibrary) {
                    LibraryOverlay(libraryImageOverlay = libraryImageOverlay)
                }
            }
        },
    )
}

@Composable
private fun LibraryOverlay(
    libraryImageOverlay: ImageVector,
    modifier: Modifier = Modifier,
) {
    Box(
        modifier = modifier.fillMaxSize(),
        contentAlignment = Alignment.TopEnd,
    ) {
        Icon(
            imageVector = libraryImageOverlay,
            contentDescription = null,
            tint = Color.White,
            modifier = Modifier
                .padding(8.dp)
                .size(20.dp),
        )
    }
}

@Composable
public fun PosterBackdropCard(
    title: String,
    imageUrl: String?,
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    textAlign: TextAlign = TextAlign.Start,
    contentScale: ContentScale = ContentScale.Crop,
    imageWidth: Dp = 120.dp,
    aspectRatio: Float = 2 / 3f,
    shape: Shape = RectangleShape,
) {
    val surface = MaterialTheme.colorScheme.surface
    val brush = remember(surface) {
        Brush.verticalGradient(
            colors = listOf(
                Color.Transparent,
                surface.copy(alpha = 0.4f),
                surface.copy(alpha = 0.7f),
                surface.copy(alpha = 0.9f),
                surface,
            ),
        )
    }

    PosterCard(
        onClick = onClick,
        modifier = modifier,
        shape = shape,
        imageWidth = imageWidth,
        content = {
            Box {
                PosterPlaceholder(
                    modifier = Modifier
                        .fillMaxWidth()
                        .aspectRatio(aspectRatio)
                        .align(Alignment.Center),
                    imageSize = 84.dp,
                )

                AsyncImageComposable(
                    modifier = Modifier
                        .fillMaxWidth()
                        .aspectRatio(aspectRatio),
                    model = imageUrl,
                    contentScale = contentScale,
                    contentDescription = stringResource(cd_show_poster.resourceId, title),
                    alignment = Alignment.Center,
                )

                Box(
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(80.dp)
                        .align(Alignment.BottomCenter)
                        .background(brush),
                )

                Text(
                    text = title,
                    style = MaterialTheme.typography.labelLarge,
                    color = MaterialTheme.colorScheme.onSurface,
                    textAlign = textAlign,
                    overflow = TextOverflow.Ellipsis,
                    maxLines = 1,
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(16.dp)
                        .align(Alignment.BottomStart),
                )
            }
        },
    )
}

@Composable
internal fun PosterCard(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    imageWidth: Dp = 120.dp,
    shape: Shape = RectangleShape,
    content: @Composable () -> Unit,
) {
    Card(
        onClick = onClick,
        modifier = modifier
            .width(imageWidth),
        shape = shape,
        elevation = CardDefaults.cardElevation(
            defaultElevation = 4.dp,
        ),
        colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface),
    ) {
        content()
    }
}

@Composable
public fun CastCard(
    profileUrl: String?,
    name: String,
    characterName: String,
    modifier: Modifier = Modifier,
    height: Dp = 160.dp,
) {
    Card(
        modifier = modifier,
        shape = MaterialTheme.shapes.small,
        elevation = CardDefaults.cardElevation(defaultElevation = 8.dp),
    ) {
        Box(
            modifier = Modifier
                .fillMaxSize()
                .size(width = 120.dp, height = height),
            contentAlignment = Alignment.BottomStart,
        ) {
            CastPlaceholder(
                modifier = Modifier.fillMaxSize(),
                imageUrl = profileUrl,
                name = name,
            )

            AsyncImageComposable(
                model = profileUrl,
                contentDescription = name,
                contentScale = ContentScale.Crop,
                modifier = Modifier.fillMaxSize(),
            )

            Box(
                modifier = Modifier
                    .matchParentSize()
                    .background(contentBackgroundGradient()),
            )

            CastNameOverlay(
                name = name,
                characterName = characterName,
            )
        }
    }
}

@Composable
private fun CastPlaceholder(
    imageUrl: String?,
    modifier: Modifier = Modifier,
    name: String? = null,
) {
    if (imageUrl.isNullOrEmpty()) {
        Box(
            modifier = modifier
                .background(
                    Brush.verticalGradient(
                        colors = listOf(
                            Color.Gray.copy(alpha = 0.8f),
                            Color.Gray,
                        ),
                    ),
                ),
            contentAlignment = Alignment.Center,
        ) {
            Icon(
                modifier = Modifier.size(52.dp),
                imageVector = Icons.Outlined.Person,
                contentDescription = name,
                tint = Color.White.copy(alpha = 0.8f),
            )
        }
    }
}

@Composable
private fun CastNameOverlay(
    name: String,
    characterName: String,
    modifier: Modifier = Modifier,
) {
    Column(modifier = modifier.padding(8.dp)) {
        Text(
            text = name,
            modifier = Modifier
                .padding(vertical = 2.dp)
                .fillMaxWidth(),
            overflow = TextOverflow.Ellipsis,
            maxLines = 1,
            style = MaterialTheme.typography.bodyMedium.copy(
                fontWeight = FontWeight.Bold,
                color = MaterialTheme.colorScheme.onSurface,
            ),
        )
        Text(
            text = characterName,
            modifier = Modifier.fillMaxWidth(),
            overflow = TextOverflow.Ellipsis,
            maxLines = 1,
            style = MaterialTheme.typography.bodyMedium.copy(
                fontWeight = FontWeight.Normal,
                color = MaterialTheme.colorScheme.onSurface,
            ),
        )
    }
}

@Composable
private fun contentBackgroundGradient(): Brush {
    val surface = MaterialTheme.colorScheme.surface
    return remember(surface) {
        Brush.verticalGradient(
            colors = listOf(
                Color.Transparent,
                surface.copy(alpha = 0.3f),
                surface.copy(alpha = 0.6f),
                surface.copy(alpha = 0.9f),
                surface,
            ),
        )
    }
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun CastCardPreview() {
    CastCard(
        profileUrl = null,
        name = "Tom Hiddleston",
        characterName = "Loki",
    )
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun PosterCardPreview() {
    PosterCard(
        imageUrl = "",
        title = "Loki",

        modifier = Modifier
            .width(100.dp)
            .aspectRatio(0.8f),
    )
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun PosterCardWithLibraryOverlayPreview() {
    PosterCard(
        imageUrl = "",
        title = "Loki",
        isInLibrary = true,

        modifier = Modifier
            .width(100.dp)
            .aspectRatio(0.8f),
    )
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun PosterBackdropPreview() {
    PosterBackdropCard(
        imageUrl = "",
        title = "Game of Thrones",
        onClick = {},
        modifier = Modifier
            .fillMaxWidth()
            .height(240.dp),
    )
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun AvatarComponentPreview() {
    AvatarComponent(
        imageUrl = "",
        size = 38.dp,
        contentDescription = "Profile",
        onClick = {},
    )
}


================================================
FILE: android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/Chip.kt
================================================
package com.thomaskioko.tvmaniac.compose.components

import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.FilterChip
import androidx.compose.material3.FilterChipDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProvideTextStyle
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp

@Composable
public fun TvManiacChip(
    text: String,
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    selected: Boolean = true,
    enabled: Boolean = true,
) {
    FilterChip(
        modifier = modifier,
        selected = selected,
        onClick = onClick,
        label = {
            ProvideTextStyle(value = MaterialTheme.typography.labelSmall) {
                Text(
                    text = text,
                    style = MaterialTheme.typography.bodyMedium,
                    color = MaterialTheme.colorScheme.secondary,
                    modifier = Modifier.padding(vertical = 8.dp),
                )
            }
        },
        enabled = enabled,
        leadingIcon = null,
        border = null,
        shape = RoundedCornerShape(4.dp),
        colors = FilterChipDefaults.filterChipColors(
            containerColor = MaterialTheme.colorScheme.secondary.copy(alpha = 0.08f),
            labelColor = MaterialTheme.colorScheme.secondary,
            selectedContainerColor = MaterialTheme.colorScheme.secondary.copy(alpha = 0.24f),
            selectedLabelColor = MaterialTheme.colorScheme.secondary,
        ),
    )
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun ChipItemSelectedPreview() {
    TvManiacChip(
        selected = true,
        text = "Season 1",
        onClick = {},
    )
}


================================================
FILE: android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/Dialogs.kt
================================================
package com.thomaskioko.tvmaniac.compose.components

import androidx.compose.foundation.layout.widthIn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Info
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalWindowInfo
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.DialogProperties
import com.thomaskioko.tvmaniac.compose.components.ThemePreviews
import com.thomaskioko.tvmaniac.compose.components.TvManiacPreviewWrapperProvider

@Composable
public fun TvManiacAlertDialog(
    title: String,
    message: String,
    confirmButtonText: String,
    onConfirm: () -> Unit,
    onDismiss: () -> Unit,
    modifier: Modifier = Modifier,
    shape: Shape = MaterialTheme.shapes.small,
    icon: ImageVector? = null,
    dismissButtonText: String? = null,
    confirmButtonTestTag: String? = null,
    dismissButtonTestTag: String? = null,
) {
    val density = LocalDensity.current
    val containerWidth = with(density) {
        LocalWindowInfo.current.containerSize.width.toDp()
    }

    AlertDialog(
        properties = DialogProperties(usePlatformDefaultWidth = false),
        modifier = Modifier.widthIn(max = (containerWidth - 80.dp).coerceAtLeast(0.dp)),
        shape = shape,
        onDismissRequest = onDismiss,
        icon = icon?.let {
            {
                Icon(
                    imageVector = it,
                    contentDescription = null,
                    tint = MaterialTheme.colorScheme.secondary,
                )
            }
        },
        title = {
            Text(
                text = title,
                style = MaterialTheme.typography.titleLarge,
                color = MaterialTheme.colorScheme.onSurface,
            )
        },
        text = {
            Text(
                text = message,
                style = MaterialTheme.typography.bodyMedium,
                color = MaterialTheme.colorScheme.onSurfaceVariant,
            )
        },
        confirmButton = {
            TextButton(
                modifier = confirmButtonTestTag?.let { Modifier.testTag(it) } ?: Modifier,
                onClick = onConfirm,
            ) {
                Text(
                    text = confirmButtonText,
                    style = MaterialTheme.typography.labelLarge,
                    color = MaterialTheme.colorScheme.secondary,
                )
            }
        },
        dismissButton = dismissButtonText?.let {
            {
                TextButton(
                    modifier = dismissButtonTestTag?.let { tag -> Modifier.testTag(tag) } ?: Modifier,
                    onClick = onDismiss,
                ) {
                    Text(
                        text = it,
                        style = MaterialTheme.typography.labelLarge,
                        color = MaterialTheme.colorScheme.onSurfaceVariant,
                    )
                }
            }
        },
    )
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun TvManiacAlertDialogPreview() {
    TvManiacAlertDialog(
        title = "Enable Notifications",
        message = "Get notified when new episodes of your favorite shows are released.",
        confirmButtonText = "Enable",
        dismissButtonText = "Not Now",
        icon = Icons.Default.Info,
        onConfirm = {},
        onDismiss = {},
    )
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun TvManiacAlertDialogNoIconPreview() {
    TvManiacAlertDialog(
        title = "Confirm Action",
        message = "Are you sure you want to proceed with this action?",
        confirmButtonText = "Confirm",
        dismissButtonText = "Cancel",
        onConfirm = {},
        onDismiss = {},
    )
}


================================================
FILE: android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/EmptyLayout.kt
================================================
package com.thomaskioko.tvmaniac.compose.components

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Inbox
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import com.thomaskioko.tvmaniac.compose.components.ThemePreviews
import com.thomaskioko.tvmaniac.compose.components.TvManiacPreviewWrapperProvider

@Composable
public fun EmptyStateView(
    title: String,
    modifier: Modifier = Modifier,
    imageVector: ImageVector = Icons.Outlined.Inbox,
    message: String? = null,
    buttonText: String? = null,
    buttonTestTag: String? = null,
    onClick: () -> Unit = {},
) {
    Column(
        modifier = modifier
            .fillMaxSize()
            .padding(horizontal = 16.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center,
    ) {
        Icon(
            modifier = Modifier.size(64.dp),
            imageVector = imageVector,
            tint = MaterialTheme.colorScheme.onSurfaceVariant,
            contentDescription = null,
        )

        Spacer(modifier = Modifier.height(24.dp))

        Text(
            text = title,
            style = MaterialTheme.typography.titleMedium,
            color = MaterialTheme.colorScheme.onSurface,
            textAlign = TextAlign.Center,
        )

        message?.let {
            Spacer(modifier = Modifier.height(8.dp))

            Text(
                text = it,
                style = MaterialTheme.typography.bodyMedium,
                color = MaterialTheme.colorScheme.onSurfaceVariant,
                textAlign = TextAlign.Center,
            )
        }

        buttonText?.let {
            Spacer(modifier = Modifier.height(24.dp))

            HorizontalOutlinedButton(
                modifier = buttonTestTag?.let { Modifier.testTag(it) } ?: Modifier,
                text = it,
                onClick = onClick,
                shape = MaterialTheme.shapes.small,
            )
        }
    }
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun EmptyStateViewPreview() {
    EmptyStateView(
        title = "Nothing here yet",
        message = "Shows you follow will appear here.",
    )
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun EmptyStateViewWithButtonPreview() {
    EmptyStateView(
        title = "Something went wrong",
        message = "We couldn't load the data.",
        buttonText = "Retry",
        onClick = {},
    )
}


================================================
FILE: android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/ErrorLayout.kt
================================================
package com.thomaskioko.tvmaniac.compose.components

import androidx.compose.animation.animateColorAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
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.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.SignalWifi4Bar
import androidx.compose.material.icons.outlined.SignalWifiOff
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import com.thomaskioko.tvmaniac.compose.components.ThemePreviews
import com.thomaskioko.tvmaniac.compose.components.TvManiacPreviewWrapperProvider
import com.thomaskioko.tvmaniac.compose.theme.green
import com.thomaskioko.tvmaniac.i18n.MR.strings.cd_connectivity_icon
import com.thomaskioko.tvmaniac.i18n.MR.strings.status_connected
import com.thomaskioko.tvmaniac.i18n.MR.strings.status_no_connection
import com.thomaskioko.tvmaniac.i18n.MR.strings.unexpected_error_retry
import com.thomaskioko.tvmaniac.i18n.resolve

@Composable
public fun ConnectionStatus(
    isConnected: Boolean,
    modifier: Modifier = Modifier,
) {
    val backgroundColor by
        animateColorAsState(
            if (isConnected) green else MaterialTheme.colorScheme.error,
            label = "",
        )
    val message = if (isConnected) {
        status_connected.resolve(LocalContext.current)
    } else {
        status_no_connection.resolve(LocalContext.current)
    }
    val icon = if (isConnected) Icons.Outlined.SignalWifi4Bar else Icons.Outlined.SignalWifiOff

    Box(
        modifier = modifier
            .background(backgroundColor)
            .fillMaxWidth()
            .padding(8.dp),
        contentAlignment = Alignment.TopCenter,
    ) {
        Row(
            verticalAlignment = Alignment.CenterVertically,
            horizontalArrangement = Arrangement.Center,
        ) {
            Icon(
                imageVector = icon,
                contentDescription = cd_connectivity_icon.resolve(LocalContext.current),
                tint = Color.White,
            )

            Spacer(modifier = Modifier.size(8.dp))

            Text(
                message,
                color = Color.White,
                style = MaterialTheme.typography.labelMedium,
            )
        }
    }
}

@Composable
public fun RowError(
    onRetry: () -> Unit,
    modifier: Modifier = Modifier,
    errorMessage: String = unexpected_error_retry.resolve(LocalContext.current),
) {
    Column(
        modifier = modifier,
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center,
    ) {
        Text(
            text = errorMessage,
            style = MaterialTheme.typography.bodyLarge,
            textAlign = TextAlign.Center,
        )

        Spacer(modifier = Modifier.height(8.dp))

        HorizontalOutlinedButton(
            text = "Retry",
            onClick = onRetry,
        )
    }
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun RowErrorPreview() {
    RowError(
        modifier = Modifier
            .fillMaxWidth()
            .padding(vertical = 16.dp),
        onRetry = {},
    )
}


================================================
FILE: android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/FilterChipSection.kt
================================================
package com.thomaskioko.tvmaniac.compose.components

import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
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.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material.icons.filled.KeyboardArrowUp
import androidx.compose.material3.FilterChip
import androidx.compose.material3.FilterChipDefaults
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
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.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import com.thomaskioko.tvmaniac.compose.components.ThemePreviews
import com.thomaskioko.tvmaniac.compose.components.TvManiacPreviewWrapperProvider
import com.thomaskioko.tvmaniac.i18n.MR.strings.label_library_filter_show_less
import com.thomaskioko.tvmaniac.i18n.MR.strings.label_library_filter_show_more
import com.thomaskioko.tvmaniac.i18n.resolve
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.ImmutableSet
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.persistentSetOf

@OptIn(ExperimentalLayoutApi::class)
@Composable
public fun <T> FilterChipSection(
    title: String,
    items: ImmutableList<T>,
    selectedItems: ImmutableSet<T>,
    onItemToggle: (T) -> Unit,
    labelProvider: (T) -> String,
    modifier: Modifier = Modifier,
    collapsedItemCount: Int = 5,
    singleSelect: Boolean = false,
) {
    val context = LocalContext.current
    var isExpanded by remember { mutableStateOf(false) }
    val visibleItems = if (isExpanded) items else items.take(collapsedItemCount)
    val hasMoreItems = items.size > collapsedItemCount

    Column(
        modifier = modifier
            .fillMaxWidth()
            .animateContentSize(),
    ) {
        SectionHeader(title = title)

        Spacer(modifier = Modifier.height(12.dp))

        FlowRow(
            horizontalArrangement = Arrangement.spacedBy(8.dp),
            verticalArrangement = Arrangement.spacedBy(8.dp),
        ) {
            visibleItems.forEach { item ->
                val isSelected = item in selectedItems
                SelectableFilterChip(
                    label = labelProvider(item),
                    isSelected = isSelected,
                    onClick = { onItemToggle(item) },
                )
            }
        }

        if (hasMoreItems) {
            Spacer(modifier = Modifier.height(8.dp))
            ShowMoreToggle(
                isExpanded = isExpanded,
                showMoreText = label_library_filter_show_more.resolve(context),
                showLessText = label_library_filter_show_less.resolve(context),
                onToggle = { isExpanded = !isExpanded },
            )
        }
    }
}

@Composable
public fun SectionHeader(
    title: String,
    modifier: Modifier = Modifier,
) {
    Row(
        modifier = modifier.fillMaxWidth(),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        HorizontalDivider(
            modifier = Modifier.weight(1f),
            color = MaterialTheme.colorScheme.outlineVariant,
        )
        Text(
            text = title,
            style = MaterialTheme.typography.labelMedium,
            color = MaterialTheme.colorScheme.onSurfaceVariant,
            modifier = Modifier.padding(horizontal = 16.dp),
        )
        HorizontalDivider(
            modifier = Modifier.weight(1f),
            color = MaterialTheme.colorScheme.outlineVariant,
        )
    }
}

@Composable
public fun SelectableFilterChip(
    label: String,
    isSelected: Boolean,
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
) {
    FilterChip(
        modifier = modifier,
        selected = isSelected,
        onClick = onClick,
        label = {
            Text(
                text = label,
                style = MaterialTheme.typography.bodyMedium,
            )
        },
        shape = RoundedCornerShape(8.dp),
        colors = FilterChipDefaults.filterChipColors(
            containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f),
            selectedContainerColor = MaterialTheme.colorScheme.secondary,
            labelColor = MaterialTheme.colorScheme.onSurface,
            selectedLabelColor = MaterialTheme.colorScheme.onSecondary,
        ),
        border = FilterChipDefaults.filterChipBorder(
            borderColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.8f),
            selectedBorderColor = Color.Transparent,
            enabled = true,
            selected = isSelected,
        ),
    )
}

@Composable
internal fun ShowMoreToggle(
    isExpanded: Boolean,
    showMoreText: String,
    showLessText: String,
    onToggle: () -> Unit,
    modifier: Modifier = Modifier,
) {
    Row(
        modifier = modifier
            .clickable { onToggle() }
            .padding(vertical = 4.dp),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.spacedBy(4.dp),
    ) {
        Text(
            text = if (isExpanded) showLessText else showMoreText,
            style = MaterialTheme.typography.bodyMedium,
            color = MaterialTheme.colorScheme.onSurfaceVariant,
        )
        Icon(
            imageVector = if (isExpanded) Icons.Filled.KeyboardArrowUp else Icons.Filled.KeyboardArrowDown,
            contentDescription = null,
            modifier = Modifier.size(20.dp),
            tint = MaterialTheme.colorScheme.onSurfaceVariant,
        )
    }
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun FilterChipSectionPreview() {
    FilterChipSection(
        title = "GENRES",
        items = persistentListOf(
            "Action & Adventure",
            "Animation",
            "Comedy",
            "Crime",
            "Drama",
            "Fantasy",
            "Sci-Fi",
        ),
        selectedItems = persistentSetOf("Drama", "Comedy"),
        onItemToggle = {},
        labelProvider = { it },
        modifier = Modifier.padding(16.dp),
    )
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun FilterChipSectionCollapsedPreview() {
    FilterChipSection(
        title = "STATUS",
        items = persistentListOf(
            "Returning Series",
            "Planned",
            "In Production",
            "Ended",
            "Canceled",
        ),
        selectedItems = persistentSetOf("Returning Series"),
        onItemToggle = {},
        labelProvider = { it },
        collapsedItemCount = 3,
        modifier = Modifier.padding(16.dp),
    )
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun SectionHeaderPreview() {
    SectionHeader(
        title = "SORT BY",
        modifier = Modifier.padding(16.dp),
    )
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun SelectableFilterChipPreview() {
    Row(
        horizontalArrangement = Arrangement.spacedBy(8.dp),
        modifier = Modifier.padding(16.dp),
    ) {
        SelectableFilterChip(
            label = "Last watched ↓",
            isSelected = true,
            onClick = {},
        )
        SelectableFilterChip(
            label = "Alphabetical",
            isSelected = false,
            onClick = {},
        )
    }
}


================================================
FILE: android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/GradientScrim.kt
================================================
package com.thomaskioko.tvmaniac.compose.components

import android.annotation.SuppressLint
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import kotlin.math.pow

/**
 * Draws a vertical gradient scrim in the foreground.
 *
 * @param color The color of the gradient scrim.
 * @param decay The exponential decay to apply to the gradient. Defaults to `3.0f` which is
 * a cubic decay.
 * @param numStops The number of color stops to draw in the gradient. Higher numbers result in
 * the higher visual quality at the cost of draw performance. Defaults to `16`.
 */
@SuppressLint("ComposeModifierComposed")
public fun Modifier.drawForegroundGradientScrim(
    color: Color,
    decay: Float = 3.0f,
    numStops: Int = 16,
    startY: Float = 0f,
    endY: Float = 1f,
): Modifier = composed {
    val colors = remember(color, numStops) {
        val baseAlpha = color.alpha
        List(numStops) { i ->
            val x = i * 1f / (numStops - 1)
            val opacity = x.pow(decay)
            color.copy(alpha = baseAlpha * opacity)
        }
    }

    drawWithContent {
        drawContent()
        drawRect(
            topLeft = Offset(x = 0f, y = startY * size.height),
            size = size.copy(height = (endY - startY) * size.height),
            brush = Brush.verticalGradient(colors = colors),
        )
    }
}


================================================
FILE: android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/Image.kt
================================================
package com.thomaskioko.tvmaniac.compose.components

import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Person
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.DefaultAlpha
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalWindowInfo
import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.times
import androidx.compose.ui.util.lerp
import androidx.compose.ui.viewinterop.AndroidView
import coil.compose.AsyncImage
import coil.compose.AsyncImagePainter
import coil.load
import coil.request.ImageRequest
import com.flaviofaria.kenburnsview.KenBurnsView
import com.thomaskioko.tvmaniac.compose.components.ThemePreviews
import com.thomaskioko.tvmaniac.compose.components.TvManiacPreviewWrapperProvider
import kotlin.math.absoluteValue

@Composable
public fun AsyncImageComposable(
    model: Any?,
    contentDescription: String?,
    modifier: Modifier = Modifier,
    shape: Shape = RectangleShape,
    border: BorderStroke? = null,
    transform: (AsyncImagePainter.State) -> AsyncImagePainter.State = AsyncImagePainter.DefaultTransform,
    onState: ((AsyncImagePainter.State) -> Unit)? = null,
    requestBuilder: (ImageRequest.Builder.() -> ImageRequest.Builder)? = null,
    alignment: Alignment = Alignment.Center,
    contentScale: ContentScale = ContentScale.Fit,
    alpha: Float = DefaultAlpha,
    colorFilter: ColorFilter? = null,
    filterQuality: FilterQuality = DrawScope.DefaultFilterQuality,
) {
    AsyncImage(
        model = requestBuilder?.let { builder ->
            when (model) {
                is ImageRequest -> model.newBuilder()
                else -> ImageRequest.Builder(LocalContext.current).data(model)
            }
                .builder()
                .build()
        } ?: model,
        contentDescription = contentDescription,
        modifier = modifier
            .clip(shape)
            .then(if (border != null) Modifier.border(border, shape) else Modifier),
        transform = transform,
        onState = onState,
        alignment = alignment,
        contentScale = contentScale,
        alpha = alpha,
        colorFilter = colorFilter,
        filterQuality = filterQuality,
    )
}

@Composable
public fun AvatarComponent(
    imageUrl: String?,
    size: Dp,
    modifier: Modifier = Modifier,
    contentDescription: String? = null,
    border: BorderStroke? = null,
    placeholderIcon: ImageVector = Icons.Outlined.Person,
    onClick: (() -> Unit)? = null,
) {
    val commonModifier = modifier
        .size(size)
        .then(if (onClick != null) Modifier.clickable(onClick = onClick) else Modifier)

    if (imageUrl.isNullOrEmpty()) {
        Box(
            modifier = commonModifier
                .background(
                    color = MaterialTheme.colorScheme.surfaceVariant,
                    shape = CircleShape,
                )
                .then(if (border != null) Modifier.border(border, CircleShape) else Modifier),
            contentAlignment = Alignment.Center,
        ) {
            Icon(
                imageVector = placeholderIcon,
                contentDescription = contentDescription,
                modifier = Modifier.size(size * 0.6f),
                tint = MaterialTheme.colorScheme.onSurfaceVariant,
            )
        }
    } else {
        AsyncImageComposable(
            model = imageUrl,
            contentDescription = contentDescription,
            modifier = commonModifier,
            shape = CircleShape,
            border = border,
            contentScale = ContentScale.Crop,
        )
    }
}

@Composable
public fun KenBurnsViewImage(
    imageUrl: String?,
    modifier: Modifier = Modifier,
) {
    val context = LocalContext.current
    val kenBuns = remember { KenBurnsView(context) }

    Box(modifier = modifier) {
        PosterPlaceholder(modifier = Modifier.fillMaxSize())

        AndroidView(
            factory = { kenBuns },
            modifier = Modifier.fillMaxSize(),
        ) { it.load(imageUrl) }
    }
}

@Composable
public fun ParallaxCarouselImage(
    state: PagerState,
    currentPage: Int,
    imageUrl: String?,
    modifier: Modifier = Modifier,
    shape: Shape = RectangleShape,
    overlayContent: @Composable () -> Unit = {},
) {
    val currentPageOffset = calculatePageOffset(state, currentPage)
    val cardTranslationX = lerp(100f, 0f, 1f - currentPageOffset)
    val cardScaleX = lerp(0.8f, 1f, 1f - currentPageOffset.absoluteValue.coerceIn(0f, 1f))
    val density = LocalDensity.current
    val screenWidth = with(density) {
        LocalWindowInfo.current.containerSize.width.toDp()
    }
    val parallaxOffset = currentPageOffset * screenWidth * 2f

    Box(
        modifier = modifier
            .fillMaxWidth()
            .graphicsLayer {
                scaleX = cardScaleX
                translationX = cardTranslationX
            },
    ) {
        PosterPlaceholder(
            modifier = Modifier
                .fillMaxSize()
                .clip(shape),
        )

        AsyncImageComposable(
            modifier = Modifier
                .fillMaxSize()
                .clip(shape)
                .graphicsLayer {
                    translationX = lerp(10f, 0f, 1f - currentPageOffset) + parallaxOffset.value
                },
            model = imageUrl,
            contentDescription = null,
            contentScale = ContentScale.Crop,
        )

        overlayContent()
    }
}

private fun calculatePageOffset(state: PagerState, currentPage: Int): Float {
    return (state.currentPage + state.currentPageOffsetFraction - currentPage).coerceIn(-1f, 1f)
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun ParallaxCarouselImagePreview() {
    val pagerState = rememberPagerState(pageCount = { 3 })
    ParallaxCarouselImage(
        state = pagerState,
        currentPage = 0,
        imageUrl = null,
        modifier = Modifier
            .fillMaxWidth()
            .height(360.dp),
    )
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun KenBurnsViewImagePreview() {
    KenBurnsViewImage(
        imageUrl = null,
        modifier = Modifier
            .fillMaxWidth()
            .height(240.dp),
    )
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun AvatarComponentPreview() {
    AvatarComponent(
        imageUrl = "https://image.png",
        size = 64.dp,
        modifier = Modifier.padding(16.dp),
        border = BorderStroke(2.dp, MaterialTheme.colorScheme.primary),
    )
}


================================================
FILE: android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/NavigationBar.kt
================================================
package com.thomaskioko.tvmaniac.compose.components

import androidx.compose.foundation.layout.RowScope
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Movie
import androidx.compose.material.icons.outlined.Search
import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material.icons.outlined.VideoLibrary
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.NavigationBarItemDefaults
import androidx.compose.material3.Text
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import com.thomaskioko.tvmaniac.compose.components.ThemePreviews
import com.thomaskioko.tvmaniac.compose.components.TvManiacPreviewWrapperProvider
import com.thomaskioko.tvmaniac.i18n.MR.strings.menu_item_discover
import com.thomaskioko.tvmaniac.i18n.MR.strings.menu_item_library
import com.thomaskioko.tvmaniac.i18n.MR.strings.menu_item_search
import com.thomaskioko.tvmaniac.i18n.MR.strings.menu_item_settings
import com.thomaskioko.tvmaniac.i18n.resolve

@Composable
public fun TvManiacNavigationBar(
    modifier: Modifier = Modifier,
    content: @Composable RowScope.() -> Unit,
) {
    NavigationBar(
        modifier = modifier,
        containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp),
        contentColor = NavigationDefaultColors.navigationContentColor(),
        tonalElevation = 8.dp,
        content = content,
    )
}

@Composable
public fun RowScope.TvManiacBottomNavigationItem(
    imageVector: ImageVector,
    title: String,
    selected: Boolean,
    modifier: Modifier = Modifier,
    onClick: () -> Unit,
) {
    NavigationBarItem(
        modifier = modifier,
        icon = {
            Icon(
                imageVector = imageVector,
                contentDescription = title,
            )
        },
        label = { Text(title) },
        selected = selected,
        alwaysShowLabel = true,
        colors = NavigationBarItemDefaults.colors(
            selectedIconColor = NavigationDefaultColors.navigationSelectedItemColor(),
            unselectedIconColor = NavigationDefaultColors.navigationContentColor(),
            selectedTextColor = NavigationDefaultColors.navigationSelectedItemColor(),
            unselectedTextColor = NavigationDefaultColors.navigationContentColor(),
            indicatorColor = Color.Transparent,
        ),
        onClick = onClick,
    )
}

public object NavigationDefaultColors {
    @Composable
    public fun navigationContentColor(): Color = MaterialTheme.colorScheme.onSurfaceVariant

    @Composable
    public fun navigationSelectedItemColor(): Color = MaterialTheme.colorScheme.secondary
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun TvManiacTvManiacNavigationBarPreviewPreview() {
    TvManiacNavigationBar {
        TvManiacBottomNavigationItem(
            imageVector = Icons.Outlined.Movie,
            title = menu_item_discover.resolve(LocalContext.current),
            selected = true,
            onClick = { },
        )

        TvManiacBottomNavigationItem(
            imageVector = Icons.Outlined.Search,
            title = menu_item_search.resolve(LocalContext.current),
            selected = false,
            onClick = { },
        )

        TvManiacBottomNavigationItem(
            imageVector = Icons.Outlined.VideoLibrary,
            title = menu_item_library.resolve(LocalContext.current),
            selected = false,
            onClick = { },
        )

        TvManiacBottomNavigationItem(
            imageVector = Icons.Outlined.Settings,
            title = menu_item_settings.resolve(LocalContext.current),
            selected = false,
            onClick = { },
        )
    }
}


================================================
FILE: android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/NotificationRationaleContent.kt
================================================
package com.thomaskioko.tvmaniac.compose.components

import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
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.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Notifications
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import com.thomaskioko.tvmaniac.compose.components.ThemePreviews
import com.thomaskioko.tvmaniac.compose.components.TvManiacPreviewWrapperProvider
import com.thomaskioko.tvmaniac.i18n.MR.strings.notification_rationale_enable
import com.thomaskioko.tvmaniac.i18n.MR.strings.notification_rationale_message
import com.thomaskioko.tvmaniac.i18n.MR.strings.notification_rationale_not_now
import com.thomaskioko.tvmaniac.i18n.MR.strings.notification_rationale_title
import com.thomaskioko.tvmaniac.testtags.notifications.NotificationRationaleTestTags
import dev.icerock.moko.resources.compose.stringResource

@Composable
public fun NotificationRationaleContent(
    onEnable: () -> Unit,
    onDismiss: () -> Unit,
    modifier: Modifier = Modifier,
) {
    Column(
        modifier = modifier
            .testTag(NotificationRationaleTestTags.BOTTOM_SHEET)
            .fillMaxWidth()
            .padding(horizontal = 24.dp, vertical = 16.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        Icon(
            imageVector = Icons.Default.Notifications,
            contentDescription = null,
            modifier = Modifier.size(48.dp),
            tint = MaterialTheme.colorScheme.secondary,
        )

        Spacer(modifier = Modifier.height(16.dp))

        Text(
            text = stringResource(notification_rationale_title),
            style = MaterialTheme.typography.headlineSmall,
            color = MaterialTheme.colorScheme.onSurface,
        )

        Spacer(modifier = Modifier.height(8.dp))

        Text(
            text = stringResource(notification_rationale_message),
            style = MaterialTheme.typography.bodyMedium,
            color = MaterialTheme.colorScheme.onSurfaceVariant,
            textAlign = TextAlign.Center,
        )

        Spacer(modifier = Modifier.height(16.dp))

        EpisodeDateSection()

        Spacer(modifier = Modifier.height(24.dp))

        Button(
            onClick = onEnable,
            modifier = Modifier
                .fillMaxWidth()
                .testTag(NotificationRationaleTestTags.ENABLE_BUTTON),
            colors = ButtonDefaults.buttonColors(
                containerColor = MaterialTheme.colorScheme.secondary,
                contentColor = MaterialTheme.colorScheme.onSecondary,
            ),
            shape = MaterialTheme.shapes.small,
        ) {
            Text(text = stringResource(notification_rationale_enable))
        }

        TextButton(
            onClick = onDismiss,
            modifier = Modifier
                .fillMaxWidth()
                .testTag(NotificationRationaleTestTags.DISMISS_BUTTON),
            colors = ButtonDefaults.textButtonColors(
                contentColor = MaterialTheme.colorScheme.secondary,
            ),
        ) {
            Text(text = stringResource(notification_rationale_not_now))
        }

        Spacer(modifier = Modifier.height(16.dp))
    }
}

@Composable
private fun EpisodeDateSection() {
    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        modifier = Modifier.fillMaxWidth(),
    ) {
        GradientDivider()

        Spacer(modifier = Modifier.height(12.dp))

        Row(
            horizontalArrangement = Arrangement.spacedBy(16.dp),
            verticalAlignment = Alignment.CenterVertically,
        ) {
            listOf(12, 13, 14).forEach { day ->
                Text(
                    text = "$day",
                    style = MaterialTheme.typography.titleLarge,
                    color = MaterialTheme.colorScheme.onSurfaceVariant,
                )
            }

            Column(
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.Center,
                modifier = Modifier
                    .size(56.dp)
                    .border(
                        width = 2.dp,
                        color = MaterialTheme.colorScheme.secondary,
                        shape = RoundedCornerShape(8.dp),
                    ),
            ) {
                Text(
                    text = "15",
                    style = MaterialTheme.typography.titleLarge,
                    color = MaterialTheme.colorScheme.onSurface,
                )
                Text(
                    text = "FEB",
                    style = MaterialTheme.typography.labelSmall,
                    color = MaterialTheme.colorScheme.onSurface,
                )
            }

            listOf(16, 17, 18).forEach { day ->
                Text(
                    text = "$day",
                    style = MaterialTheme.typography.titleLarge,
                    color = MaterialTheme.colorScheme.onSurfaceVariant,
                )
            }
        }

        Spacer(modifier = Modifier.height(12.dp))

        GradientDivider()
    }
}

@Composable
private fun GradientDivider() {
    Box(
        modifier = Modifier
            .fillMaxWidth()
            .padding(horizontal = 24.dp)
            .height(1.dp)
            .background(
                Brush.horizontalGradient(
                    colors = listOf(
                        Color.Transparent,
                        MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.3f),
                        Color.Transparent,
                    ),
                ),
            ),
    )
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun NotificationRationaleContentPreview() {
    NotificationRationaleContent(
        onEnable = {},
        onDismiss = {},
    )
}


================================================
FILE: android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/PosterPlaceholder.kt
================================================
package com.thomaskioko.tvmaniac.compose.components

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Movie
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension
import com.thomaskioko.tvmaniac.compose.components.ThemePreviews
import com.thomaskioko.tvmaniac.compose.components.TvManiacPreviewWrapperProvider

@Composable
public fun PosterPlaceholder(
    modifier: Modifier = Modifier,
    imageSize: Dp = 52.dp,
    title: String? = null,
) {
    val brush = remember {
        Brush.verticalGradient(
            colors = listOf(
                Color.Gray.copy(alpha = 0.8f),
                Color.Gray,
            ),
        )
    }
    ConstraintLayout(
        modifier = modifier
            .fillMaxSize()
            .background(brush),
    ) {
        val (icon, text) = createRefs()

        Icon(
            modifier = Modifier
                .size(imageSize)
                .constrainAs(icon) {
                    centerTo(parent)
                },
            imageVector = Icons.Outlined.Movie,
            contentDescription = title,
            tint = Color.White.copy(alpha = 0.8f),
        )

        title?.let {
            Text(
                text = it,
                modifier = Modifier
                    .padding(horizontal = 4.dp)
                    .constrainAs(text) {
                        top.linkTo(icon.bottom)
                        start.linkTo(parent.start)
                        end.linkTo(parent.end)
                        width = Dimension.fillToConstraints
                    },
                style = MaterialTheme.typography.bodyMedium,
                color = Color.White.copy(alpha = 0.8f),
                textAlign = TextAlign.Center,
                maxLines = 2,
                overflow = TextOverflow.Ellipsis,
            )
        }
    }
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun PosterPlaceholderPreview() {
    PosterPlaceholder(
        title = "Loki",
        modifier = Modifier
            .width(120.dp)
            .aspectRatio(2 / 3f),
    )
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun PosterPlaceholderNoTitlePreview() {
    PosterPlaceholder(
        modifier = Modifier
            .width(120.dp)
            .aspectRatio(2 / 3f),
    )
}


================================================
FILE: android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/ProgressIndicator.kt
================================================
package com.thomaskioko.tvmaniac.compose.components

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp

@Composable
public fun LoadingIndicator(
    modifier: Modifier = Modifier,
    color: Color = MaterialTheme.colorScheme.secondary,
    strokeWidth: Dp = 4.dp,
) {
    Box(
        modifier = modifier.fillMaxSize(),
        contentAlignment = Alignment.Center,
    ) {
        CircularProgressIndicator(
            color = color,
            strokeWidth = strokeWidth,
        )
    }
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun CircularProgressIndicatorPreview() {
    CircularProgressIndicator()
}


================================================
FILE: android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/ScanlineOverlay.kt
================================================
package com.thomaskioko.tvmaniac.compose.components

import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.thomaskioko.tvmaniac.domain.theme.Theme

public data class ScanlineConfiguration(
    val enabled: Boolean,
    val color: Color,
    val lineHeight: Dp = 2.dp,
    val opacity: Float = 0.15f,
) {
    internal companion object {
        internal val Disabled: ScanlineConfiguration =
            ScanlineConfiguration(enabled = false, color = Color.Transparent)

        internal fun terminal(): ScanlineConfiguration = ScanlineConfiguration(
            enabled = true,
            color = Color(0xFF20C020),
            opacity = 0.12f,
        )

        internal fun amber(): ScanlineConfiguration = ScanlineConfiguration(
            enabled = true,
            color = Color(0xFFFF8C00),
            opacity = 0.12f,
        )

        internal fun snow(): ScanlineConfiguration = ScanlineConfiguration(
            enabled = true,
            color = Color(0xFFFFFFFF),
            opacity = 0.08f,
        )

        internal fun crimson(): ScanlineConfiguration = ScanlineConfiguration(
            enabled = true,
            color = Color(0xFFFF4D6A),
            opacity = 0.12f,
        )
    }
}

internal fun Theme.toScanlineConfiguration(): ScanlineConfiguration = when (this) {
    Theme.TERMINAL_THEME -> ScanlineConfiguration.terminal()
    Theme.AMBER_THEME -> ScanlineConfiguration.amber()
    Theme.SNOW_THEME -> ScanlineConfiguration.snow()
    Theme.CRIMSON_THEME -> ScanlineConfiguration.crimson()
    else -> ScanlineConfiguration.Disabled
}

@Composable
public fun ScanlineOverlay(
    configuration: ScanlineConfiguration,
    modifier: Modifier = Modifier,
) {
    if (!configuration.enabled) return

    val lineColor = configuration.color.copy(alpha = configuration.opacity)

    Canvas(modifier = modifier.fillMaxSize()) {
        val lineHeightPx = configuration.lineHeight.toPx()
        val lineSpacing = lineHeightPx * 2
        var y = 0f

        while (y < size.height) {
            drawRect(
                color = lineColor,
                topLeft = Offset(0f, y),
                size = Size(size.width, lineHeightPx),
            )
            y += lineSpacing
        }
    }
}


================================================
FILE: android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/SearchTextField.kt
================================================
package com.thomaskioko.tvmaniac.compose.components

import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Clear
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import com.thomaskioko.tvmaniac.compose.components.ThemePreviews
import com.thomaskioko.tvmaniac.compose.components.TvManiacPreviewWrapperProvider
import com.thomaskioko.tvmaniac.i18n.MR.strings.cd_clear_text
import com.thomaskioko.tvmaniac.i18n.resolve
import kotlinx.coroutines.launch

@Composable
public fun SearchTextContainer(
    query: String,
    hint: String,
    lazyListState: LazyListState,
    onQueryChanged: (String) -> Unit,
    onClearQuery: () -> Unit,
    modifier: Modifier = Modifier,
    textFieldModifier: Modifier = Modifier,
    keyboardType: KeyboardType = KeyboardType.Text,
    content: @Composable () -> Unit,
) {
    val keyboardController = LocalSoftwareKeyboardController.current
    val focusManager = LocalFocusManager.current
    val coroutineScope = rememberCoroutineScope()

    val textState = remember(query) {
        mutableStateOf(TextFieldValue(query, TextRange(query.length)))
    }
    val hasFocus = remember { mutableStateOf(false) }

    LaunchedEffect(lazyListState) {
        snapshotFlow { lazyListState.isScrollInProgress }
            .collect { isScrolling ->
                if (isScrolling) {
                    keyboardController?.hide()
                    focusManager.clearFocus()
                }
            }
    }

    SearchTextFieldContent(
        modifier = modifier
            .pointerInput(Unit) {
                detectTapGestures(
                    onTap = {
                        keyboardController?.hide()
                        focusManager.clearFocus()
                    },
                )
            },
        textFieldModifier = textFieldModifier,
        textFieldValue = textState.value,
        hint = hint,
        keyboardType = keyboardType,
        onTextChanged = { newValue ->
            textState.value = newValue
            onQueryChanged(newValue.text)
        },
        onFocusChanged = { hasFocus.value = it },
        onClearClick = {
            textState.value = TextFieldValue()
            onClearQuery()
            keyboardController?.hide()
            focusManager.clearFocus()
        },
        onSubmit = {
            coroutineScope.launch {
                onQueryChanged(textState.value.text)
                keyboardController?.hide()
                focusManager.clearFocus()
            }
        },
        content = content,
    )
}

@Composable
private fun SearchTextFieldContent(
    textFieldValue: TextFieldValue,
    hint: String,
    keyboardType: KeyboardType,
    onTextChanged: (TextFieldValue) -> Unit,
    onFocusChanged: (Boolean) -> Unit,
    onClearClick: () -> Unit,
    onSubmit: () -> Unit,
    modifier: Modifier = Modifier,
    textFieldModifier: Modifier = Modifier,
    content: @Composable () -> Unit,
) {
    Column(modifier = modifier) {
        SearchTextField(
            modifier = textFieldModifier
                .padding(horizontal = 16.dp),
            onFocusChanged = onFocusChanged,
            textFieldValue = textFieldValue,
            onTextChanged = onTextChanged,
            hint = hint,
            keyboardType = keyboardType,
            onSubmit = onSubmit,
            onClearClick = onClearClick,
        )

        content()
    }
}

@Composable
private fun SearchTextField(
    onFocusChanged: (Boolean) -> Unit,
    textFieldValue: TextFieldValue,
    onTextChanged: (TextFieldValue) -> Unit,
    hint: String,
    keyboardType: KeyboardType,
    onSubmit: () -> Unit,
    onClearClick: () -> Unit,
    modifier: Modifier = Modifier,
    shape: Shape = MaterialTheme.shapes.medium,
) {
    OutlinedTextField(
        modifier = modifier
            .fillMaxWidth()
            .onFocusChanged { onFocusChanged(it.isFocused) },
        value = textFieldValue,
        onValueChange = onTextChanged,
        placeholder = {
            Text(
                text = hint,
                style = MaterialTheme.typography.bodyMedium.copy(
                    color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f),
                ),
            )
        },
        singleLine = true,
        maxLines = 1,
        textStyle = MaterialTheme.typography.bodyMedium,
        keyboardOptions = KeyboardOptions(
            keyboardType = keyboardType,
            imeAction = ImeAction.Search,
        ),
        keyboardActions = KeyboardActions(
            onSearch = { onSubmit() },
        ),
        leadingIcon = {
            Icon(
                imageVector = Icons.Filled.Search,
                contentDescription = null,
                tint = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f),
            )
        },
        trailingIcon = {
            IconButton(onClick = onClearClick) {
                Icon(
                    imageVector = Icons.Filled.Clear,
                    contentDescription = cd_clear_text.resolve(LocalContext.current),
                    tint = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f),
                )
            }
        },
        shape = shape,
        colors = TextFieldDefaults.colors(
            focusedIndicatorColor = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f),
            unfocusedIndicatorColor = MaterialTheme.colorScheme.outline.copy(alpha = 0.3f),
            focusedContainerColor = MaterialTheme.colorScheme.surface,
            unfocusedContainerColor = MaterialTheme.colorScheme.surface,
            cursorColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f),
        ),
    )
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun SearchTextFieldPreview() {
    SearchTextContainer(
        hint = "Enter Show Title",
        query = "",
        lazyListState = remember { LazyListState() },
        onClearQuery = {},
        onQueryChanged = {},
        content = {},
    )
}


================================================
FILE: android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/SegmentedProgressBar.kt
================================================
package com.thomaskioko.tvmaniac.compose.components

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.thomaskioko.tvmaniac.compose.components.ThemePreviews
import com.thomaskioko.tvmaniac.compose.components.TvManiacPreviewWrapperProvider
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf

@Composable
public fun SegmentedProgressBar(
    segmentProgress: ImmutableList<Float>,
    modifier: Modifier = Modifier,
    height: Dp = 6.dp,
    segmentGap: Dp = 4.dp,
    trackColor: Color = MaterialTheme.colorScheme.secondary.copy(alpha = 0.3f),
) {
    if (segmentProgress.isEmpty()) return

    Row(
        modifier = modifier.fillMaxWidth(),
        horizontalArrangement = Arrangement.spacedBy(segmentGap),
    ) {
        segmentProgress.forEach { progress ->
            ProgressSegment(
                progress = progress,
                modifier = Modifier.weight(1f),
                height = height,
                trackColor = trackColor,
            )
        }
    }
}

@Composable
private fun ProgressSegment(
    progress: Float,
    modifier: Modifier = Modifier,
    height: Dp = 6.dp,
    trackColor: Color,
) {
    val shape = RoundedCornerShape(height / 2)
    val progressColor = MaterialTheme.colorScheme.secondary

    Box(
        modifier = modifier
            .height(height)
            .clip(shape)
            .background(trackColor),
    ) {
        Box(
            modifier = Modifier
                .fillMaxWidth(fraction = progress.coerceIn(0f, 1f))
                .height(height)
                .clip(shape)
                .background(progressColor),
        )
    }
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun SegmentedProgressBarPreview() {
    SegmentedProgressBar(
        segmentProgress = persistentListOf(1f, 0.5f, 0f),
        modifier = Modifier.fillMaxWidth(),
    )
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun SegmentedProgressBarSinglePreview() {
    SegmentedProgressBar(
        segmentProgress = persistentListOf(0.75f),
        modifier = Modifier.fillMaxWidth(),
    )
}


================================================
FILE: android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/SheetDragHandle.kt
================================================
package com.thomaskioko.tvmaniac.compose.components

import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
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.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.KeyboardArrowDown
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import com.thomaskioko.tvmaniac.compose.components.ThemePreviews
import com.thomaskioko.tvmaniac.compose.components.TvManiacPreviewWrapperProvider
import com.thomaskioko.tvmaniac.i18n.MR.strings.cd_expand_collapse
import com.thomaskioko.tvmaniac.i18n.resolve

@Composable
public fun SheetDragHandle(
    onClick: () -> Unit,
    imageVector: ImageVector,
    modifier: Modifier = Modifier,
    title: String? = null,
    textAlign: TextAlign? = null,
    tint: Color = LocalContentColor.current,
) {
    val context = LocalContext.current

    Box(
        modifier = modifier
            .fillMaxWidth()
            .statusBarsPadding()
            .height(56.dp)
            .background(Color.Transparent),
    ) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .align(Alignment.CenterStart)
                .padding(start = 16.dp),
            verticalAlignment = Alignment.CenterVertically,
        ) {
            Icon(
                imageVector = imageVector,
                tint = tint,
                contentDescription = cd_expand_collapse.resolve(context),
                modifier = Modifier
                    .size(24.dp)
                    .clickable { onClick() },
            )

            Spacer(modifier = Modifier.width(8.dp))

            title?.let {
                Text(
                    text = title,
                    style = MaterialTheme.typography.titleMedium,
                    maxLines = 1,
                    overflow = TextOverflow.Ellipsis,
                    textAlign = textAlign,
                    modifier = Modifier.fillMaxWidth(),
                )
            }
        }
    }
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun CustomSheetDragHandlePreview() {
    SheetDragHandle(
        title = "Drag Handle",
        onClick = {},
        imageVector = Icons.Outlined.KeyboardArrowDown,
    )
}


================================================
FILE: android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/ShowLinearProgressIndicator.kt
================================================
package com.thomaskioko.tvmaniac.compose.components

import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import com.thomaskioko.tvmaniac.compose.theme.green

@Composable
public fun ShowLinearProgressIndicator(
    progress: Float,
    modifier: Modifier = Modifier,
) {
    LinearProgressIndicator(
        progress = { progress },
        color = MaterialTheme.colorScheme.secondary,
        trackColor = if (progress == 1f) {
            green.copy(alpha = 0.5F)
        } else {
            MaterialTheme.colorScheme.secondary.copy(
                alpha = 0.5F,
            )
        },
        strokeCap = StrokeCap.Butt,
        drawStopIndicator = {},
        gapSize = 0.dp,
        modifier = modifier,
    )
}

@ThemePreviews
@PreviewWrapper(TvManiacPreviewWrapperProvider::class)
@Composable
private fun ShowLinearProgressIndicatorPreview() {
    ShowLinearProgressIndicator(progress = 0.6f)
}


================================================
FILE: android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/Snackbar.kt
================================================
package com.thomaskioko.tvmaniac.compose.components

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.tween
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.offset
import androi
Download .txt
Showing preview only (273K chars total). Download the full file or copy to clipboard to get everything.
gitextract_xytv9ppo/

├── .editorconfig
├── .geminiignore
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.yml
│   │   ├── config.yml
│   │   └── feature_request.yml
│   ├── actions/
│   │   ├── setup-android-release/
│   │   │   └── action.yml
│   │   ├── setup-gradle/
│   │   │   └── action.yml
│   │   ├── setup-ios/
│   │   │   └── action.yml
│   │   └── setup-ios-release/
│   │       └── action.yml
│   ├── release.yml
│   ├── renovate.json
│   └── workflows/
│       ├── baseline-profile.yml
│       ├── beta-release.yml
│       ├── ci.yml
│       ├── compare-screenshot.yml
│       ├── daily-build.yml
│       ├── nightly-integration-tests.yml
│       ├── promote-release.yml
│       ├── release.yml
│       └── store-screenshot.yml
├── .gitignore
├── .idea/
│   ├── codeStyles/
│   │   ├── Project.xml
│   │   └── codeStyleConfig.xml
│   └── dictionaries/
│       └── project.xml
├── .ruby-version
├── .swiftformat
├── .swiftlint.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── GEMINI.md
├── Gemfile
├── LICENSE
├── README.md
├── android-designsystem/
│   ├── build.gradle.kts
│   └── src/
│       ├── debug/
│       │   └── res/
│       │       └── values/
│       │           └── strings.xml
│       ├── main/
│       │   ├── kotlin/
│       │   │   └── com/
│       │   │       └── thomaskioko/
│       │   │           └── tvmaniac/
│       │   │               └── compose/
│       │   │                   ├── components/
│       │   │                   │   ├── Background.kt
│       │   │                   │   ├── BadgeChip.kt
│       │   │                   │   ├── Buttons.kt
│       │   │                   │   ├── Card.kt
│       │   │                   │   ├── Chip.kt
│       │   │                   │   ├── Dialogs.kt
│       │   │                   │   ├── EmptyLayout.kt
│       │   │                   │   ├── ErrorLayout.kt
│       │   │                   │   ├── FilterChipSection.kt
│       │   │                   │   ├── GradientScrim.kt
│       │   │                   │   ├── Image.kt
│       │   │                   │   ├── NavigationBar.kt
│       │   │                   │   ├── NotificationRationaleContent.kt
│       │   │                   │   ├── PosterPlaceholder.kt
│       │   │                   │   ├── ProgressIndicator.kt
│       │   │                   │   ├── ScanlineOverlay.kt
│       │   │                   │   ├── SearchTextField.kt
│       │   │                   │   ├── SegmentedProgressBar.kt
│       │   │                   │   ├── SheetDragHandle.kt
│       │   │                   │   ├── ShowLinearProgressIndicator.kt
│       │   │                   │   ├── Snackbar.kt
│       │   │                   │   ├── Text.kt
│       │   │                   │   ├── TextTitlePill.kt
│       │   │                   │   ├── TopBar.kt
│       │   │                   │   ├── TvManiacBottomSheet.kt
│       │   │                   │   └── TvManiacPreviewWrapperProvider.kt
│       │   │                   ├── extensions/
│       │   │                   │   ├── GradientExtensions.kt
│       │   │                   │   ├── LazyListExtensions.kt
│       │   │                   │   ├── PaddingValuesExtentions.kt
│       │   │                   │   └── ScrimExtentions.kt
│       │   │                   ├── theme/
│       │   │                   │   ├── Background.kt
│       │   │                   │   ├── Colors.kt
│       │   │                   │   ├── Shape.kt
│       │   │                   │   ├── Theme.kt
│       │   │                   │   └── Type.kt
│       │   │                   └── util/
│       │   │                       ├── AutoAdvanceLocal.kt
│       │   │                       └── DynamicTheming.kt
│       │   └── res/
│       │       └── values/
│       │           └── strings.xml
│       └── test/
│           └── kotlin/
│               └── com/
│                   └── thomaskioko/
│                       └── tvmaniac/
│                           └── compose/
│                               └── roborazzi/
│                                   ├── NotificationRationaleContentScreenshotTest.kt
│                                   └── TvManiacSnackBarScreenshotTest.kt
├── api/
│   ├── tmdb/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── tmdb/
│   │   │                               └── api/
│   │   │                                   ├── TmdbConfig.kt
│   │   │                                   ├── TmdbSeasonDetailsNetworkDataSource.kt
│   │   │                                   ├── TmdbShowDetailsNetworkDataSource.kt
│   │   │                                   ├── TmdbShowsNetworkDataSource.kt
│   │   │                                   └── model/
│   │   │                                       ├── CreditsResponse.kt
│   │   │                                       ├── EpisodesResponse.kt
│   │   │                                       ├── GenreResponse.kt
│   │   │                                       ├── ImagesResponse.kt
│   │   │                                       ├── LastEpisodeToAirResponse.kt
│   │   │                                       ├── NetworksResponse.kt
│   │   │                                       ├── NextEpisodeToAirResponse.kt
│   │   │                                       ├── SeasonsResponse.kt
│   │   │                                       ├── TmdbGenreResult.kt
│   │   │                                       ├── TmdbSeasonDetailsResponse.kt
│   │   │                                       ├── TmdbShowDetailsResponse.kt
│   │   │                                       ├── TmdbShowResponse.kt
│   │   │                                       ├── VideosResponse.kt
│   │   │                                       └── WatchProvidersResult.kt
│   │   └── implementation/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           ├── androidMain/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── tmdb/
│   │           │                       └── implementation/
│   │           │                           └── TmdbPlatformBindingContainer.kt
│   │           ├── commonMain/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── tmdb/
│   │           │                       └── implementation/
│   │           │                           ├── DefaultTmdbSeasonDetailsNetworkDataSource.kt
│   │           │                           ├── DefaultTmdbShowDetailsNetworkDataSource.kt
│   │           │                           ├── DefaultTmdbShowsNetworkDataSource.kt
│   │           │                           ├── TmdbBindingContainer.kt
│   │           │                           └── TmdbClient.kt
│   │           └── iosMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── tmdb/
│   │                                   └── implementation/
│   │                                       └── TmdbPlatformBindingContainer.kt
│   └── trakt/
│       ├── api/
│       │   ├── build.gradle.kts
│       │   └── src/
│       │       └── commonMain/
│       │           └── kotlin/
│       │               └── com/
│       │                   └── thomaskioko/
│       │                       └── tvmaniac/
│       │                           └── trakt/
│       │                               └── api/
│       │                                   ├── TraktCalendarRemoteDataSource.kt
│       │                                   ├── TraktConfig.kt
│       │                                   ├── TraktEpisodeHistoryRemoteDataSource.kt
│       │                                   ├── TraktListRemoteDataSource.kt
│       │                                   ├── TraktShowsRemoteDataSource.kt
│       │                                   ├── TraktSyncRemoteDataSource.kt
│       │                                   ├── TraktTokenRemoteDataSource.kt
│       │                                   ├── TraktUserRemoteDataSource.kt
│       │                                   └── model/
│       │                                       ├── AccessTokenBody.kt
│       │                                       ├── RefreshAccessTokenBody.kt
│       │                                       ├── TraktAccessRefreshTokenResponse.kt
│       │                                       ├── TraktAccessTokenResponse.kt
│       │                                       ├── TraktAddShowRequest.kt
│       │                                       ├── TraktAddShowToListResponse.kt
│       │                                       ├── TraktCalendarResponse.kt
│       │                                       ├── TraktCreateListRequest.kt
│       │                                       ├── TraktCreateListResponse.kt
│       │                                       ├── TraktFollowedShowResponse.kt
│       │                                       ├── TraktGenreResponse.kt
│       │                                       ├── TraktLastActivitiesResponse.kt
│       │                                       ├── TraktNextEpisodeResponse.kt
│       │                                       ├── TraktPeopleResponse.kt
│       │                                       ├── TraktPersonalListsResponse.kt
│       │                                       ├── TraktRemoveShowFromListResponse.kt
│       │                                       ├── TraktSeasonEpisodesResponse.kt
│       │                                       ├── TraktSeasonsResponse.kt
│       │                                       ├── TraktShowsResponse.kt
│       │                                       ├── TraktSyncModels.kt
│       │                                       ├── TraktUserResponse.kt
│       │                                       ├── TraktUserStatsResponse.kt
│       │                                       ├── TraktVideosResponse.kt
│       │                                       └── TraktWatchedProgressResponse.kt
│       └── implementation/
│           ├── build.gradle.kts
│           └── src/
│               ├── androidMain/
│               │   └── kotlin/
│               │       └── com/
│               │           └── thomaskioko/
│               │               └── trakt/
│               │                   └── service/
│               │                       └── implementation/
│               │                           └── TraktPlatformBindingContainer.kt
│               ├── commonMain/
│               │   └── kotlin/
│               │       └── com/
│               │           └── thomaskioko/
│               │               └── trakt/
│               │                   └── service/
│               │                       └── implementation/
│               │                           ├── TraktAuthPlugin.kt
│               │                           ├── TraktBindingContainer.kt
│               │                           ├── TraktHttpClient.kt
│               │                           └── api/
│               │                               ├── DefaultTraktCalendarRemoteDataSource.kt
│               │                               ├── DefaultTraktEpisodeRemoteDataSource.kt
│               │                               ├── DefaultTraktListRemoteDataSource.kt
│               │                               ├── DefaultTraktShowsRemoteDataSource.kt
│               │                               ├── DefaultTraktSyncRemoteDataSource.kt
│               │                               ├── DefaultTraktTokenRemoteDataSource.kt
│               │                               └── DefaultTraktUserRemoteDataSource.kt
│               ├── commonTest/
│               │   └── kotlin/
│               │       └── com/
│               │           └── thomaskioko/
│               │               └── trakt/
│               │                   └── service/
│               │                       └── implementation/
│               │                           └── TraktAuthGuardPluginTest.kt
│               ├── iosMain/
│               │   └── kotlin/
│               │       └── com/
│               │           └── thomaskioko/
│               │               └── trakt/
│               │                   └── service/
│               │                       └── implementation/
│               │                           └── TraktPlatformBindingContainer.kt
│               └── jvmTest/
│                   ├── kotlin/
│                   │   └── com/
│                   │       └── thomaskioko/
│                   │           └── trakt/
│                   │               └── service/
│                   │                   └── implementation/
│                   │                       ├── TestResourceLoader.jvm.kt
│                   │                       └── api/
│                   │                           └── DefaultTraktListRemoteDataSourceTest.kt
│                   └── resources/
│                       ├── trakt_add_show_response.json
│                       ├── trakt_error_response.json
│                       └── trakt_user_response.json
├── app/
│   ├── benchmark-rules.pro
│   ├── build.gradle.kts
│   ├── lint-baseline.xml
│   ├── proguard-rules.pro
│   └── src/
│       ├── androidTest/
│       │   └── kotlin/
│       │       └── com/
│       │           └── thomaskioko/
│       │               └── tvmaniac/
│       │                   └── app/
│       │                       └── test/
│       │                           └── runner/
│       │                               └── TvManiacInstrumentationRunner.kt
│       ├── debug/
│       │   ├── AndroidManifest.xml
│       │   ├── kotlin/
│       │   │   └── com/
│       │   │       └── thomaskioko/
│       │   │           └── tvmaniac/
│       │   │               └── app/
│       │   │                   └── debug/
│       │   │                       ├── DebugNotificationIconProvider.kt
│       │   │                       ├── DebugNotificationInitializer.kt
│       │   │                       └── di/
│       │   │                           └── DebugNotificationInitializerBindingContainer.kt
│       │   └── res/
│       │       └── drawable/
│       │           ├── ic_app_launcher.xml
│       │           ├── ic_debug_bug.xml
│       │           └── ic_launcher_foreground.xml
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   ├── generated/
│       │   │   └── baselineProfiles/
│       │   │       ├── baseline-prof.txt
│       │   │       └── startup-prof.txt
│       │   ├── kotlin/
│       │   │   └── com/
│       │   │       └── thomaskioko/
│       │   │           └── tvmaniac/
│       │   │               └── app/
│       │   │                   ├── MainActivity.kt
│       │   │                   ├── TvManicApplication.kt
│       │   │                   ├── di/
│       │   │                   │   ├── ActivityGraph.kt
│       │   │                   │   └── ApplicationGraph.kt
│       │   │                   └── util/
│       │   │                       ├── AppNotificationIconProvider.kt
│       │   │                       └── TvManiacWorkerFactory.kt
│       │   └── res/
│       │       ├── drawable/
│       │       │   ├── ic_app_launcher.xml
│       │       │   ├── ic_launcher_background.xml
│       │       │   ├── ic_launcher_foreground.xml
│       │       │   └── ic_launcher_monochrome.xml
│       │       ├── mipmap-anydpi-v26/
│       │       │   ├── ic_launcher.xml
│       │       │   └── ic_launcher_round.xml
│       │       ├── values/
│       │       │   ├── colors.xml
│       │       │   └── themes.xml
│       │       ├── values-night/
│       │       │   ├── colors.xml
│       │       │   └── themes.xml
│       │       └── xml/
│       │           ├── backup_rules.xml
│       │           └── data_extraction_rules.xml
│       ├── sharedTest/
│       │   └── kotlin/
│       │       └── com/
│       │           └── thomaskioko/
│       │               └── tvmaniac/
│       │                   └── app/
│       │                       └── test/
│       │                           ├── BaseAppFlowTest.kt
│       │                           ├── TestAppComponent.kt
│       │                           ├── TvManiacTestApplication.kt
│       │                           └── compose/
│       │                               ├── TvManiacTestActivity.kt
│       │                               ├── flows/
│       │                               │   ├── calendar/
│       │                               │   │   └── CalendarFlowTest.kt
│       │                               │   ├── discover/
│       │                               │   │   ├── DiscoverToSeasonDetailsFlowTest.kt
│       │                               │   │   └── DiscoverToShowDetailsFollowFlowTest.kt
│       │                               │   ├── library/
│       │                               │   │   └── LibraryFlowTest.kt
│       │                               │   ├── search/
│       │                               │   │   └── SearchFlowTest.kt
│       │                               │   ├── seasons/
│       │                               │   │   └── SeasonFlowTest.kt
│       │                               │   ├── settings/
│       │                               │   │   └── SettingsFlowTest.kt
│       │                               │   ├── sheet/
│       │                               │   │   └── EpisodeSheetFlowTest.kt
│       │                               │   ├── showdetails/
│       │                               │   │   └── ShowDetailsFeaturesFlowTest.kt
│       │                               │   ├── upnext/
│       │                               │   │   └── UpNextFlowTests.kt
│       │                               │   └── userlists/
│       │                               │       └── UserListFlowTests.kt
│       │                               ├── journey/
│       │                               │   ├── AuthenticatedUserJourneyTest.kt
│       │                               │   └── UnauthenticatedUserJourneyTest.kt
│       │                               ├── robot/
│       │                               │   ├── CalendarRobot.kt
│       │                               │   ├── DiscoverRobot.kt
│       │                               │   ├── EpisodeSheetRobot.kt
│       │                               │   ├── HomeRobot.kt
│       │                               │   ├── LibraryRobot.kt
│       │                               │   ├── ProfileRobot.kt
│       │                               │   ├── ProgressRobot.kt
│       │                               │   ├── RootRobot.kt
│       │                               │   ├── SearchRobot.kt
│       │                               │   ├── SeasonDetailsRobot.kt
│       │                               │   ├── SettingsRobot.kt
│       │                               │   └── ShowDetailsRobot.kt
│       │                               └── stubs/
│       │                                   └── Scenarios.kt
│       └── test/
│           └── kotlin/
│               └── com/
│                   └── thomaskioko/
│                       └── tvmaniac/
│                           └── app/
│                               └── test/
│                                   └── graph/
│                                       ├── GraphFactories.kt
│                                       └── NavigationRouteTest.kt
├── benchmark/
│   ├── build.gradle.kts
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           └── kotlin/
│               └── com/
│                   └── thomaskioko/
│                       └── tvmaniac/
│                           └── benchmark/
│                               ├── Common.kt
│                               ├── baselineprofile/
│                               │   └── BaselineProfileGenerator.kt
│                               └── benchmark/
│                                   └── StartupBenchmarks.kt
├── build.gradle.kts
├── cliff.toml
├── compose-stability.conf
├── core/
│   ├── appconfig/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── appconfig/
│   │   │                               └── ApplicationInfo.kt
│   │   └── implementation/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           ├── androidMain/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── appconfig/
│   │           │                       └── AndroidAppConfigBindingContainer.kt
│   │           ├── commonMain/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── appconfig/
│   │           │                       ├── DefaultTmdbConfig.kt
│   │           │                       └── DefaultTraktConfig.kt
│   │           └── iosMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── appconfig/
│   │                                   └── IosAppConfigBindingContainer.kt
│   ├── base/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       ├── androidMain/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── core/
│   │       │                       └── base/
│   │       │                           └── di/
│   │       │                               └── BaseAndroidBindingContainer.kt
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── core/
│   │                               └── base/
│   │                                   ├── ActivityScope.kt
│   │                                   ├── AppInitializers.kt
│   │                                   ├── Initializer.kt
│   │                                   ├── Qualifiers.kt
│   │                                   ├── di/
│   │                                   │   ├── BaseBindingContainer.kt
│   │                                   │   └── InitializerMultibindings.kt
│   │                                   ├── extensions/
│   │                                   │   ├── Combine.kt
│   │                                   │   ├── DecomposeUtils.kt
│   │                                   │   ├── Lazy.kt
│   │                                   │   └── ParallelUtils.kt
│   │                                   ├── interactor/
│   │                                   │   └── Interactor.kt
│   │                                   └── model/
│   │                                       └── AppCoroutineDispatchers.kt
│   ├── connectivity/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── core/
│   │   │                               └── connectivity/
│   │   │                                   └── api/
│   │   │                                       └── InternetConnectionChecker.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── androidMain/
│   │   │       │   ├── AndroidManifest.xml
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── core/
│   │   │       │                       └── connectivity/
│   │   │       │                           └── implementation/
│   │   │       │                               └── PlatformInternetConnectionChecker.android.kt
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── core/
│   │   │       │                       └── connectivity/
│   │   │       │                           └── implementation/
│   │   │       │                               └── PlatformInternetConnectionChecker.kt
│   │   │       ├── iosMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── core/
│   │   │       │                       └── connectivity/
│   │   │       │                           └── implementation/
│   │   │       │                               └── PlatformInternetConnectionChecker.ios.kt
│   │   │       └── jvmMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── core/
│   │   │                               └── connectivity/
│   │   │                                   └── implementation/
│   │   │                                       └── PlatformInternetConnectionChecker.jvm.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── core/
│   │                                   └── connectivity/
│   │                                       └── testing/
│   │                                           └── FakeInternetConnectionChecker.kt
│   ├── imageloading/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── imageloading/
│   │   │                               └── api/
│   │   │                                   └── ImageQualityProvider.kt
│   │   └── implementation/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── main/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── imageloading/
│   │                                   └── implementation/
│   │                                       ├── CoilImageLoaderFactory.kt
│   │                                       ├── CoilImageLoaderInitializer.kt
│   │                                       ├── DefaultImageQualityProvider.kt
│   │                                       ├── di/
│   │                                       │   ├── CoilImageLoaderInitializerBindingContainer.kt
│   │                                       │   └── ImageLoadingBindingContainer.kt
│   │                                       └── interceptors/
│   │                                           └── TmdbInterceptor.kt
│   ├── integration/
│   │   ├── infra/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── androidHostTest/
│   │   │       │   ├── AndroidManifest.xml
│   │   │       │   ├── kotlin/
│   │   │       │   │   └── com/
│   │   │       │   │       └── thomaskioko/
│   │   │       │   │           └── tvmaniac/
│   │   │       │   │               └── testing/
│   │   │       │   │                   └── integration/
│   │   │       │   │                       ├── EndpointsCatalogTest.kt
│   │   │       │   │                       ├── FixtureLoaderTest.kt
│   │   │       │   │                       └── MockEngineHandlerTest.kt
│   │   │       │   └── resources/
│   │   │       │       └── fixtures/
│   │   │       │           └── test/
│   │   │       │               └── hello.json
│   │   │       ├── androidMain/
│   │   │       │   ├── kotlin/
│   │   │       │   │   └── com/
│   │   │       │   │       └── thomaskioko/
│   │   │       │   │           └── tvmaniac/
│   │   │       │   │               └── testing/
│   │   │       │   │                   └── integration/
│   │   │       │   │                       ├── Endpoints.kt
│   │   │       │   │                       ├── MockEngineHandler.kt
│   │   │       │   │                       ├── SearchStubs.kt
│   │   │       │   │                       ├── ShowFixtures.kt
│   │   │       │   │                       ├── bindings/
│   │   │       │   │                       │   ├── TestAuthBindingContainer.kt
│   │   │       │   │                       │   ├── TestConnectivityBindingContainer.kt
│   │   │       │   │                       │   ├── TestDateTimeBindingContainer.kt
│   │   │       │   │                       │   ├── TestDispatcherBindingContainer.kt
│   │   │       │   │                       │   ├── TestImageLoaderBindingContainer.kt
│   │   │       │   │                       │   ├── TestInitializerBindingContainer.kt
│   │   │       │   │                       │   ├── TestLoggerBindingContainer.kt
│   │   │       │   │                       │   ├── TestNotificationBindingContainer.kt
│   │   │       │   │                       │   ├── TestTmdbBindingContainer.kt
│   │   │       │   │                       │   ├── TestTraktAuthManagerBindingContainer.kt
│   │   │       │   │                       │   ├── TestTraktBindingContainer.kt
│   │   │       │   │                       │   └── TestWorkerBindingContainer.kt
│   │   │       │   │                       └── util/
│   │   │       │   │                           └── FixtureLoader.kt
│   │   │       │   └── resources/
│   │   │       │       └── fixtures/
│   │   │       │           ├── empty_array.json
│   │   │       │           ├── tmdb/
│   │   │       │           │   ├── credits/
│   │   │       │           │   │   ├── error.json
│   │   │       │           │   │   └── success.json
│   │   │       │           │   ├── details/
│   │   │       │           │   │   ├── error.json
│   │   │       │           │   │   └── success.json
│   │   │       │           │   ├── discover/
│   │   │       │           │   │   ├── error.json
│   │   │       │           │   │   └── success.json
│   │   │       │           │   └── watchproviders/
│   │   │       │           │       ├── error.json
│   │   │       │           │       └── success.json
│   │   │       │           └── trakt/
│   │   │       │               ├── calendar/
│   │   │       │               │   ├── error.json
│   │   │       │               │   └── success.json
│   │   │       │               ├── episodes/
│   │   │       │               │   ├── season1/
│   │   │       │               │   │   ├── error.json
│   │   │       │               │   │   └── success.json
│   │   │       │               │   └── season2/
│   │   │       │               │       ├── error.json
│   │   │       │               │       └── success.json
│   │   │       │               ├── genres/
│   │   │       │               │   ├── error.json
│   │   │       │               │   └── success.json
│   │   │       │               ├── search/
│   │   │       │               │   ├── error.json
│   │   │       │               │   └── success.json
│   │   │       │               ├── seasons/
│   │   │       │               │   ├── error.json
│   │   │       │               │   └── success.json
│   │   │       │               ├── shows/
│   │   │       │               │   ├── details/
│   │   │       │               │   │   ├── error.json
│   │   │       │               │   │   └── success.json
│   │   │       │               │   ├── favorite/
│   │   │       │               │   │   ├── error.json
│   │   │       │               │   │   └── success.json
│   │   │       │               │   ├── people/
│   │   │       │               │   │   ├── error.json
│   │   │       │               │   │   └── success.json
│   │   │       │               │   ├── popular/
│   │   │       │               │   │   ├── error.json
│   │   │       │               │   │   └── success.json
│   │   │       │               │   ├── progress/
│   │   │       │               │   │   ├── refreshed/
│   │   │       │               │   │   │   ├── error.json
│   │   │       │               │   │   │   └── success.json
│   │   │       │               │   │   └── watched/
│   │   │       │               │   │       ├── error.json
│   │   │       │               │   │       └── success.json
│   │   │       │               │   ├── related/
│   │   │       │               │   │   ├── error.json
│   │   │       │               │   │   └── success.json
│   │   │       │               │   ├── trending/
│   │   │       │               │   │   ├── error.json
│   │   │       │               │   │   └── success.json
│   │   │       │               │   └── videos/
│   │   │       │               │       ├── error.json
│   │   │       │               │       └── success.json
│   │   │       │               ├── sync/
│   │   │       │               │   ├── error.json
│   │   │       │               │   ├── history/
│   │   │       │               │   │   ├── error.json
│   │   │       │               │   │   └── success.json
│   │   │       │               │   └── success.json
│   │   │       │               └── users/
│   │   │       │                   ├── lists/
│   │   │       │                   │   ├── create/
│   │   │       │                   │   │   ├── error.json
│   │   │       │                   │   │   └── success.json
│   │   │       │                   │   ├── error.json
│   │   │       │                   │   ├── items/
│   │   │       │                   │   │   ├── add/
│   │   │       │                   │   │   │   ├── error.json
│   │   │       │                   │   │   │   └── success.json
│   │   │       │                   │   │   └── remove/
│   │   │       │                   │   │       ├── error.json
│   │   │       │                   │   │       └── success.json
│   │   │       │                   │   └── success.json
│   │   │       │                   ├── me/
│   │   │       │                   │   ├── error.json
│   │   │       │                   │   └── success.json
│   │   │       │                   ├── stats/
│   │   │       │                   │   ├── error.json
│   │   │       │                   │   └── success.json
│   │   │       │                   └── watchlist/
│   │   │       │                       ├── error.json
│   │   │       │                       └── success.json
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── testing/
│   │   │       │                       └── di/
│   │   │       │                           ├── FakeAppConfigBindingContainer.kt
│   │   │       │                           └── TestScope.kt
│   │   │       ├── iosMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── testing/
│   │   │       │                       └── di/
│   │   │       │                           ├── FakeIosPlatformBindingContainer.kt
│   │   │       │                           ├── RunTestWithGraph.kt
│   │   │       │                           └── TestGraph.kt
│   │   │       ├── jvmAndIosMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── testing/
│   │   │       │                       └── di/
│   │   │       │                           └── FakeAppBindingContainer.kt
│   │   │       ├── jvmMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── testing/
│   │   │       │                       └── di/
│   │   │       │                           ├── RunTestWithGraph.kt
│   │   │       │                           ├── TestGraph.kt
│   │   │       │                           └── TestJvmPlatformBindingContainer.kt
│   │   │       └── jvmTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── testing/
│   │   │                               └── di/
│   │   │                                   └── TestJvmGraphTest.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── androidMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── testing/
│   │                                   └── integration/
│   │                                       └── ui/
│   │                                           ├── BaseRobot.kt
│   │                                           └── SystemDialogUtil.kt
│   ├── locale/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── locale/
│   │   │                               └── api/
│   │   │                                   ├── Language.kt
│   │   │                                   └── LocaleProvider.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── androidDeviceTest/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── locale/
│   │   │       │                       └── implementation/
│   │   │       │                           └── PlatformLocaleProviderAndroidTest.kt
│   │   │       ├── androidMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── locale/
│   │   │       │                       └── implementation/
│   │   │       │                           └── PlatformLocaleProvider.android.kt
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── locale/
│   │   │       │                       └── implementation/
│   │   │       │                           ├── DefaultLocaleProvider.kt
│   │   │       │                           └── PlatformLocaleProvider.kt
│   │   │       ├── commonTest/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── locale/
│   │   │       │                       └── implementation/
│   │   │       │                           └── PlatformLocaleProviderTest.kt
│   │   │       ├── iosMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── locale/
│   │   │       │                       └── implementation/
│   │   │       │                           └── PlatformLocaleProvider.ios.kt
│   │   │       ├── iosTest/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── locale/
│   │   │       │                       └── implementation/
│   │   │       │                           └── PlatformLocaleProviderIosTest.kt
│   │   │       ├── jvmMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── locale/
│   │   │       │                       └── implementation/
│   │   │       │                           └── PlatformLocaleProvider.jvm.kt
│   │   │       └── jvmTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── locale/
│   │   │                               └── implementation/
│   │   │                                   └── PlatformLocaleProviderJvmTest.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── locale/
│   │                                   └── testing/
│   │                                       └── FakeLocaleProvider.kt
│   ├── logger/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── core/
│   │   │       │                       └── logger/
│   │   │       │                           ├── CrashReporter.kt
│   │   │       │                           └── Logger.kt
│   │   │       └── iosMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── core/
│   │   │                               └── logger/
│   │   │                                   ├── CrashReportingBridge.kt
│   │   │                                   └── CrashReportingBridgeHolder.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── androidMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── core/
│   │   │       │                       └── logger/
│   │   │       │                           ├── AndroidCrashReporter.kt
│   │   │       │                           └── AndroidLoggerBindingContainer.kt
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── core/
│   │   │       │                       └── logger/
│   │   │       │                           ├── CompositeLogger.kt
│   │   │       │                           ├── FirebaseCrashLogger.kt
│   │   │       │                           ├── KermitLogger.kt
│   │   │       │                           └── LoggingInitializer.kt
│   │   │       └── iosMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── core/
│   │   │                               └── logger/
│   │   │                                   ├── IosCrashReporter.kt
│   │   │                                   ├── IosCrashReporterBindingContainer.kt
│   │   │                                   └── NoOpCrashReportingBridge.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── core/
│   │                                   └── logger/
│   │                                       └── fixture/
│   │                                           ├── FakeCrashReporter.kt
│   │                                           └── FakeLogger.kt
│   ├── network-util/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── core/
│   │   │       │                       └── networkutil/
│   │   │       │                           └── api/
│   │   │       │                               ├── ApiRateLimiter.kt
│   │   │       │                               ├── extensions/
│   │   │       │                               │   ├── ApiRateLimiterExtensions.kt
│   │   │       │                               │   ├── ApiResponseExtensions.kt
│   │   │       │                               │   ├── InternetConnectionPlugin.kt
│   │   │       │                               │   └── StoreExtensions.kt
│   │   │       │                               └── model/
│   │   │       │                                   ├── ApiExceptions.kt
│   │   │       │                                   ├── ApiResponse.kt
│   │   │       │                                   ├── AuthenticationException.kt
│   │   │       │                                   ├── HttpExceptions.kt
│   │   │       │                                   ├── NoInternetException.kt
│   │   │       │                                   ├── SyncError.kt
│   │   │       │                                   └── SyncException.kt
│   │   │       ├── commonTest/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── core/
│   │   │       │                       └── networkutil/
│   │   │       │                           └── api/
│   │   │       │                               └── model/
│   │   │       │                                   └── ThrowableToSyncErrorTest.kt
│   │   │       └── jvmTest/
│   │   │           ├── kotlin/
│   │   │           │   └── com/
│   │   │           │       └── thomaskioko/
│   │   │           │           └── tvmaniac/
│   │   │           │               └── core/
│   │   │           │                   └── networkutil/
│   │   │           │                       └── api/
│   │   │           │                           └── extensions/
│   │   │           │                               ├── ApiResponseExtensionsTest.kt
│   │   │           │                               ├── InternetConnectionPluginTest.kt
│   │   │           │                               └── TestResourceLoader.jvm.kt
│   │   │           └── resources/
│   │   │               ├── error_response.json
│   │   │               └── success_response.json
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── core/
│   │   │       │                       └── networkutil/
│   │   │       │                           └── ratelimit/
│   │   │       │                               └── AdaptiveApiRateLimiter.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── core/
│   │   │                               └── networkutil/
│   │   │                                   ├── model/
│   │   │                                   │   └── SyncErrorTest.kt
│   │   │                                   └── ratelimit/
│   │   │                                       └── AdaptiveApiRateLimiterTest.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── core/
│   │                                   └── networkutil/
│   │                                       └── testing/
│   │                                           └── FakeApiRateLimiter.kt
│   ├── notifications/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── androidMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── core/
│   │   │       │                       └── notifications/
│   │   │       │                           └── api/
│   │   │       │                               └── NotificationIconProvider.kt
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── core/
│   │   │                               └── notifications/
│   │   │                                   └── api/
│   │   │                                       ├── EpisodeNotification.kt
│   │   │                                       ├── NotificationChannel.kt
│   │   │                                       └── NotificationManager.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── androidMain/
│   │   │       │   ├── AndroidManifest.xml
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── core/
│   │   │       │                       └── notifications/
│   │   │       │                           └── implementation/
│   │   │       │                               ├── AndroidNotificationManager.kt
│   │   │       │                               ├── BootCompletedReceiver.kt
│   │   │       │                               ├── DebugNotificationManager.kt
│   │   │       │                               ├── EpisodeNotificationReceiver.kt
│   │   │       │                               ├── PendingNotificationsStore.kt
│   │   │       │                               └── model/
│   │   │       │                                   └── StoredNotification.kt
│   │   │       └── iosMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── core/
│   │   │                               └── notifications/
│   │   │                                   └── implementation/
│   │   │                                       └── IosNotificationManager.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── core/
│   │                                   └── notifications/
│   │                                       └── testing/
│   │                                           └── FakeNotificationManager.kt
│   ├── paging/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── core/
│   │                               └── paging/
│   │                                   ├── CommonPagingConfig.kt
│   │                                   ├── KeyedQueryPagingSource.kt
│   │                                   ├── OffsetQueryPagingSource.kt
│   │                                   ├── PaginatedRemoteMediator.kt
│   │                                   └── QueryPagingSource.kt
│   ├── screenshot-tests/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── main/
│   │           ├── AndroidManifest.xml
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── screenshottests/
│   │                               └── RoborazziScreenshotUtil.kt
│   ├── tasks/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── core/
│   │   │                               └── tasks/
│   │   │                                   └── api/
│   │   │                                       ├── BackgroundTaskScheduler.kt
│   │   │                                       ├── BackgroundWorker.kt
│   │   │                                       ├── PeriodicTaskRequest.kt
│   │   │                                       ├── TaskConstraints.kt
│   │   │                                       ├── WorkerFactory.kt
│   │   │                                       └── WorkerResult.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── androidMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── core/
│   │   │       │                       └── tasks/
│   │   │       │                           └── implementation/
│   │   │       │                               ├── AndroidTaskScheduler.kt
│   │   │       │                               ├── SchedulerDispatchWorker.kt
│   │   │       │                               └── di/
│   │   │       │                                   └── WorkManagerBindingContainer.kt
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── core/
│   │   │       │                       └── tasks/
│   │   │       │                           └── implementation/
│   │   │       │                               └── DefaultWorkerFactory.kt
│   │   │       └── iosMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── core/
│   │   │                               └── tasks/
│   │   │                                   └── implementation/
│   │   │                                       └── IosTaskScheduler.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── core/
│   │                                   └── tasks/
│   │                                       └── testing/
│   │                                           └── FakeBackgroundTaskScheduler.kt
│   ├── test-tags/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── testtags/
│   │                               ├── calendar/
│   │                               │   └── CalendarTestTags.kt
│   │                               ├── component/
│   │                               │   └── DesignComponentTestTags.kt
│   │                               ├── discover/
│   │                               │   └── DiscoverTestTags.kt
│   │                               ├── episodesheet/
│   │                               │   └── EpisodeSheetTestTags.kt
│   │                               ├── home/
│   │                               │   └── HomeTestTags.kt
│   │                               ├── library/
│   │                               │   └── LibraryTestTags.kt
│   │                               ├── moreshows/
│   │                               │   └── MoreShowsTestTags.kt
│   │                               ├── notifications/
│   │                               │   └── NotificationRationaleTestTags.kt
│   │                               ├── profile/
│   │                               │   └── ProfileTestTags.kt
│   │                               ├── progress/
│   │                               │   └── ProgressTestTags.kt
│   │                               ├── search/
│   │                               │   └── SearchTestTags.kt
│   │                               ├── seasondetails/
│   │                               │   └── SeasonDetailsTestTags.kt
│   │                               ├── settings/
│   │                               │   └── SettingsTestTags.kt
│   │                               ├── showdetails/
│   │                               │   └── ShowDetailsTestTags.kt
│   │                               └── upnext/
│   │                                   └── UpNextTestTags.kt
│   ├── util/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── util/
│   │   │                               └── api/
│   │   │                                   ├── AppUtils.kt
│   │   │                                   ├── DateTimeProvider.kt
│   │   │                                   ├── FormatterUtil.kt
│   │   │                                   └── ItemSyncer.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── androidMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── util/
│   │   │       │                       ├── AndroidAppUtils.kt
│   │   │       │                       └── AndroidFormatterUtil.kt
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── util/
│   │   │       │                       ├── DateTimeBindingContainer.kt
│   │   │       │                       └── DefaultDateTimeProvider.kt
│   │   │       ├── commonTest/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── util/
│   │   │       │                       └── DefaultDateTimeProviderTest.kt
│   │   │       ├── iosMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── util/
│   │   │       │                       ├── IosAppUtils.kt
│   │   │       │                       └── IosFormatterUtil.kt
│   │   │       ├── iosTest/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── util/
│   │   │       │                       └── IosFormatterUtilTest.kt
│   │   │       └── test/
│   │   │           └── java/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── util/
│   │   │                               └── AndroidFormatterUtilTest.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           ├── commonMain/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── util/
│   │           │                       └── testing/
│   │           │                           ├── FakeApplicationInfo.kt
│   │           │                           ├── FakeDateTimeProvider.kt
│   │           │                           ├── FakeFormatterUtil.kt
│   │           │                           └── FlakyTests.kt
│   │           └── jvmAndroidMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── util/
│   │                                   └── testing/
│   │                                       └── FlakyTestRule.kt
│   └── view/
│       ├── build.gradle.kts
│       └── src/
│           └── commonMain/
│               └── kotlin/
│                   └── com/
│                       └── thomaskioko/
│                           └── tvmaniac/
│                               └── core/
│                                   └── view/
│                                       ├── ErrorToStringMapper.kt
│                                       ├── InvokeStatus.kt
│                                       ├── ObservableLoadingCounter.kt
│                                       └── UiMessage.kt
├── data/
│   ├── calendar/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── calendar/
│   │   │                                   ├── CalendarDao.kt
│   │   │                                   ├── CalendarEntry.kt
│   │   │                                   └── CalendarRepository.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── calendar/
│   │   │                                   └── implementation/
│   │   │                                       ├── CalendarStore.kt
│   │   │                                       ├── DefaultCalendarDao.kt
│   │   │                                       └── DefaultCalendarRepository.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── data/
│   │                                   └── calendar/
│   │                                       └── testing/
│   │                                           └── FakeCalendarRepository.kt
│   ├── cast/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── cast/
│   │   │                                   └── api/
│   │   │                                       ├── CastDao.kt
│   │   │                                       └── CastRepository.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── cast/
│   │   │                                   └── implementation/
│   │   │                                       ├── DefaultCastDao.kt
│   │   │                                       ├── DefaultCastRepository.kt
│   │   │                                       ├── ShowCastResult.kt
│   │   │                                       └── ShowCastStore.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── data/
│   │                                   └── cast/
│   │                                       └── testing/
│   │                                           └── FakeCastRepository.kt
│   ├── database/
│   │   ├── sqldelight/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── androidMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── db/
│   │   │       │                       └── DatabasePlatformBindingContainer.kt
│   │   │       ├── commonMain/
│   │   │       │   └── sqldelight/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   ├── db/
│   │   │       │                   │   ├── Calendar.sq
│   │   │       │                   │   ├── Cast.sq
│   │   │       │                   │   ├── EpisodeImage.sq
│   │   │       │                   │   ├── Episodes.sq
│   │   │       │                   │   ├── FeaturedShows.sq
│   │   │       │                   │   ├── FollowedShows.sq
│   │   │       │                   │   ├── GenreShows.sq
│   │   │       │                   │   ├── Genres.sq
│   │   │       │                   │   ├── LastRequests.sq
│   │   │       │                   │   ├── Library.sq
│   │   │       │                   │   ├── NextEpisodes.sq
│   │   │       │                   │   ├── PopularShows.sq
│   │   │       │                   │   ├── RecommendedShows.sq
│   │   │       │                   │   ├── SeasonImages.sq
│   │   │       │                   │   ├── SeasonVideos.sq
│   │   │       │                   │   ├── Seasons.sq
│   │   │       │                   │   ├── ShowGenres.sq
│   │   │       │                   │   ├── ShowMetadata.sq
│   │   │       │                   │   ├── ShowsLastWatched.sq
│   │   │       │                   │   ├── ShowsNextToWatch.sq
│   │   │       │                   │   ├── SimilarShows.sq
│   │   │       │                   │   ├── Stats.sq
│   │   │       │                   │   ├── TopratedShows.sq
│   │   │       │                   │   ├── Trailers.sq
│   │   │       │                   │   ├── TraktGenres.sq
│   │   │       │                   │   ├── TraktLastActivity.sq
│   │   │       │                   │   ├── TraktListShows.sq
│   │   │       │                   │   ├── TraktLists.sq
│   │   │       │                   │   ├── TrendingShows.sq
│   │   │       │                   │   ├── TvShow.sq
│   │   │       │                   │   ├── UpcomingShows.sq
│   │   │       │                   │   ├── User.sq
│   │   │       │                   │   ├── WatchProviders.sq
│   │   │       │                   │   └── WatchedEpisodes.sq
│   │   │       │                   └── migrations/
│   │   │       │                       ├── 1.sqm
│   │   │       │                       ├── 10.sqm
│   │   │       │                       ├── 11.sqm
│   │   │       │                       ├── 12.sqm
│   │   │       │                       ├── 13.sqm
│   │   │       │                       ├── 14.sqm
│   │   │       │                       ├── 15.sqm
│   │   │       │                       ├── 16.sqm
│   │   │       │                       ├── 17.sqm
│   │   │       │                       ├── 18.sqm
│   │   │       │                       ├── 19.sqm
│   │   │       │                       ├── 2.sqm
│   │   │       │                       ├── 20.sqm
│   │   │       │                       ├── 21.sqm
│   │   │       │                       ├── 22.sqm
│   │   │       │                       ├── 23.sqm
│   │   │       │                       ├── 3.sqm
│   │   │       │                       ├── 4.sqm
│   │   │       │                       ├── 5.sqm
│   │   │       │                       ├── 6.sqm
│   │   │       │                       ├── 7.sqm
│   │   │       │                       ├── 8.sqm
│   │   │       │                       └── 9.sqm
│   │   │       ├── iosMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── db/
│   │   │       │                       └── DatabasePlatformBindingContainer.kt
│   │   │       └── jvmTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── db/
│   │   │                               ├── Migration22TraktListShowsTest.kt
│   │   │                               ├── Migration23DropParentFkTest.kt
│   │   │                               ├── SchemaCreateTest.kt
│   │   │                               └── util/
│   │   │                                   └── MigrationTestUtil.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           ├── androidMain/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── database/
│   │           │                       └── test/
│   │           │                           └── BaseDatabaseTest.android.kt
│   │           ├── commonMain/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── database/
│   │           │                       └── test/
│   │           │                           └── BaseDatabaseTest.kt
│   │           ├── iosMain/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── database/
│   │           │                       └── test/
│   │           │                           └── BaseDatabaseTest.ios.kt
│   │           └── jvmMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── database/
│   │                                   └── test/
│   │                                       └── BaseDatabaseTest.jvm.kt
│   ├── datastore/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── datastore/
│   │   │                               └── api/
│   │   │                                   ├── AppTheme.kt
│   │   │                                   ├── DatastoreRepository.kt
│   │   │                                   ├── ImageQuality.kt
│   │   │                                   └── ListStyle.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   ├── src/
│   │   │   │   ├── androidMain/
│   │   │   │   │   └── kotlin/
│   │   │   │   │       └── com/
│   │   │   │   │           └── thomaskioko/
│   │   │   │   │               └── tvmaniac/
│   │   │   │   │                   └── datastore/
│   │   │   │   │                       └── implementation/
│   │   │   │   │                           └── DataStorePlatformBindingContainer.kt
│   │   │   │   ├── commonMain/
│   │   │   │   │   └── kotlin/
│   │   │   │   │       └── com/
│   │   │   │   │           └── thomaskioko/
│   │   │   │   │               └── tvmaniac/
│   │   │   │   │                   └── datastore/
│   │   │   │   │                       └── implementation/
│   │   │   │   │                           ├── DataStoreHelper.kt
│   │   │   │   │                           └── DefaultDatastoreRepository.kt
│   │   │   │   ├── commonTest/
│   │   │   │   │   └── kotlin/
│   │   │   │   │       └── com/
│   │   │   │   │           └── thomaskioko/
│   │   │   │   │               └── tvmaniac/
│   │   │   │   │                   └── datastore/
│   │   │   │   │                       └── implemetation/
│   │   │   │   │                           └── DatastoreRepositoryTest.kt
│   │   │   │   └── iosMain/
│   │   │   │       └── kotlin/
│   │   │   │           └── com/
│   │   │   │               └── thomaskioko/
│   │   │   │                   └── tvmaniac/
│   │   │   │                       └── datastore/
│   │   │   │                           └── implementation/
│   │   │   │                               └── DataStorePlatformBindingContainer.kt
│   │   │   └── test.preferences_pb
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── datastore/
│   │                                   └── testing/
│   │                                       └── FakeDatastoreRepository.kt
│   ├── episode/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── episodes/
│   │   │                               └── api/
│   │   │                                   ├── EpisodeRepository.kt
│   │   │                                   ├── EpisodeWatchesDataSource.kt
│   │   │                                   ├── EpisodesDao.kt
│   │   │                                   ├── NextEpisodeDao.kt
│   │   │                                   ├── WatchedEpisodeDao.kt
│   │   │                                   ├── WatchedEpisodeEntry.kt
│   │   │                                   ├── WatchedEpisodeSyncRepository.kt
│   │   │                                   └── model/
│   │   │                                       ├── EpisodeExtensions.kt
│   │   │                                       ├── EpisodeWatchParams.kt
│   │   │                                       ├── LastWatchedEpisode.kt
│   │   │                                       ├── SeasonWatchProgress.kt
│   │   │                                       ├── ShowWatchProgress.kt
│   │   │                                       ├── UnwatchedEpisode.kt
│   │   │                                       ├── UpcomingEpisode.kt
│   │   │                                       ├── WatchProgress.kt
│   │   │                                       └── WatchedEpisode.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── episodes/
│   │   │       │                       └── implementation/
│   │   │       │                           ├── DefaultEpisodeRepository.kt
│   │   │       │                           ├── DefaultWatchedEpisodeSyncRepository.kt
│   │   │       │                           ├── EpisodeWatchesLastRequestStore.kt
│   │   │       │                           ├── TraktEpisodeWatchesDataSource.kt
│   │   │       │                           ├── UpcomingEpisodesStore.kt
│   │   │       │                           ├── dao/
│   │   │       │                           │   ├── DefaultEpisodesDao.kt
│   │   │       │                           │   ├── DefaultNextEpisodeDao.kt
│   │   │       │                           │   └── DefaultWatchedEpisodeDao.kt
│   │   │       │                           └── model/
│   │   │       │                               └── NextEpisodeKey.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── episodes/
│   │   │                               └── implementation/
│   │   │                                   ├── DefaultEpisodeRepositoryTest.kt
│   │   │                                   ├── DefaultEpisodesDaoTest.kt
│   │   │                                   ├── DefaultNextEpisodeDaoTest.kt
│   │   │                                   ├── DefaultWatchedEpisodeDaoTest.kt
│   │   │                                   ├── EpisodesCacheTest.kt
│   │   │                                   └── MockData.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── episodes/
│   │                                   └── testing/
│   │                                       ├── FakeEpisodeRepository.kt
│   │                                       ├── FakeEpisodeWatchesDataSource.kt
│   │                                       └── FakeWatchedEpisodeSyncRepository.kt
│   ├── featuredshows/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── featuredshows/
│   │   │                                   └── api/
│   │   │                                       ├── FeaturedShowsDao.kt
│   │   │                                       ├── FeaturedShowsRepository.kt
│   │   │                                       └── interactor/
│   │   │                                           └── FeaturedShowsInteractor.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── data/
│   │   │       │                       └── featuredshows/
│   │   │       │                           └── implementation/
│   │   │       │                               ├── DefaultFeaturedShowsDao.kt
│   │   │       │                               ├── DefaultFeaturedShowsRepository.kt
│   │   │       │                               └── FeaturedShowsStore.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── featuredshows/
│   │   │                                   └── implementation/
│   │   │                                       └── DefaultFeaturedShowsDaoTest.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── data/
│   │                                   └── featuredshows/
│   │                                       └── testing/
│   │                                           └── FakeFeaturedShowsRepository.kt
│   ├── followedshows/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── followedshows/
│   │   │                               └── api/
│   │   │                                   ├── FollowedShowEntry.kt
│   │   │                                   ├── FollowedShowsDao.kt
│   │   │                                   ├── FollowedShowsRepository.kt
│   │   │                                   └── PendingAction.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── followedshows/
│   │   │       │                       └── implementation/
│   │   │       │                           ├── DefaultFollowedShowsDao.kt
│   │   │       │                           └── DefaultFollowedShowsRepository.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── followedshows/
│   │   │                               └── implementation/
│   │   │                                   ├── DefaultFollowedShowsDaoTest.kt
│   │   │                                   └── DefaultFollowedShowsRepositoryTest.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── followedshows/
│   │                                   └── testing/
│   │                                       ├── FakeFollowedShowsDao.kt
│   │                                       └── FakeFollowedShowsRepository.kt
│   ├── genre/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── genre/
│   │   │                               ├── GenreDao.kt
│   │   │                               ├── GenreRepository.kt
│   │   │                               ├── ShowGenresEntity.kt
│   │   │                               ├── TraktGenreDao.kt
│   │   │                               └── model/
│   │   │                                   ├── GenreShowCategory.kt
│   │   │                                   ├── GenreShowsStoreKey.kt
│   │   │                                   ├── GenreWithShowsEntity.kt
│   │   │                                   └── TraktGenreEntity.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── genre/
│   │   │                               ├── DefaultGenreDao.kt
│   │   │                               ├── DefaultGenreRepository.kt
│   │   │                               ├── DefaultTraktGenreDao.kt
│   │   │                               ├── GenrePosterStore.kt
│   │   │                               ├── GenreShowsStore.kt
│   │   │                               ├── GenreStore.kt
│   │   │                               ├── ShowsByGenreIdStore.kt
│   │   │                               └── TraktGenresStore.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── genre/
│   │                                   └── FakeGenreRepository.kt
│   ├── library/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── library/
│   │   │                                   ├── LibraryDao.kt
│   │   │                                   ├── LibraryRepository.kt
│   │   │                                   └── model/
│   │   │                                       ├── LibraryItem.kt
│   │   │                                       ├── LibrarySortOption.kt
│   │   │                                       └── WatchProvider.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── library/
│   │   │                                   └── implementation/
│   │   │                                       ├── DefaultLibraryDao.kt
│   │   │                                       ├── DefaultLibraryRepository.kt
│   │   │                                       └── LibraryStore.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── data/
│   │                                   └── library/
│   │                                       └── testing/
│   │                                           └── FakeLibraryRepository.kt
│   ├── popularshows/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── popularshows/
│   │   │                                   └── api/
│   │   │                                       ├── PopularShowsDao.kt
│   │   │                                       ├── PopularShowsInteractor.kt
│   │   │                                       └── PopularShowsRepository.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── data/
│   │   │       │                       └── popularshows/
│   │   │       │                           └── implementation/
│   │   │       │                               ├── DefaultPopularShowsDao.kt
│   │   │       │                               ├── DefaultPopularShowsRepository.kt
│   │   │       │                               └── PopularShowsStore.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── popularshows/
│   │   │                                   └── implementation/
│   │   │                                       └── DefaultPopularShowsDaoTest.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── data/
│   │                                   └── popularshows/
│   │                                       └── testing/
│   │                                           └── FakePopularShowsRepository.kt
│   ├── recommendedshows/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── recommendedshows/
│   │   │                                   └── api/
│   │   │                                       ├── RecommendedShowsDao.kt
│   │   │                                       ├── RecommendedShowsParams.kt
│   │   │                                       └── RecommendedShowsRepository.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── recommendedshows/
│   │   │                                   └── implementation/
│   │   │                                       ├── DefaultRecommendedShowsDao.kt
│   │   │                                       ├── DefaultRecommendedShowsRepository.kt
│   │   │                                       ├── RecommendedShowResult.kt
│   │   │                                       └── RecommendedShowsStore.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── data/
│   │                                   └── recommendedshows/
│   │                                       └── testing/
│   │                                           └── FakeRecommendedShowsRepository.kt
│   ├── request-manager/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── resourcemanager/
│   │   │                               └── api/
│   │   │                                   ├── RequestManagerRepository.kt
│   │   │                                   └── RequestTypeConfig.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── resourcemanager/
│   │   │       │                       └── implementation/
│   │   │       │                           └── DefaultRequestManagerRepository.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── resourcemanager/
│   │   │                               └── implementation/
│   │   │                                   ├── CacheValidationTest.kt
│   │   │                                   └── DefaultRequestManagerRepositoryTest.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── requestmanager/
│   │                                   └── testing/
│   │                                       └── FakeRequestManagerRepository.kt
│   ├── search/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── search/
│   │   │                               └── api/
│   │   │                                   └── SearchRepository.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── search/
│   │   │                               └── implementation/
│   │   │                                   ├── DefaultSearchRepository.kt
│   │   │                                   ├── SearchShowResult.kt
│   │   │                                   └── SearchShowStore.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── search/
│   │                                   └── testing/
│   │                                       └── FakeSearchRepository.kt
│   ├── seasondetails/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── seasondetails/
│   │   │                               └── api/
│   │   │                                   ├── SeasonDetailsDao.kt
│   │   │                                   ├── SeasonDetailsParam.kt
│   │   │                                   ├── SeasonDetailsRepository.kt
│   │   │                                   └── model/
│   │   │                                       ├── ContinueTrackingResult.kt
│   │   │                                       ├── EpisodeDetails.kt
│   │   │                                       └── SeasonDetailsWithEpisodes.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── seasondetails/
│   │   │                               └── implementation/
│   │   │                                   ├── DefaultSeasonDetailsDao.kt
│   │   │                                   ├── DefaultSeasonDetailsRepository.kt
│   │   │                                   ├── SeasonDetailsResponse.kt
│   │   │                                   └── SeasonDetailsStore.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── seasondetails/
│   │                                   └── testing/
│   │                                       └── FakeSeasonDetailsRepository.kt
│   ├── seasons/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── seasons/
│   │   │                               └── api/
│   │   │                                   ├── FollowedShowSeason.kt
│   │   │                                   ├── SeasonsDao.kt
│   │   │                                   ├── SeasonsEpisodesSyncRepository.kt
│   │   │                                   └── SeasonsRepository.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── seasons/
│   │   │                               └── implementation/
│   │   │                                   ├── DefaultSeasonsDao.kt
│   │   │                                   ├── DefaultSeasonsEpisodesSyncRepository.kt
│   │   │                                   ├── DefaultSeasonsRepository.kt
│   │   │                                   └── SeasonsWithEpisodesStore.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── seasons/
│   │                                   └── testing/
│   │                                       └── FakeSeasonsRepository.kt
│   ├── showdetails/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── showdetails/
│   │   │                                   └── api/
│   │   │                                       ├── ShowDetailsDao.kt
│   │   │                                       └── ShowDetailsRepository.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── showdetails/
│   │   │                                   └── implementation/
│   │   │                                       ├── DefaultShowDetailsDao.kt
│   │   │                                       ├── DefaultShowDetailsRepository.kt
│   │   │                                       ├── ShowDetailsResponse.kt
│   │   │                                       └── ShowDetailsStore.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── data/
│   │                                   └── showdetails/
│   │                                       └── testing/
│   │                                           └── FakeShowDetailsRepository.kt
│   ├── shows/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── shows/
│   │   │       │                       └── api/
│   │   │       │                           ├── MergeShowUtil.kt
│   │   │       │                           ├── TvShowsDao.kt
│   │   │       │                           └── model/
│   │   │       │                               ├── Category.kt
│   │   │       │                               ├── ShowDefaults.kt
│   │   │       │                               └── ShowEntity.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── shows/
│   │   │                               └── api/
│   │   │                                   ├── MockData.kt
│   │   │                                   └── TvShowCacheTest.kt
│   │   └── implementation/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── shows/
│   │                                   └── implementation/
│   │                                       └── DefaultTvShowsDao.kt
│   ├── similar/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── similar/
│   │   │                               └── api/
│   │   │                                   ├── SimilarShowsDao.kt
│   │   │                                   └── SimilarShowsRepository.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── similar/
│   │   │                               └── implementation/
│   │   │                                   ├── DefaultSimilarShowsDao.kt
│   │   │                                   ├── DefaultSimilarShowsRepository.kt
│   │   │                                   ├── SimilarParams.kt
│   │   │                                   ├── SimilarShowResult.kt
│   │   │                                   └── SimilarShowStore.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── similar/
│   │                                   └── testing/
│   │                                       └── FakeSimilarShowsRepository.kt
│   ├── sync-activity/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── syncactivity/
│   │   │                               └── api/
│   │   │                                   ├── TraktActivityDao.kt
│   │   │                                   ├── TraktActivityRepository.kt
│   │   │                                   └── model/
│   │   │                                       ├── ActivityType.kt
│   │   │                                       └── TraktLastActivity.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── syncactivity/
│   │   │       │                       └── implementation/
│   │   │       │                           ├── DefaultTraktActivityDao.kt
│   │   │       │                           ├── DefaultTraktActivityRepository.kt
│   │   │       │                           └── TraktActivityStore.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── syncactivity/
│   │   │                               └── implementation/
│   │   │                                   └── DefaultTraktActivityDaoTest.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── syncactivity/
│   │                                   └── testing/
│   │                                       ├── FakeTraktActivityDao.kt
│   │                                       └── FakeTraktActivityRepository.kt
│   ├── topratedshows/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── topratedshows/
│   │   │                               └── data/
│   │   │                                   └── api/
│   │   │                                       ├── TopRatedShowsDao.kt
│   │   │                                       ├── TopRatedShowsInteractor.kt
│   │   │                                       └── TopRatedShowsRepository.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── toprated/
│   │   │       │                       └── data/
│   │   │       │                           └── implementation/
│   │   │       │                               ├── DefaultTopRatedShowsDao.kt
│   │   │       │                               ├── DefaultTopRatedShowsRepository.kt
│   │   │       │                               ├── TopRatedShowWithImages.kt
│   │   │       │                               └── TopRatedShowsStore.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── toprated/
│   │   │                               └── data/
│   │   │                                   └── implementation/
│   │   │                                       └── DefaultTopRatedShowsDaoTest.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── data/
│   │                                   └── topratedshows/
│   │                                       └── testing/
│   │                                           └── FakeTopRatedShowsRepository.kt
│   ├── trailers/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com.thomaskioko.tvmaniac.data.trailers.implementation/
│   │   │                   ├── TrailerDao.kt
│   │   │                   └── TrailerRepository.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── trailers/
│   │   │                                   └── implementation/
│   │   │                                       ├── DefaultTrailerDao.kt
│   │   │                                       ├── DefaultTrailerRepository.kt
│   │   │                                       └── TrailerStore.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── trailers/
│   │                                   └── testing/
│   │                                       ├── FakeTrailerRepository.kt
│   │                                       └── MockData.kt
│   ├── traktauth/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── traktauth/
│   │   │                               └── api/
│   │   │                                   ├── AuthError.kt
│   │   │                                   ├── AuthState.kt
│   │   │                                   ├── AuthStore.kt
│   │   │                                   ├── RefreshTokenResult.kt
│   │   │                                   ├── TokenRefreshResult.kt
│   │   │                                   ├── TraktAuthManager.kt
│   │   │                                   ├── TraktAuthRepository.kt
│   │   │                                   ├── TraktAuthState.kt
│   │   │                                   └── TraktRefreshTokenAction.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── androidMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── traktauth/
│   │   │       │                       └── implementation/
│   │   │       │                           ├── AndroidAuthStore.kt
│   │   │       │                           ├── AndroidTraktAuthManager.kt
│   │   │       │                           ├── TraktActivityResultContract.kt
│   │   │       │                           └── di/
│   │   │       │                               └── TraktAuthAndroidBindingContainer.kt
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── traktauth/
│   │   │       │                       └── implementation/
│   │   │       │                           ├── DefaultTraktAuthRepository.kt
│   │   │       │                           ├── DefaultTraktRefreshTokenAction.kt
│   │   │       │                           ├── TokenRefreshInitializer.kt
│   │   │       │                           ├── TokenRefreshWorker.kt
│   │   │       │                           └── di/
│   │   │       │                               └── TokenRefreshInitializerBindingContainer.kt
│   │   │       └── iosMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── traktauth/
│   │   │                               └── implementation/
│   │   │                                   ├── DefaultIOSTraktAuthManager.kt
│   │   │                                   └── IosAuthStore.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── traktauth/
│   │                                   └── testing/
│   │                                       ├── FakeAuthStore.kt
│   │                                       ├── FakeTraktAuthManager.kt
│   │                                       ├── FakeTraktAuthRepository.kt
│   │                                       └── FakeTraktRefreshTokenAction.kt
│   ├── traktlists/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── traktlists/
│   │   │                               └── api/
│   │   │                                   ├── TraktList.kt
│   │   │                                   ├── TraktListDao.kt
│   │   │                                   ├── TraktListEntity.kt
│   │   │                                   ├── TraktListRepository.kt
│   │   │                                   ├── TraktListShowDao.kt
│   │   │                                   └── TraktListShowEntry.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── traktlists/
│   │   │                               └── implementation/
│   │   │                                   ├── CreateTraktListStore.kt
│   │   │                                   ├── DefaultTraktListDao.kt
│   │   │                                   ├── DefaultTraktListRepository.kt
│   │   │                                   ├── DefaultTraktListShowDao.kt
│   │   │                                   └── TraktListsStore.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── traktlists/
│   │                                   └── testing/
│   │                                       └── FakeTraktListRepository.kt
│   ├── trendingshows/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── discover/
│   │   │                               └── api/
│   │   │                                   ├── TrendingShowsDao.kt
│   │   │                                   ├── TrendingShowsInteractor.kt
│   │   │                                   ├── TrendingShowsParams.kt
│   │   │                                   └── TrendingShowsRepository.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── discover/
│   │   │       │                       └── implementation/
│   │   │       │                           ├── DefaultTrendingShowsDao.kt
│   │   │       │                           ├── DefaultTrendingShowsRepository.kt
│   │   │       │                           ├── TrendingShowWithImages.kt
│   │   │       │                           └── TrendingShowsStore.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── discover/
│   │   │                               └── implementation/
│   │   │                                   └── DefaultTrendingShowsDaoTest.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── data/
│   │                                   └── trendingshows/
│   │                                       └── testing/
│   │                                           └── FakeTrendingShowsRepository.kt
│   ├── upcomingshows/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── upcomingshows/
│   │   │                                   └── api/
│   │   │                                       ├── UpcomingShowsDao.kt
│   │   │                                       ├── UpcomingShowsInteractor.kt
│   │   │                                       └── UpcomingShowsRepository.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── data/
│   │   │       │                       └── upcomingshows/
│   │   │       │                           └── implementation/
│   │   │       │                               ├── DefaultUpcomingShowsDao.kt
│   │   │       │                               ├── DefaultUpcomingShowsRepository.kt
│   │   │       │                               ├── UpcomingShowsStore.kt
│   │   │       │                               └── model/
│   │   │       │                                   ├── UpcomingParams.kt
│   │   │       │                                   └── UpcomingShowResult.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── upcomingshows/
│   │   │                                   └── implementation/
│   │   │                                       └── DefaultUpcomingShowsDaoTest.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── data/
│   │                                   └── upcomingshows/
│   │                                       └── testing/
│   │                                           └── FakeUpcomingShowsRepository.kt
│   ├── upnext/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── upnext/
│   │   │                               └── api/
│   │   │                                   ├── UpNextDao.kt
│   │   │                                   ├── UpNextRepository.kt
│   │   │                                   └── model/
│   │   │                                       └── NextEpisodeWithShow.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── upnext/
│   │   │       │                       └── implementation/
│   │   │       │                           ├── DefaultUpNextDao.kt
│   │   │       │                           ├── DefaultUpNextRepository.kt
│   │   │       │                           └── ShowUpNextStore.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── upnext/
│   │   │                               └── implementation/
│   │   │                                   ├── DefaultUpNextDaoTest.kt
│   │   │                                   └── DefaultUpNextRepositoryTest.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── upnext/
│   │                                   └── testing/
│   │                                       ├── FakeUpNextDao.kt
│   │                                       └── FakeUpNextRepository.kt
│   ├── user/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── user/
│   │   │                                   └── api/
│   │   │                                       ├── UserDao.kt
│   │   │                                       ├── UserRepository.kt
│   │   │                                       ├── UserStatsDao.kt
│   │   │                                       └── model/
│   │   │                                           ├── UserProfile.kt
│   │   │                                           ├── UserProfileStats.kt
│   │   │                                           └── UserWatchTime.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── data/
│   │   │       │                       └── user/
│   │   │       │                           └── implementation/
│   │   │       │                               ├── DefaultUserDao.kt
│   │   │       │                               ├── DefaultUserRepository.kt
│   │   │       │                               ├── DefaultUserStatsDao.kt
│   │   │       │                               ├── UserStatsStore.kt
│   │   │       │                               └── UserStore.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── data/
│   │   │                               └── user/
│   │   │                                   └── implementation/
│   │   │                                       ├── DefaultUserDaoTest.kt
│   │   │                                       └── DefaultUserStatsDaoTest.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── data/
│   │                                   └── user/
│   │                                       └── testing/
│   │                                           └── FakeUserRepository.kt
│   ├── watchlist/
│   │   ├── api/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── shows/
│   │   │                               └── api/
│   │   │                                   ├── WatchlistDao.kt
│   │   │                                   └── WatchlistRepository.kt
│   │   ├── implementation/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── watchlist/
│   │   │                               └── implementation/
│   │   │                                   ├── DefaultWatchlistDao.kt
│   │   │                                   └── DefaultWatchlistRepository.kt
│   │   └── testing/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── watchlist/
│   │                                   └── testing/
│   │                                       └── FakeWatchlistRepository.kt
│   └── watchproviders/
│       ├── api/
│       │   ├── build.gradle.kts
│       │   └── src/
│       │       └── commonMain/
│       │           └── kotlin/
│       │               └── com/
│       │                   └── thomaskioko/
│       │                       └── tvmaniac/
│       │                           └── data/
│       │                               └── watchproviders/
│       │                                   └── api/
│       │                                       ├── WatchProviderDao.kt
│       │                                       └── WatchProviderRepository.kt
│       ├── implementation/
│       │   ├── build.gradle.kts
│       │   └── src/
│       │       └── commonMain/
│       │           └── kotlin/
│       │               └── com/
│       │                   └── thomaskioko/
│       │                       └── tvmaniac/
│       │                           └── data/
│       │                               └── watchproviders/
│       │                                   └── implementation/
│       │                                       ├── DefaultWatchProviderDao.kt
│       │                                       ├── DefaultWatchProviderRepository.kt
│       │                                       └── WatchProvidersStore.kt
│       └── testing/
│           ├── build.gradle.kts
│           └── src/
│               └── commonMain/
│                   └── kotlin/
│                       └── com/
│                           └── thomaskioko/
│                               └── tvmaniac/
│                                   └── data/
│                                       └── watchproviders/
│                                           └── testing/
│                                               └── FakeWatchProviderRepository.kt
├── docs/
│   ├── architecture/
│   │   ├── README.md
│   │   ├── data-layer.md
│   │   ├── dependency-injection.md
│   │   ├── integration-testing.md
│   │   ├── journey-tests.md
│   │   ├── modularization.md
│   │   ├── navigation-codegen.md
│   │   ├── navigation.md
│   │   ├── presentation-layer.md
│   │   └── scopes.md
│   ├── privacy_policy.md
│   ├── setup.md
│   └── terms_conditions.md
├── domain/
│   ├── calendar/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       ├── commonMain/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── domain/
│   │       │                       └── calendar/
│   │       │                           ├── CalendarEpisodeFormatter.kt
│   │       │                           ├── CalendarWeekCalculator.kt
│   │       │                           ├── FetchCalendarInteractor.kt
│   │       │                           ├── ObserveCalendarInteractor.kt
│   │       │                           └── model/
│   │       │                               ├── DateLabel.kt
│   │       │                               ├── GroupedCalendarEntry.kt
│   │       │                               └── GroupedEpisodeEntry.kt
│   │       └── commonTest/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── calendar/
│   │                                   ├── CalendarWeekCalculatorTest.kt
│   │                                   └── ObserveCalendarInteractorTest.kt
│   ├── discover/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       ├── commonMain/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── domain/
│   │       │                       └── discover/
│   │       │                           └── DiscoverShowsInteractor.kt
│   │       └── commonTest/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── discover/
│   │                                   └── DiscoverShowsInteractorTest.kt
│   ├── episode/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       ├── commonMain/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── domain/
│   │       │                       └── episode/
│   │       │                           ├── MarkEpisodeUnwatchedInteractor.kt
│   │       │                           ├── MarkEpisodeWatchedInteractor.kt
│   │       │                           ├── ObserveEpisodeByIdInteractor.kt
│   │       │                           └── ObserveShowWatchProgressInteractor.kt
│   │       └── commonTest/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── episode/
│   │                                   └── MarkEpisodeWatchedInteractorTest.kt
│   ├── followedshows/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── followedshows/
│   │                                   └── UnfollowShowInteractor.kt
│   ├── genre/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── genre/
│   │                                   ├── FetchGenreContentInteractor.kt
│   │                                   └── GenreShowsInteractor.kt
│   ├── library/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── library/
│   │                                   ├── LibrarySyncWorker.kt
│   │                                   ├── ObserveLibraryInteractor.kt
│   │                                   ├── SyncLibraryInteractor.kt
│   │                                   ├── SyncTasksInitializer.kt
│   │                                   └── di/
│   │                                       └── SyncTasksInitializerBindingContainer.kt
│   ├── logout/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── logout/
│   │                                   └── LogoutInteractor.kt
│   ├── notifications/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       ├── commonMain/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── domain/
│   │       │                       └── notifications/
│   │       │                           ├── EpisodeNotificationWorker.kt
│   │       │                           ├── NotificationTasksInitializer.kt
│   │       │                           ├── di/
│   │       │                           │   └── NotificationTasksInitializerBindingContainer.kt
│   │       │                           └── interactor/
│   │       │                               ├── RefreshUpcomingSeasonDetailsInteractor.kt
│   │       │                               ├── ScheduleDebugEpisodeNotificationInteractor.kt
│   │       │                               ├── ScheduleEpisodeNotificationsInteractor.kt
│   │       │                               ├── SyncTraktCalendarInteractor.kt
│   │       │                               └── ToggleEpisodeNotificationsInteractor.kt
│   │       └── commonTest/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── notifications/
│   │                                   └── interactor/
│   │                                       └── ScheduleEpisodeNotificationsInteractorTest.kt
│   ├── recommendedshows/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── recommendedshows/
│   │                                   └── RecommendedShowsInteractor.kt
│   ├── seasondetails/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── seasondetails/
│   │                                   ├── FetchPreviousSeasonsInteractor.kt
│   │                                   ├── MarkSeasonUnwatchedInteractor.kt
│   │                                   ├── MarkSeasonWatchedInteractor.kt
│   │                                   ├── ObservableSeasonDetailsInteractor.kt
│   │                                   ├── ObserveSeasonWatchProgressInteractor.kt
│   │                                   ├── ObserveUnwatchedInPreviousSeasonsInteractor.kt
│   │                                   ├── SeasonDetailsInteractor.kt
│   │                                   └── model/
│   │                                       ├── SeasonCast.kt
│   │                                       ├── SeasonDetailsResult.kt
│   │                                       └── SeasonImages.kt
│   ├── settings/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── settings/
│   │                                   └── ObserveSettingsPreferencesInteractor.kt
│   ├── showdetails/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── showdetails/
│   │                                   ├── FollowShowInteractor.kt
│   │                                   ├── Mapper.kt
│   │                                   ├── ObservableShowDetailsInteractor.kt
│   │                                   ├── ShowContentSyncInteractor.kt
│   │                                   ├── ShowDetailsInteractor.kt
│   │                                   └── model/
│   │                                       └── ShowDetails.kt
│   ├── similarshows/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── similarshows/
│   │                                   └── SimilarShowsInteractor.kt
│   ├── theme/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── theme/
│   │                                   ├── ImageQuality.kt
│   │                                   └── Theme.kt
│   ├── traktlists/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── traktlists/
│   │                                   ├── CreateTraktListInteractor.kt
│   │                                   ├── ObserveTraktListsInteractor.kt
│   │                                   ├── SyncTraktListsInteractor.kt
│   │                                   └── ToggleShowInListInteractor.kt
│   ├── upnext/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       ├── commonMain/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── domain/
│   │       │                       └── upnext/
│   │       │                           ├── ObserveUpNextInteractor.kt
│   │       │                           ├── RefreshUpNextInteractor.kt
│   │       │                           ├── UpNextSyncWorker.kt
│   │       │                           ├── UpNextTasksInitializer.kt
│   │       │                           ├── di/
│   │       │                           │   └── UpNextTasksInitializerBindingContainer.kt
│   │       │                           └── model/
│   │       │                               ├── UpNextResult.kt
│   │       │                               └── UpNextSortOption.kt
│   │       └── commonTest/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── upnext/
│   │                                   └── ObserveUpNextInteractorTest.kt
│   ├── user/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── user/
│   │                                   ├── ObserveUserProfileInteractor.kt
│   │                                   ├── UpdateUserProfileData.kt
│   │                                   └── model/
│   │                                       ├── UserProfile.kt
│   │                                       └── UserStats.kt
│   ├── watchlist/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       ├── commonMain/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── domain/
│   │       │                       └── watchlist/
│   │       │                           ├── ObservableWatchlistInteractor.kt
│   │       │                           ├── ObserveUpNextSectionsInteractor.kt
│   │       │                           ├── ObserveWatchlistSectionsInteractor.kt
│   │       │                           ├── UpNextSectionsMapper.kt
│   │       │                           ├── WatchlistSyncInteractor.kt
│   │       │                           └── model/
│   │       │                               ├── EpisodeBadge.kt
│   │       │                               ├── UpNextSections.kt
│   │       │                               └── WatchlistSections.kt
│   │       └── commonTest/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── domain/
│   │                               └── watchlist/
│   │                                   ├── ObservableWatchlistInteractorTest.kt
│   │                                   ├── ObserveUpNextSectionsInteractorTest.kt
│   │                                   ├── ObserveWatchlistSectionsInteractorTest.kt
│   │                                   └── UpNextSectionsMapperTest.kt
│   └── watchproviders/
│       ├── build.gradle.kts
│       └── src/
│           └── commonMain/
│               └── kotlin/
│                   └── com/
│                       └── thomaskioko/
│                           └── tvmaniac/
│                               └── domain/
│                                   └── watchproviders/
│                                       └── WatchProvidersInteractor.kt
├── fastlane/
│   ├── Appfile
│   ├── Fastfile
│   ├── Matchfile
│   ├── Pluginfile
│   └── README.md
├── features/
│   ├── calendar/
│   │   ├── nav/
│   │   │   └── build.gradle.kts
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── presentation/
│   │   │       │                       └── calendar/
│   │   │       │                           ├── CalendarAction.kt
│   │   │       │                           ├── CalendarPresenter.kt
│   │   │       │                           ├── CalendarState.kt
│   │   │       │                           ├── CalendarStateMapper.kt
│   │   │       │                           └── model/
│   │   │       │                               ├── CalendarDateGroup.kt
│   │   │       │                               └── CalendarEpisodeItem.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── presentation/
│   │   │                               └── calendar/
│   │   │                                   └── CalendarPresenterTest.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           ├── main/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── ui/
│   │           │                       └── calendar/
│   │           │                           └── CalendarScreen.kt
│   │           └── test/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── ui/
│   │                                   └── calendar/
│   │                                       └── roborrazi/
│   │                                           └── CalendarScreenshotTest.kt
│   ├── debug/
│   │   ├── nav/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── debug/
│   │   │                               └── nav/
│   │   │                                   └── DebugRoute.kt
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── debug/
│   │   │       │                       └── presenter/
│   │   │       │                           ├── DebugActions.kt
│   │   │       │                           ├── DebugPresenter.kt
│   │   │       │                           └── DebugState.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── debug/
│   │   │                               └── presenter/
│   │   │                                   └── DebugPresenterTest.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── main/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── debug/
│   │                                   └── ui/
│   │                                       └── DebugMenuScreen.kt
│   ├── discover/
│   │   ├── nav/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── discover/
│   │   │                               └── nav/
│   │   │                                   └── DiscoverNavigator.kt
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── discover/
│   │   │       │                       └── presenter/
│   │   │       │                           ├── DiscoverShowsAction.kt
│   │   │       │                           ├── DiscoverShowsMapper.kt
│   │   │       │                           ├── DiscoverShowsPresenter.kt
│   │   │       │                           ├── DiscoverViewState.kt
│   │   │       │                           ├── di/
│   │   │       │                           │   └── DefaultDiscoverNavigator.kt
│   │   │       │                           └── model/
│   │   │       │                               ├── DiscoverShow.kt
│   │   │       │                               └── NextEpisodeUiModel.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── discover/
│   │   │                               └── presenter/
│   │   │                                   └── DiscoverShowsPresenterTest.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       ├── lint-baseline.xml
│   │       └── src/
│   │           ├── main/
│   │           │   └── java/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── discover/
│   │           │                       └── ui/
│   │           │                           ├── DiscoverPreviewParameterProvider.kt
│   │           │                           ├── DiscoverScreen.kt
│   │           │                           └── component/
│   │           │                               ├── CircularIndicator.kt
│   │           │                               ├── DiscoverHeaderContent.kt
│   │           │                               ├── HorizontalRowContent.kt
│   │           │                               ├── NextEpisodeCard.kt
│   │           │                               └── NextEpisodesSection.kt
│   │           └── test/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── discover/
│   │                                   └── roborrazi/
│   │                                       └── DiscoverScreenshotTest.kt
│   ├── episode-sheet/
│   │   ├── nav/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── espisodedetails/
│   │   │                               └── nav/
│   │   │                                   └── model/
│   │   │                                       ├── EpisodeSheetConfig.kt
│   │   │                                       ├── ScreenSource.kt
│   │   │                                       └── SheetNavigatorExt.kt
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── presentation/
│   │   │       │                       └── episodedetail/
│   │   │       │                           ├── EpisodeSheetAction.kt
│   │   │       │                           ├── EpisodeSheetMapper.kt
│   │   │       │                           ├── EpisodeSheetPresenter.kt
│   │   │       │                           └── EpisodeSheetState.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── presentation/
│   │   │                               └── episodedetail/
│   │   │                                   └── EpisodeSheetPresenterTest.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           ├── main/
│   │           │   └── java/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── episodedetail/
│   │           │                       └── ui/
│   │           │                           ├── EpisodeDetailBottomSheet.kt
│   │           │                           └── EpisodeSheet.kt
│   │           └── test/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── episodedetail/
│   │                                   └── roborrazi/
│   │                                       └── EpisodeSheetScreenshotTest.kt
│   ├── genre-shows/
│   │   ├── nav/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── genreshows/
│   │   │                               └── nav/
│   │   │                                   ├── GenreShowsDestination.kt
│   │   │                                   └── GenreShowsRoute.kt
│   │   └── presenter/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── commonMain/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── genreshows/
│   │                                   └── presenter/
│   │                                       └── di/
│   │                                           └── GenreShowsNavDestinationBinding.kt
│   ├── home/
│   │   ├── nav/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── home/
│   │   │                               └── nav/
│   │   │                                   ├── HomeRoute.kt
│   │   │                                   ├── HomeTabNavigator.kt
│   │   │                                   ├── TabChild.kt
│   │   │                                   ├── TabDestination.kt
│   │   │                                   └── di/
│   │   │                                       ├── TabDestinationMultibindings.kt
│   │   │                                       └── model/
│   │   │                                           └── HomeConfig.kt
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── presenter/
│   │   │       │                       └── home/
│   │   │       │                           ├── HomePresenter.kt
│   │   │       │                           └── di/
│   │   │       │                               └── DefaultHomeTabNavigator.kt
│   │   │       ├── commonTest/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── presenter/
│   │   │       │                       └── home/
│   │   │       │                           └── HomePresenterTest.kt
│   │   │       ├── iosTest/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── presenter/
│   │   │       │                       └── home/
│   │   │       │                           └── HomePresenterIosTest.kt
│   │   │       └── jvmTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── presenter/
│   │   │                               └── home/
│   │   │                                   └── HomePresenterJvmTest.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── main/
│   │               └── java/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── home/
│   │                                   └── ui/
│   │                                       └── HomeScreen.kt
│   ├── library/
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── presentation/
│   │   │                               └── library/
│   │   │                                   ├── LibraryAction.kt
│   │   │                                   ├── LibraryPresenter.kt
│   │   │                                   ├── LibraryState.kt
│   │   │                                   └── model/
│   │   │                                       ├── LibraryShowItem.kt
│   │   │                                       ├── LibrarySortOption.kt
│   │   │                                       └── ShowStatus.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── main/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── ui/
│   │                                   └── library/
│   │                                       ├── LibraryListItem.kt
│   │                                       ├── LibraryScreen.kt
│   │                                       ├── LibrarySearchbar.kt
│   │                                       ├── SortOptionsContent.kt
│   │                                       └── preview/
│   │                                           ├── LibraryListItemPreviewParameterProvider.kt
│   │                                           └── LibraryStatePreviewParameterProvider.kt
│   ├── more-shows/
│   │   ├── nav/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── moreshows/
│   │   │                               └── nav/
│   │   │                                   └── MoreShowsRoute.kt
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── moreshows/
│   │   │                               └── presentation/
│   │   │                                   ├── MoreShowsAction.kt
│   │   │                                   ├── MoreShowsPresenter.kt
│   │   │                                   ├── MoreShowsState.kt
│   │   │                                   └── TvShow.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           ├── main/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── moreshows/
│   │           │                       └── ui/
│   │           │                           ├── MoreShowsPreviewParameterProvider.kt
│   │           │                           └── MoreShowsScreen.kt
│   │           └── test/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── moreshows/
│   │                                   └── roborrazi/
│   │                                       └── MoreShowsScreenTest.kt
│   ├── profile/
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── profile/
│   │   │       │                       └── presenter/
│   │   │       │                           ├── ProfileAction.kt
│   │   │       │                           ├── ProfilePresenter.kt
│   │   │       │                           └── model/
│   │   │       │                               ├── ProfileInfo.kt
│   │   │       │                               ├── ProfileState.kt
│   │   │       │                               └── ProfileStats.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── presenter/
│   │   │                               └── profile/
│   │   │                                   └── ProfilePresenterTest.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           ├── main/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── profile/
│   │           │                       └── ui/
│   │           │                           ├── ProfilePreviewParameterProvider.kt
│   │           │                           ├── ProfileScreen.kt
│   │           │                           ├── StatsCardItem.kt
│   │           │                           └── UnauthenticatedContent.kt
│   │           └── test/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── profile/
│   │                                   └── roborazzi/
│   │                                       └── ProfileScreenTest.kt
│   ├── progress/
│   │   ├── nav/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── progress/
│   │   │                               └── nav/
│   │   │                                   └── scope/
│   │   │                                       └── ProgressChildScope.kt
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── presentation/
│   │   │                               └── progress/
│   │   │                                   ├── ProgressAction.kt
│   │   │                                   ├── ProgressChildGraph.kt
│   │   │                                   ├── ProgressPresenter.kt
│   │   │                                   └── ProgressState.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           ├── main/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── ui/
│   │           │                       └── progress/
│   │           │                           └── ProgressScreen.kt
│   │           └── test/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── ui/
│   │                                   └── progress/
│   │                                       └── roborrazi/
│   │                                           └── ProgressScreenshotTest.kt
│   ├── root/
│   │   ├── nav/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── root/
│   │   │                           ├── model/
│   │   │                           │   ├── DeepLinkDestination.kt
│   │   │                           │   ├── NotificationPermissionState.kt
│   │   │                           │   └── ThemeState.kt
│   │   │                           └── nav/
│   │   │                               └── NotificationRationale.kt
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── presenter/
│   │   │       │                       └── root/
│   │   │       │                           ├── DefaultRootPresenter.kt
│   │   │       │                           ├── RootPresenter.kt
│   │   │       │                           └── di/
│   │   │       │                               ├── DefaultNotificationRationale.kt
│   │   │       │                               ├── DefaultSheetNavigator.kt
│   │   │       │                               └── RootPresenterBindingContainer.kt
│   │   │       ├── commonTest/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── presenter/
│   │   │       │                       └── root/
│   │   │       │                           └── DefaultRootPresenterTest.kt
│   │   │       ├── iosTest/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── presenter/
│   │   │       │                       └── root/
│   │   │       │                           └── DefaultRootPresenterIosTest.kt
│   │   │       └── jvmTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── presenter/
│   │   │                               └── root/
│   │   │                                   └── DefaultRootPresenterJvmTest.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── main/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── app/
│   │                                   └── ui/
│   │                                       └── RootScreen.kt
│   ├── search/
│   │   ├── nav/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── search/
│   │   │                               └── nav/
│   │   │                                   └── SearchRoute.kt
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── search/
│   │   │       │                       └── presenter/
│   │   │       │                           ├── Mapper.kt
│   │   │       │                           ├── SearchShowAction.kt
│   │   │       │                           ├── SearchShowState.kt
│   │   │       │                           ├── SearchShowsPresenter.kt
│   │   │       │                           └── model/
│   │   │       │                               ├── CategoryItem.kt
│   │   │       │                               ├── GenreRowModel.kt
│   │   │       │                               ├── ShowGenre.kt
│   │   │       │                               └── ShowItem.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── presenter/
│   │   │                               └── search/
│   │   │                                   └── SearchShowsPresenterTest.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           ├── main/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── search/
│   │           │                       └── ui/
│   │           │                           ├── SearchPreviewParameterProvider.kt
│   │           │                           ├── SearchScreen.kt
│   │           │                           └── components/
│   │           │                               ├── HorizontalShowContentRow.kt
│   │           │                               └── SearchResultItem.kt
│   │           └── test/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── search/
│   │                                   └── roborrazi/
│   │                                       └── SearchScreenTest.kt
│   ├── season-details/
│   │   ├── nav/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── seasondetails/
│   │   │                               └── nav/
│   │   │                                   ├── SeasonDetailsRoute.kt
│   │   │                                   └── SeasonDetailsUiParam.kt
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── seasondetails/
│   │   │       │                       └── presenter/
│   │   │       │                           ├── Mapper.kt
│   │   │       │                           ├── SeasonDetailsAction.kt
│   │   │       │                           ├── SeasonDetailsModel.kt
│   │   │       │                           ├── SeasonDetailsPresenter.kt
│   │   │       │                           └── model/
│   │   │       │                               ├── Cast.kt
│   │   │       │                               ├── EpisodeDetailsModel.kt
│   │   │       │                               └── SeasonImagesModel.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── seasondetails/
│   │   │                               └── presenter/
│   │   │                                   ├── SeasonPresenterTest.kt
│   │   │                                   └── data/
│   │   │                                       └── MockData.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           ├── main/
│   │           │   └── java/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── seasondetails/
│   │           │                       └── ui/
│   │           │                           ├── SeasonDetailsScreen.kt
│   │           │                           ├── SeasonPreviewParameterProvider.kt
│   │           │                           └── components/
│   │           │                               ├── CollapsableContent.kt
│   │           │                               └── EpisodeItem.kt
│   │           └── test/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── seasondetails/
│   │                                   └── roborrazi/
│   │                                       └── SeasonScreenshotTest.kt
│   ├── settings/
│   │   ├── nav/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── settings/
│   │   │                               └── nav/
│   │   │                                   └── SettingsRoute.kt
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── settings/
│   │   │       │                       └── presenter/
│   │   │       │                           ├── SettingsActions.kt
│   │   │       │                           ├── SettingsPresenter.kt
│   │   │       │                           ├── SettingsState.kt
│   │   │       │                           └── ThemeModel.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── presenter/
│   │   │                               └── settings/
│   │   │                                   └── SettingsPresenterTest.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           ├── main/
│   │           │   ├── kotlin/
│   │           │   │   └── com/
│   │           │   │       └── thomaskioko/
│   │           │   │           └── tvmaniac/
│   │           │   │               └── settings/
│   │           │   │                   └── ui/
│   │           │   │                       ├── AboutSheetContent.kt
│   │           │   │                       ├── SettingsPreviewParameterProvider.kt
│   │           │   │                       ├── SettingsScreen.kt
│   │           │   │                       ├── ThemePreviewSwatch.kt
│   │           │   │                       └── ThemeSelectorSection.kt
│   │           │   └── res/
│   │           │       └── drawable/
│   │           │           └── ic_app_launcher.xml
│   │           └── test/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── seasondetails/
│   │                                   └── roborrazi/
│   │                                       └── SettingsScreenshotTest.kt
│   ├── show-details/
│   │   ├── nav/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── showdetails/
│   │   │                               └── nav/
│   │   │                                   ├── ShowDetailsRoute.kt
│   │   │                                   └── model/
│   │   │                                       ├── ShowDetailsParam.kt
│   │   │                                       └── ShowSeasonDetailsParam.kt
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── presenter/
│   │   │       │                       └── showdetails/
│   │   │       │                           ├── ShowDetailsAction.kt
│   │   │       │                           ├── ShowDetailsContent.kt
│   │   │       │                           ├── ShowDetailsMapper.kt
│   │   │       │                           ├── ShowDetailsPresenter.kt
│   │   │       │                           └── model/
│   │   │       │                               ├── CastModel.kt
│   │   │       │                               ├── ContinueTrackingEpisodeModel.kt
│   │   │       │                               ├── ProviderModel.kt
│   │   │       │                               ├── SeasonModel.kt
│   │   │       │                               ├── ShowDetailsModel.kt
│   │   │       │                               ├── ShowModel.kt
│   │   │       │                               ├── TrailerModel.kt
│   │   │       │                               └── TraktListModel.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── presenter/
│   │   │                               └── showdetails/
│   │   │                                   ├── MockData.kt
│   │   │                                   └── ShowDetailsPresenterTest.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           ├── main/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── showdetails/
│   │           │                       └── ui/
│   │           │                           ├── DetailPreviewParameterProvider.kt
│   │           │                           ├── ShowDetailScreen.kt
│   │           │                           └── components/
│   │           │                               ├── ContinueTrackingCard.kt
│   │           │                               ├── ContinueTrackingSection.kt
│   │           │                               ├── SeasonChipItem.kt
│   │           │                               ├── ShowListSheetContent.kt
│   │           │                               └── WatchProgressSection.kt
│   │           └── test/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── showdetails/
│   │                                   └── roborrazi/
│   │                                       ├── ShowDetailsScreenScreenshotTest.kt
│   │                                       └── ShowListSheetScreenshotTest.kt
│   ├── trailers/
│   │   ├── nav/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       └── commonMain/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── trailers/
│   │   │                               └── nav/
│   │   │                                   └── TrailersRoute.kt
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── presenter/
│   │   │       │                       └── trailers/
│   │   │       │                           ├── Mapper.kt
│   │   │       │                           ├── TrailersAction.kt
│   │   │       │                           ├── TrailersPresenter.kt
│   │   │       │                           ├── TrailersState.kt
│   │   │       │                           └── model/
│   │   │       │                               └── Trailer.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── presenter/
│   │   │                               └── trailers/
│   │   │                                   └── TrailersPresenterTest.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           └── main/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── trailers/
│   │                                   └── ui/
│   │                                       ├── TrailerPreviewParameterProvider.kt
│   │                                       └── TrailersScreen.kt
│   ├── upnext/
│   │   ├── presenter/
│   │   │   ├── build.gradle.kts
│   │   │   └── src/
│   │   │       ├── commonMain/
│   │   │       │   └── kotlin/
│   │   │       │       └── com/
│   │   │       │           └── thomaskioko/
│   │   │       │               └── tvmaniac/
│   │   │       │                   └── presentation/
│   │   │       │                       └── upnext/
│   │   │       │                           ├── UpNextAction.kt
│   │   │       │                           ├── UpNextPresenter.kt
│   │   │       │                           ├── UpNextState.kt
│   │   │       │                           └── model/
│   │   │       │                               └── UpNextEpisodeUiModel.kt
│   │   │       └── commonTest/
│   │   │           └── kotlin/
│   │   │               └── com/
│   │   │                   └── thomaskioko/
│   │   │                       └── tvmaniac/
│   │   │                           └── presentation/
│   │   │                               └── upnext/
│   │   │                                   └── UpNextPresenterTest.kt
│   │   └── ui/
│   │       ├── build.gradle.kts
│   │       └── src/
│   │           ├── main/
│   │           │   └── kotlin/
│   │           │       └── com/
│   │           │           └── thomaskioko/
│   │           │               └── tvmaniac/
│   │           │                   └── ui/
│   │           │                       └── upnext/
│   │           │                           ├── UpNextListItem.kt
│   │           │                           ├── UpNextScreen.kt
│   │           │                           └── preview/
│   │           │                               └── UpNextPreviewParameterProvider.kt
│   │           └── test/
│   │               └── kotlin/
│   │                   └── com/
│   │                       └── thomaskioko/
│   │                           └── tvmaniac/
│   │                               └── ui/
│   │                                   └── upnext/
│   │                                       └── roborrazi/
│   │                                           └── UpNextScreenshotTest.kt
│   └── watchlist/
│       ├── presenter/
│       │   ├── build.gradle.kts
│       │   └── src/
│       │       ├── commonMain/
│       │       │   └── kotlin/
│       │       │       └── com/
│       │       │           └── thomaskioko/
│       │       │               └── tvmaniac/
│       │       │                   └── watchlist/
│       │       │                       └── presenter/
│       │       │                           ├── Mapper.kt
│       │       │                           ├── WatchlistAction.kt
│       │       │                           ├── WatchlistPresenter.kt
│       │       │                           ├── WatchlistState.kt
│       │       │                           └── model/
│       │       │                               ├── EpisodeBadge.kt
│       │       │                               ├── NextEpisodeItem.kt
│       │       │                               ├── SectionedEpisodes.kt
│       │       │                               ├── SectionedItems.kt
│       │       │                               ├── UpNextEpisodeItem.kt
│       │       │                               └── WatchlistItem.kt
│       │       └── commonTest/
│       │           └── kotlin/
│       │               └── com/
│       │                   └── thomaskioko/
│       │                       └── tvmaniac/
│       │                           ├── domain/
│       │                           │   └── watchlist/
│       │                           │       ├── MockData.kt
│       │                           │       └── WatchlistPresenterTest.kt
│       │                           └── watchlist/
│       │                               └── presenter/
│       │                                   └── FakeWatchlistPresenterBuilder.kt
│       └── ui/
│           ├── build.gradle.kts
│           └── src/
│               ├── main/
│               │   └── kotlin/
│               │       └── com/
│               │           └── thomaskioko/
│               │               └── tvmaniac/
│               │                   └── ui/
│               │                       └── library/
│               │                           ├── WatchListUpNextListItem.kt
│               │                           ├── WatchlistListItem.kt
│               │                           ├── WatchlistPreviewParameterProvider.kt
│               │                           ├── WatchlistScreen.kt
│               │                           └── component/
│               │                               └── Searchbar.kt
│               └── test/
│                   └── kotlin/
│                       └── com/
│                           └── thomaskioko/
│                               └── tvmaniac/
│                                   └── watchlist/
│                                       └── roborrazi/
│                                           └── WatchlistScreenTest.kt
├── gradle/
│   ├── gradle-daemon-jvm.properties
│   ├── libs.versions.toml
│   ├── lint.xml
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── i18n/
│   ├── api/
│   │   ├── build.gradle.kts
│   │   └── src/
│   │       └── commonMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── i18n/
│   │                               └── api/
│   │                                   └── Localizer.kt
│   ├── generator/
│   │   ├── build.gradle.kts
│   │   ├── lint-baseline.xml
│   │   └── src/
│   │       ├── androidMain/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── i18n/
│   │       │                       └── Resources.kt
│   │       ├── commonMain/
│   │       │   └── moko-resources/
│   │       │       ├── base/
│   │       │       │   ├── plurals.xml
│   │       │       │   └── strings.xml
│   │       │       ├── de/
│   │       │       │   ├── plurals.xml
│   │       │       │   └── strings.xml
│   │       │       └── fr/
│   │       │           ├── plurals.xml
│   │       │           └── strings.xml
│   │       ├── commonTest/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── i18n/
│   │       │                       └── generator/
│   │       │                           └── ResourceTest.kt
│   │       └── iosMain/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── i18n/
│   │                               └── Resources.kt
│   ├── implementation/
│   │   ├── build.gradle.kts
│   │   ├── lint-baseline.xml
│   │   └── src/
│   │       ├── androidHostTest/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── i18n/
│   │       │                       └── util/
│   │       │                           └── BaseResourceTests.kt
│   │       ├── androidMain/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── i18n/
│   │       │                       └── PlatformLocalizer.android.kt
│   │       ├── commonMain/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── i18n/
│   │       │                       ├── LocalizedErrorToStringMapper.kt
│   │       │                       ├── MokoLocaleInitializer.kt
│   │       │                       ├── MokoResourcesLocalizer.kt
│   │       │                       ├── PlatformLocalizer.kt
│   │       │                       └── di/
│   │       │                           └── MokoLocaleInitializerBindingContainer.kt
│   │       ├── commonTest/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── i18n/
│   │       │                       ├── LocalizedStringTest.kt
│   │       │                       ├── MokoLocalizerTest.kt
│   │       │                       └── util/
│   │       │                           └── BaseResourceTests.kt
│   │       ├── iosMain/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── i18n/
│   │       │                       └── PlatformLocalizer.ios.kt
│   │       ├── iosTest/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── i18n/
│   │       │                       └── util/
│   │       │                           └── BaseResourceTests.kt
│   │       ├── jvmMain/
│   │       │   └── kotlin/
│   │       │       └── com/
│   │       │           └── thomaskioko/
│   │       │               └── tvmaniac/
│   │       │                   └── i18n/
│   │       │                       └── PlatformLocalizer.jvm.kt
│   │       └── jvmTest/
│   │           └── kotlin/
│   │               └── com/
│   │                   └── thomaskioko/
│   │                       └── tvmaniac/
│   │                           └── i18n/
│   │                               └── util/
│   │                                   └── BaseResourceTests.kt
│   └── testing/
│       ├── build.gradle.kts
│       └── src/
│           ├── androidMain/
│           │   └── kotlin/
│           │       └── com/
│           │           └── thomaskioko/
│           │               └── tvmaniac/
│           │                   └── i18n/
│           │                       └── testing/
│           │                           └── util/
│           │                               ├── BaseLocalizerTest.android.kt
│           │                               └── StringDescExt.kt
│           ├── commonMain/
│           │   └── kotlin/
│           │       └── com/
│           │           └── thomaskioko/
│           │               └── tvmaniac/
│           │                   └── i18n/
│           │                       └── testing/
│           │                           ├── FakeLocalizer.kt
│           │                           └── util/
│           │                               ├── BaseLocalizerTest.kt
│           │                               └── StringDescExt.kt
│           ├── iosMain/
│           │   └── kotlin/
│           │       └── com/
│           │           └── thomaskioko/
│           │               └── tvmaniac/
│           │                   └── i18n/
│           │                       └── testing/
│           │                           └── util/
│           │                               ├── BaseLocalizerTest.ios.kt
│           │                               └── StringDescExt.ios.kt
│           └── jvmMain/
│               └── kotlin/
│                   └── com/
│                       └── thomaskioko/
│                           └── tvmaniac/
│                               └── i18n/
│                                   └── testing/
│                                       └── util/
│                                           ├── BaseLocalizerTest.jvm.kt
│                                           └── StringDescExt.jvm.kt
├── ios/
│   ├── .gitignore
│   ├── .swiftformat
│   ├── Config/
│   │   ├── Debug.xcconfig
│   │   └── Release.xcconfig
│   ├── Modules/
│   │   ├── CoreKit/
│   │   │   ├── Package.swift
│   │   │   └── Sources/
│   │   │       └── CoreKit/
│   │   │           ├── CoreLogger.swift
│   │   │           ├── DefaultDiagnosticLogger.swift
│   │   │           ├── DiagnosticLogger.swift
│   │   │           ├── FirebaseCrashlyticsBridge.swift
│   │   │           ├── ImageCacheManager.swift
│   │   │           ├── MemoryMonitor.swift
│   │   │           └── SystemMemory.swift
│   │   ├── SnapshotTestingLib/
│   │   │   ├── .gitignore
│   │   │   ├── .swiftpm/
│   │   │   │   └── xcode/
│   │   │   │       └── package.xcworkspace/
│   │   │   │           └── xcshareddata/
│   │   │   │               └── IDEWorkspaceChecks.plist
│   │   │   ├── Package.swift
│   │   │   └── Sources/
│   │   │       └── SnapshotTestingLib/
│   │   │           └── SnapshotTesting+Extensions.swift
│   │   ├── SwiftUIComponents/
│   │   │   ├── .gitignore
│   │   │   ├── .swiftpm/
│   │   │   │   └── xcode/
│   │   │   │       └── package.xcworkspace/
│   │   │   │           └── xcshareddata/
│   │   │   │               └── IDEWorkspaceChecks.plist
│   │   │   ├── Package.swift
│   │   │   ├── Sources/
│   │   │   │   └── SwiftUIComponents/
│   │   │   │       ├── Components/
│   │   │   │       │   ├── BorderTextView.swift
│   │   │   │       │   ├── BottomSheet/
│   │   │   │       │   │   └── EpisodeDetailSheetContent.swift
│   │   │   │       │   ├── Buttons/
│   │   │   │       │   │   ├── CircularButton.swift
│   │   │   │       │   │   ├── FilledImageButton.swift
│   │   │   │       │   │   ├── OutlinedButton.swift
│   │   │   │       │   │   ├── RoundedButton.swift
│   │   │   │       │   │   └── TvManiacButton.swift
│   │   │   │       │   ├── CarouselView.swift
│   │   │   │       │   ├── CastListView.swift
│   │   │   │       │   ├── ChevronTitle.swift
│   │   │   │       │   ├── ChipView.swift
│   │   │   │       │   ├── CircularIndicator.swift
│   │   │   │       │   ├── ContinueTracking/
│   │   │   │       │   │   ├── ContinueTrackingCard.swift
│   │   │   │       │   │   ├── ContinueTrackingSection.swift
│   │   │   │       │   │   └── SwiftContinueTrackingEpisode.swift
│   │   │   │       │   ├── EmptyUIView.swift
│   │   │   │       │   ├── Episode/
│   │   │   │       │   │   ├── EpisodeCollapsible.swift
│   │   │   │       │   │   ├── EpisodeItemView.swift
│   │   │   │       │   │   └── EpisodeListView.swift
│   │   │   │       │   ├── FilterChip.swift
│   │   │   │       │   ├── FilterChipSection.swift
│   │   │   │       │   ├── FlowLayout.swift
│   │   │   │       │   ├── FullScreenView.swift
│   │   │   │       │   ├── GlassButton.swift
│   │   │   │       │   ├── GlassToolbar.swift
│   │   │   │       │   ├── GridView.swift
│   │   │   │       │   ├── HorizontalItemListView.swift
│   │   │   │       │   ├── ImageGalleryContentView.swift
│   │   │   │       │   ├── Images/
│   │   │   │       │   │   ├── AvatarView.swift
│   │   │   │       │   │   ├── BackdropPosterCard.swift
│   │   │   │       │   │   ├── CastCardView.swift
│   │   │   │       │   │   ├── FeaturedContentPosterView.swift
│   │   │   │       │   │   ├── HeaderCoverArtWorkView.swift
│   │   │   │       │   │   ├── LazyResizableImage.swift
│   │   │   │       │   │   ├── PosterCardView.swift
│   │   │   │       │   │   ├── PosterItemView.swift
│   │   │   │       │   │   ├── PosterPlaceholder.swift
│   │   │   │       │   │   ├── ProviderItemView.swift
│   │   │   │       │   │   └── TransparentImageBackground.swift
│   │   │   │       │   ├── LibraryListItemView.swift
│   │   │   │       │   ├── LoadingIndicatorView.swift
│   │   │   │       │   ├── Models/
│   │   │   │       │   │   ├── DebugMenuItem.swift
│   │   │   │       │   │   ├── SettingsModels.swift
│   │   │   │       │   │   ├── ShowPosterImage.swift
│   │   │   │       │   │   ├── SwiftCalendarDateGroup.swift
│   │   │   │       │   │   ├── SwiftCast.swift
│   │   │   │       │   │   ├── SwiftGenreRow.swift
│   │   │   │       │   │   ├── SwiftGenres.swift
│   │   │   │       │   │   ├── SwiftLibraryItem.swift
│   │   │   │       │   │   ├── SwiftProfile.swift
│   │   │   │       │   │   ├── SwiftProviders.swift
│   │   │   │       │   │   ├── SwiftSearchShow.swift
│   │   │   │       │   │   ├── SwiftSeason.swift
│   │   │   │       │   │   ├── SwiftShow.swift
│   │   │   │       │   │   ├── SwiftShowGenre.swift
│   │   │   │       │   │   ├── SwiftTrailer.swift
│   │   │   │       │   │   └── SwiftTraktListItem.swift
│   │   │   │       │   ├── NavigationTopBar.swift
│   │   │   │       │   ├── NextEpisode/
│   │   │   │       │   │   ├── NextEpisodeCard.swift
│   │   │   │       │   │   ├── NextEpisodesSection.swift
│   │   │   │       │   │   ├── SwiftNextEpisode.swift
│   │   │   │       │   │   └── UpNextListItemView.swift
│   │   │   │       │   ├── NotificationRationaleSheet.swift
│   │   │   │       │   ├── OverviewBoxView.swift
│   │   │   │       │   ├── ParallaxView.swift
│   │   │   │       │   ├── ProviderListView.swift
│   │   │   │       │   ├── ScanlineOverlay.swift
│   │   │   │       │   ├── Search/
│   │   │   │       │   │   ├── SearchItemView.swift
│   │   │   │       │   │   ├── SearchResultListView.swift
│   │   │   │       │   │   └── ShowContent/
│   │   │   │       │   │       ├── HorizontalShowContentView.swift
│   │   │   │       │   │       └── ShowContentItemView.swift
│ 
Copy disabled (too large) Download .json
Condensed preview — 1716 files, each showing path, character count, and a content snippet. Download the .json file for the full structured content (10,518K chars).
[
  {
    "path": ".editorconfig",
    "chars": 832,
    "preview": "root = true\n\n[*]\ncharset = utf-8\nindent_size = 4\nindent_style = space\ninsert_final_newline = true\ntrim_trailing_whitespa"
  },
  {
    "path": ".geminiignore",
    "chars": 209,
    "preview": ".gradle/\nbuild/\n.kotlin/\n.idea/\n.build/\nios/build/\nios/SourcePackages/\nderived_data/\n*.log\n*.class\n*.apk\n*.ap_\n*.dex\nloc"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "chars": 2478,
    "preview": "name: Bug Report\ndescription: Report a reproducible bug in TvManiac.\ntitle: \"[Bug]: \"\nlabels: [\"bug\"]\nbody:\n  - type: ma"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 28,
    "preview": "blank_issues_enabled: false\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yml",
    "chars": 1618,
    "preview": "name: Feature Request\ndescription: Suggest a new feature or improvement for TvManiac.\ntitle: \"[Feature]: \"\nlabels: [\"enh"
  },
  {
    "path": ".github/actions/setup-android-release/action.yml",
    "chars": 1642,
    "preview": "name: 'Setup Android Release'\ndescription: 'Common setup for Android release builds: Gradle, Ruby, google-services.json,"
  },
  {
    "path": ".github/actions/setup-gradle/action.yml",
    "chars": 1155,
    "preview": "name: 'Setup Gradle Environment'\ndescription: 'Common setup for Gradle builds with JDK 21 and environment variables'\n\nin"
  },
  {
    "path": ".github/actions/setup-ios/action.yml",
    "chars": 1202,
    "preview": "name: 'Setup iOS Environment'\ndescription: 'Common setup for iOS builds with Xcode, Ruby, SPM cache, and Gradle'\n\ninputs"
  },
  {
    "path": ".github/actions/setup-ios-release/action.yml",
    "chars": 990,
    "preview": "name: 'Setup iOS Release'\ndescription: 'Common setup for iOS release builds: Xcode, Ruby, SPM cache, Gradle, and GoogleS"
  },
  {
    "path": ".github/release.yml",
    "chars": 528,
    "preview": "changelog:\n  exclude:\n    labels:\n      - skip-changelog\n    authors:\n      - renovate[bot]\n      - dependabot[bot]\n  ca"
  },
  {
    "path": ".github/renovate.json",
    "chars": 1702,
    "preview": "{\n    \"$schema\": \"https://docs.renovatebot.com/renovate-schema.json\",\n    \"extends\": [\n        \"config:recommended\",\n   "
  },
  {
    "path": ".github/workflows/baseline-profile.yml",
    "chars": 1817,
    "preview": "name: Weekly Baseline Profile Generation\n\non:\n  schedule:\n    - cron: '30 0 * * 0,3,5' #Every Sunday, Wednesday, and Fri"
  },
  {
    "path": ".github/workflows/beta-release.yml",
    "chars": 5092,
    "preview": "name: Beta Release\n\non:\n  workflow_dispatch:\n    inputs:\n      skip_android:\n        description: 'Skip Android build'\n "
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 7202,
    "preview": "name: build\n\non:\n    push:\n        branches: [ main ]\n    pull_request:\n        types: [ opened, synchronize ]\n    workf"
  },
  {
    "path": ".github/workflows/compare-screenshot.yml",
    "chars": 5726,
    "preview": "name: Compare Screenshot\n\non:\n  pull_request:\n\npermissions: {}\n\nenv:\n  TMDB_API_KEY: ${{ secrets.TMDB_API_KEY }}\n  TRAKT"
  },
  {
    "path": ".github/workflows/daily-build.yml",
    "chars": 4902,
    "preview": "name: Daily Build\n\non:\n  workflow_dispatch:\n  # Uncomment to enable scheduled daily builds\n  # schedule:\n  #   - cron: '"
  },
  {
    "path": ".github/workflows/nightly-integration-tests.yml",
    "chars": 2480,
    "preview": "name: Nightly Integration Tests\n\non:\n  schedule:\n    - cron: '30 1 * * *'\n  workflow_dispatch:\n  workflow_call:\n\nconcurr"
  },
  {
    "path": ".github/workflows/promote-release.yml",
    "chars": 9478,
    "preview": "name: Promote Release\n\non:\n  # schedule:\n  #   - cron: '0 5 * * *'\n  workflow_dispatch:\n    inputs:\n      android_rollou"
  },
  {
    "path": ".github/workflows/release.yml",
    "chars": 6336,
    "preview": "name: Release\n\non:\n  push:\n    tags:\n      - 'v[0-9]+.[0-9]+.[0-9]+'\n\npermissions:\n  contents: write\n\nenv:\n  XCODE_VERSI"
  },
  {
    "path": ".github/workflows/store-screenshot.yml",
    "chars": 1423,
    "preview": "name: Store Screenshot\n\non:\n  push:\n    branches:\n      - main\n\npermissions: {}\n\nenv:\n  TMDB_API_KEY: ${{ secrets.TMDB_A"
  },
  {
    "path": ".gitignore",
    "chars": 1635,
    "preview": "# Built application files\n*.apk\n*.ap_\n\n# Files for the ART/Dalvik VM\n*.dex\n\n# Java class files\n*.class\n\n# Generated file"
  },
  {
    "path": ".idea/codeStyles/Project.xml",
    "chars": 5383,
    "preview": "<component name=\"ProjectCodeStyleConfiguration\">\n  <code_scheme name=\"Project\" version=\"173\">\n    <JavaCodeStyleSettings"
  },
  {
    "path": ".idea/codeStyles/codeStyleConfig.xml",
    "chars": 151,
    "preview": "<component name=\"ProjectCodeStyleConfiguration\">\n  <state>\n    <option name=\"PREFERRED_PROJECT_CODE_STYLE\" value=\"tv-man"
  },
  {
    "path": ".idea/dictionaries/project.xml",
    "chars": 163,
    "preview": "<component name=\"ProjectDictionaryState\">\n  <dictionary name=\"project\">\n    <words>\n      <w>tmdb</w>\n      <w>trakt</w>"
  },
  {
    "path": ".ruby-version",
    "chars": 6,
    "preview": "3.3.0\n"
  },
  {
    "path": ".swiftformat",
    "chars": 177,
    "preview": "--indent 4\n--exclude ios/build,ios/Modules/*/.build,**/.build,**/build,**/ios-framework/build,ios/SourcePackages,ios/der"
  },
  {
    "path": ".swiftlint.yml",
    "chars": 715,
    "preview": "cyclomatic_complexity:\n    warning: 15\n    error: 20\n\nfunction_body_length:\n    warning: 75\n    error: 100\n\ndisabled_rul"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 1007,
    "preview": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\n## [0.1.2] - 2026-03-25\n\n### Bug Fixe"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 4088,
    "preview": "# Contributing to TvManiac\n\nTvManiac is a personal learning playground for Kotlin Multiplatform development. Contributio"
  },
  {
    "path": "GEMINI.md",
    "chars": 5303,
    "preview": "# TV Maniac Agent Rules\n\n## Project Overview\nTV Maniac is a Kotlin Multiplatform (KMP) project for tracking TV shows. It"
  },
  {
    "path": "Gemfile",
    "chars": 213,
    "preview": "source \"https://rubygems.org\"\n\ngem \"fastlane\"\ngem \"xcode-install\"\ngem \"xcpretty\"\n\nplugins_path = File.join(File.dirname("
  },
  {
    "path": "LICENSE",
    "chars": 11324,
    "preview": " Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licen"
  },
  {
    "path": "README.md",
    "chars": 7104,
    "preview": "<p align=\"center\">\n<img src=\"art/TvManiacBanner.png\" width=\"100%\" />\n</p>\n\n# TvManiac\n\n![Check](https://github.com/thoma"
  },
  {
    "path": "android-designsystem/build.gradle.kts",
    "chars": 1227,
    "preview": "plugins { alias(libs.plugins.app.android) }\n\nscaffold {\n    android {\n        enableAndroidResources()\n\n        useCompo"
  },
  {
    "path": "android-designsystem/src/debug/res/values/strings.xml",
    "chars": 131,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name\" translatable=\"false\">TvManiac</string>\n</"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/Background.kt",
    "chars": 1958,
    "preview": "package com.thomaskioko.tvmaniac.compose.components\n\nimport android.content.res.Configuration\nimport androidx.compose.fo"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/BadgeChip.kt",
    "chars": 1890,
    "preview": "package com.thomaskioko.tvmaniac.compose.components\n\nimport androidx.compose.foundation.layout.padding\nimport androidx.c"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/Buttons.kt",
    "chars": 14717,
    "preview": "package com.thomaskioko.tvmaniac.compose.components\n\nimport androidx.compose.animation.Crossfade\nimport androidx.compose"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/Card.kt",
    "chars": 11456,
    "preview": "package com.thomaskioko.tvmaniac.compose.components\n\nimport androidx.compose.foundation.background\nimport androidx.compo"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/Chip.kt",
    "chars": 1967,
    "preview": "package com.thomaskioko.tvmaniac.compose.components\n\nimport androidx.compose.foundation.layout.padding\nimport androidx.c"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/Dialogs.kt",
    "chars": 4325,
    "preview": "package com.thomaskioko.tvmaniac.compose.components\n\nimport androidx.compose.foundation.layout.widthIn\nimport androidx.c"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/EmptyLayout.kt",
    "chars": 3318,
    "preview": "package com.thomaskioko.tvmaniac.compose.components\n\nimport androidx.compose.foundation.layout.Arrangement\nimport androi"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/ErrorLayout.kt",
    "chars": 3999,
    "preview": "package com.thomaskioko.tvmaniac.compose.components\n\nimport androidx.compose.animation.animateColorAsState\nimport androi"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/FilterChipSection.kt",
    "chars": 8460,
    "preview": "package com.thomaskioko.tvmaniac.compose.components\n\nimport androidx.compose.animation.animateContentSize\nimport android"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/GradientScrim.kt",
    "chars": 1569,
    "preview": "package com.thomaskioko.tvmaniac.compose.components\n\nimport android.annotation.SuppressLint\nimport androidx.compose.runt"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/Image.kt",
    "chars": 8098,
    "preview": "package com.thomaskioko.tvmaniac.compose.components\n\nimport androidx.compose.foundation.BorderStroke\nimport androidx.com"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/NavigationBar.kt",
    "chars": 4214,
    "preview": "package com.thomaskioko.tvmaniac.compose.components\n\nimport androidx.compose.foundation.layout.RowScope\nimport androidx."
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/NotificationRationaleContent.kt",
    "chars": 6988,
    "preview": "package com.thomaskioko.tvmaniac.compose.components\n\nimport androidx.compose.foundation.background\nimport androidx.compo"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/PosterPlaceholder.kt",
    "chars": 3288,
    "preview": "package com.thomaskioko.tvmaniac.compose.components\n\nimport androidx.compose.foundation.background\nimport androidx.compo"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/ProgressIndicator.kt",
    "chars": 1102,
    "preview": "package com.thomaskioko.tvmaniac.compose.components\n\nimport androidx.compose.foundation.layout.Box\nimport androidx.compo"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/ScanlineOverlay.kt",
    "chars": 2560,
    "preview": "package com.thomaskioko.tvmaniac.compose.components\n\nimport androidx.compose.foundation.Canvas\nimport androidx.compose.f"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/SearchTextField.kt",
    "chars": 7603,
    "preview": "package com.thomaskioko.tvmaniac.compose.components\n\nimport androidx.compose.foundation.gestures.detectTapGestures\nimpor"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/SegmentedProgressBar.kt",
    "chars": 2841,
    "preview": "package com.thomaskioko.tvmaniac.compose.components\n\nimport androidx.compose.foundation.background\nimport androidx.compo"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/SheetDragHandle.kt",
    "chars": 3287,
    "preview": "package com.thomaskioko.tvmaniac.compose.components\n\nimport androidx.compose.foundation.background\nimport androidx.compo"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/ShowLinearProgressIndicator.kt",
    "chars": 1177,
    "preview": "package com.thomaskioko.tvmaniac.compose.components\n\nimport androidx.compose.material3.LinearProgressIndicator\nimport an"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/Snackbar.kt",
    "chars": 10583,
    "preview": "package com.thomaskioko.tvmaniac.compose.components\n\nimport androidx.compose.animation.AnimatedVisibility\nimport android"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/Text.kt",
    "chars": 5742,
    "preview": "package com.thomaskioko.tvmaniac.compose.components\n\nimport androidx.compose.foundation.clickable\nimport androidx.compos"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/TextTitlePill.kt",
    "chars": 2505,
    "preview": "package com.thomaskioko.tvmaniac.compose.components\n\nimport androidx.compose.foundation.BorderStroke\nimport androidx.com"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/TopBar.kt",
    "chars": 12847,
    "preview": "package com.thomaskioko.tvmaniac.compose.components\n\nimport android.annotation.SuppressLint\nimport androidx.compose.anim"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/TvManiacBottomSheet.kt",
    "chars": 3506,
    "preview": "package com.thomaskioko.tvmaniac.compose.components\n\nimport androidx.compose.foundation.layout.ColumnScope\nimport androi"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/components/TvManiacPreviewWrapperProvider.kt",
    "chars": 568,
    "preview": "package com.thomaskioko.tvmaniac.compose.components\n\nimport androidx.compose.runtime.Composable\nimport androidx.compose."
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/extensions/GradientExtensions.kt",
    "chars": 1104,
    "preview": "package com.thomaskioko.tvmaniac.compose.extensions\n\nimport androidx.compose.material3.MaterialTheme\nimport androidx.com"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/extensions/LazyListExtensions.kt",
    "chars": 1280,
    "preview": "package com.thomaskioko.tvmaniac.compose.extensions\n\nimport androidx.compose.runtime.Composable\nimport androidx.compose."
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/extensions/PaddingValuesExtentions.kt",
    "chars": 1085,
    "preview": "package com.thomaskioko.tvmaniac.compose.extensions\n\nimport androidx.compose.foundation.layout.PaddingValues\nimport andr"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/extensions/ScrimExtentions.kt",
    "chars": 3208,
    "preview": "package com.thomaskioko.tvmaniac.compose.extensions\n\nimport android.annotation.SuppressLint\nimport androidx.annotation.F"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/theme/Background.kt",
    "chars": 584,
    "preview": "package com.thomaskioko.tvmaniac.compose.theme\n\nimport androidx.compose.runtime.Immutable\nimport androidx.compose.runtim"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/theme/Colors.kt",
    "chars": 6623,
    "preview": "package com.thomaskioko.tvmaniac.compose.theme\n\nimport androidx.compose.ui.graphics.Color\n\npublic val green: Color = Col"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/theme/Shape.kt",
    "chars": 341,
    "preview": "package com.thomaskioko.tvmaniac.compose.theme\n\nimport androidx.compose.foundation.shape.RoundedCornerShape\nimport andro"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/theme/Theme.kt",
    "chars": 6766,
    "preview": "package com.thomaskioko.tvmaniac.compose.theme\n\nimport androidx.compose.foundation.isSystemInDarkTheme\nimport androidx.c"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/theme/Type.kt",
    "chars": 4150,
    "preview": "package com.thomaskioko.tvmaniac.compose.theme\n\nimport androidx.compose.material3.Typography\nimport androidx.compose.run"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/util/AutoAdvanceLocal.kt",
    "chars": 802,
    "preview": "package com.thomaskioko.tvmaniac.compose.util\n\nimport androidx.compose.runtime.ProvidableCompositionLocal\nimport android"
  },
  {
    "path": "android-designsystem/src/main/kotlin/com/thomaskioko/tvmaniac/compose/util/DynamicTheming.kt",
    "chars": 5524,
    "preview": "package com.thomaskioko.tvmaniac.compose.util\n\nimport android.content.Context\nimport androidx.collection.LruCache\nimport"
  },
  {
    "path": "android-designsystem/src/main/res/values/strings.xml",
    "chars": 131,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name\" translatable=\"false\">TvManiac</string>\n</"
  },
  {
    "path": "android-designsystem/src/test/kotlin/com/thomaskioko/tvmaniac/compose/roborazzi/NotificationRationaleContentScreenshotTest.kt",
    "chars": 1206,
    "preview": "package com.thomaskioko.tvmaniac.compose.roborazzi\n\nimport androidx.activity.ComponentActivity\nimport androidx.compose.m"
  },
  {
    "path": "android-designsystem/src/test/kotlin/com/thomaskioko/tvmaniac/compose/roborazzi/TvManiacSnackBarScreenshotTest.kt",
    "chars": 2295,
    "preview": "package com.thomaskioko.tvmaniac.compose.roborazzi\n\nimport androidx.activity.ComponentActivity\nimport androidx.compose.m"
  },
  {
    "path": "api/tmdb/api/build.gradle.kts",
    "chars": 294,
    "preview": "plugins {\n    alias(libs.plugins.app.kmp)\n}\n\nscaffold {\n    useSerialization()\n}\n\nkotlin {\n    sourceSets {\n        comm"
  },
  {
    "path": "api/tmdb/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/tmdb/api/TmdbConfig.kt",
    "chars": 105,
    "preview": "package com.thomaskioko.tvmaniac.tmdb.api\n\npublic interface TmdbConfig {\n    public val apiKey: String\n}\n"
  },
  {
    "path": "api/tmdb/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/tmdb/api/TmdbSeasonDetailsNetworkDataSource.kt",
    "chars": 488,
    "preview": "package com.thomaskioko.tvmaniac.tmdb.api\n\nimport com.thomaskioko.tvmaniac.core.networkutil.api.model.ApiResponse\nimport"
  },
  {
    "path": "api/tmdb/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/tmdb/api/TmdbShowDetailsNetworkDataSource.kt",
    "chars": 1213,
    "preview": "package com.thomaskioko.tvmaniac.tmdb.api\n\nimport com.thomaskioko.tvmaniac.core.networkutil.api.model.ApiResponse\nimport"
  },
  {
    "path": "api/tmdb/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/tmdb/api/TmdbShowsNetworkDataSource.kt",
    "chars": 3559,
    "preview": "package com.thomaskioko.tvmaniac.tmdb.api\n\nimport com.thomaskioko.tvmaniac.core.networkutil.api.model.ApiResponse\nimport"
  },
  {
    "path": "api/tmdb/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/tmdb/api/model/CreditsResponse.kt",
    "chars": 555,
    "preview": "package com.thomaskioko.tvmaniac.tmdb.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Se"
  },
  {
    "path": "api/tmdb/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/tmdb/api/model/EpisodesResponse.kt",
    "chars": 747,
    "preview": "package com.thomaskioko.tvmaniac.tmdb.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Se"
  },
  {
    "path": "api/tmdb/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/tmdb/api/model/GenreResponse.kt",
    "chars": 258,
    "preview": "package com.thomaskioko.tvmaniac.tmdb.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Se"
  },
  {
    "path": "api/tmdb/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/tmdb/api/model/ImagesResponse.kt",
    "chars": 585,
    "preview": "package com.thomaskioko.tvmaniac.tmdb.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Se"
  },
  {
    "path": "api/tmdb/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/tmdb/api/model/LastEpisodeToAirResponse.kt",
    "chars": 804,
    "preview": "package com.thomaskioko.tvmaniac.tmdb.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Se"
  },
  {
    "path": "api/tmdb/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/tmdb/api/model/NetworksResponse.kt",
    "chars": 320,
    "preview": "package com.thomaskioko.tvmaniac.tmdb.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Se"
  },
  {
    "path": "api/tmdb/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/tmdb/api/model/NextEpisodeToAirResponse.kt",
    "chars": 804,
    "preview": "package com.thomaskioko.tvmaniac.tmdb.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Se"
  },
  {
    "path": "api/tmdb/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/tmdb/api/model/SeasonsResponse.kt",
    "chars": 607,
    "preview": "package com.thomaskioko.tvmaniac.tmdb.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Se"
  },
  {
    "path": "api/tmdb/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/tmdb/api/model/TmdbGenreResult.kt",
    "chars": 247,
    "preview": "package com.thomaskioko.tvmaniac.tmdb.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Se"
  },
  {
    "path": "api/tmdb/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/tmdb/api/model/TmdbSeasonDetailsResponse.kt",
    "chars": 789,
    "preview": "package com.thomaskioko.tvmaniac.tmdb.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Se"
  },
  {
    "path": "api/tmdb/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/tmdb/api/model/TmdbShowDetailsResponse.kt",
    "chars": 1629,
    "preview": "package com.thomaskioko.tvmaniac.tmdb.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Se"
  },
  {
    "path": "api/tmdb/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/tmdb/api/model/TmdbShowResponse.kt",
    "chars": 1226,
    "preview": "package com.thomaskioko.tvmaniac.tmdb.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Se"
  },
  {
    "path": "api/tmdb/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/tmdb/api/model/VideosResponse.kt",
    "chars": 706,
    "preview": "package com.thomaskioko.tvmaniac.tmdb.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Se"
  },
  {
    "path": "api/tmdb/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/tmdb/api/model/WatchProvidersResult.kt",
    "chars": 1503,
    "preview": "package com.thomaskioko.tvmaniac.tmdb.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Se"
  },
  {
    "path": "api/tmdb/implementation/build.gradle.kts",
    "chars": 1186,
    "preview": "plugins {\n    alias(libs.plugins.app.kmp)\n}\n\nscaffold {\n    addAndroidTarget()\n    useSerialization()\n    useMetro()\n}\n\n"
  },
  {
    "path": "api/tmdb/implementation/src/androidMain/kotlin/com/thomaskioko/tvmaniac/tmdb/implementation/TmdbPlatformBindingContainer.kt",
    "chars": 624,
    "preview": "package com.thomaskioko.tvmaniac.tmdb.implementation\n\nimport com.thomaskioko.tvmaniac.core.base.TmdbApi\nimport dev.zacsw"
  },
  {
    "path": "api/tmdb/implementation/src/commonMain/kotlin/com/thomaskioko/tvmaniac/tmdb/implementation/DefaultTmdbSeasonDetailsNetworkDataSource.kt",
    "chars": 1212,
    "preview": "package com.thomaskioko.tvmaniac.tmdb.implementation\n\nimport com.thomaskioko.tvmaniac.core.base.TmdbApi\nimport com.thoma"
  },
  {
    "path": "api/tmdb/implementation/src/commonMain/kotlin/com/thomaskioko/tvmaniac/tmdb/implementation/DefaultTmdbShowDetailsNetworkDataSource.kt",
    "chars": 2202,
    "preview": "package com.thomaskioko.tvmaniac.tmdb.implementation\n\nimport com.thomaskioko.tvmaniac.core.base.TmdbApi\nimport com.thoma"
  },
  {
    "path": "api/tmdb/implementation/src/commonMain/kotlin/com/thomaskioko/tvmaniac/tmdb/implementation/DefaultTmdbShowsNetworkDataSource.kt",
    "chars": 5130,
    "preview": "package com.thomaskioko.tvmaniac.tmdb.implementation\n\nimport com.thomaskioko.tvmaniac.core.base.TmdbApi\nimport com.thoma"
  },
  {
    "path": "api/tmdb/implementation/src/commonMain/kotlin/com/thomaskioko/tvmaniac/tmdb/implementation/TmdbBindingContainer.kt",
    "chars": 1511,
    "preview": "package com.thomaskioko.tvmaniac.tmdb.implementation\n\nimport com.thomaskioko.tvmaniac.appconfig.ApplicationInfo\nimport c"
  },
  {
    "path": "api/tmdb/implementation/src/commonMain/kotlin/com/thomaskioko/tvmaniac/tmdb/implementation/TmdbClient.kt",
    "chars": 2984,
    "preview": "package com.thomaskioko.tvmaniac.tmdb.implementation\n\nimport com.thomaskioko.tvmaniac.core.connectivity.api.InternetConn"
  },
  {
    "path": "api/tmdb/implementation/src/iosMain/kotlin/com/thomaskioko/tvmaniac/tmdb/implementation/TmdbPlatformBindingContainer.kt",
    "chars": 624,
    "preview": "package com.thomaskioko.tvmaniac.tmdb.implementation\n\nimport com.thomaskioko.tvmaniac.core.base.TmdbApi\nimport dev.zacsw"
  },
  {
    "path": "api/trakt/api/build.gradle.kts",
    "chars": 239,
    "preview": "plugins {\n    alias(libs.plugins.app.kmp)\n}\n\nscaffold {\n    useSerialization()\n}\n\nkotlin {\n    sourceSets {\n        comm"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/TraktCalendarRemoteDataSource.kt",
    "chars": 375,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api\n\nimport com.thomaskioko.tvmaniac.core.networkutil.api.model.ApiResponse\nimpor"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/TraktConfig.kt",
    "chars": 180,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api\n\npublic interface TraktConfig {\n    public val clientId: String\n    public va"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/TraktEpisodeHistoryRemoteDataSource.kt",
    "chars": 665,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api\n\nimport com.thomaskioko.tvmaniac.core.networkutil.api.model.ApiResponse\nimpor"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/TraktListRemoteDataSource.kt",
    "chars": 1797,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api\n\nimport com.thomaskioko.tvmaniac.core.networkutil.api.model.ApiResponse\nimpor"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/TraktShowsRemoteDataSource.kt",
    "chars": 9543,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api\n\nimport com.thomaskioko.tvmaniac.core.networkutil.api.model.ApiResponse\nimpor"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/TraktSyncRemoteDataSource.kt",
    "chars": 325,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api\n\nimport com.thomaskioko.tvmaniac.core.networkutil.api.model.ApiResponse\nimpor"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/TraktTokenRemoteDataSource.kt",
    "chars": 587,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api\n\nimport com.thomaskioko.tvmaniac.core.networkutil.api.model.ApiResponse\nimpor"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/TraktUserRemoteDataSource.kt",
    "chars": 631,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api\n\nimport com.thomaskioko.tvmaniac.core.networkutil.api.model.ApiResponse\nimpor"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/model/AccessTokenBody.kt",
    "chars": 455,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.S"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/model/RefreshAccessTokenBody.kt",
    "chars": 504,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.S"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/model/TraktAccessRefreshTokenResponse.kt",
    "chars": 521,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.S"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/model/TraktAccessTokenResponse.kt",
    "chars": 514,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.S"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/model/TraktAddShowRequest.kt",
    "chars": 484,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.S"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/model/TraktAddShowToListResponse.kt",
    "chars": 1072,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.S"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/model/TraktCalendarResponse.kt",
    "chars": 1005,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.S"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/model/TraktCreateListRequest.kt",
    "chars": 664,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.S"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/model/TraktCreateListResponse.kt",
    "chars": 506,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.S"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/model/TraktFollowedShowResponse.kt",
    "chars": 916,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.S"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/model/TraktGenreResponse.kt",
    "chars": 271,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.S"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/model/TraktLastActivitiesResponse.kt",
    "chars": 1309,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.S"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/model/TraktNextEpisodeResponse.kt",
    "chars": 665,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.S"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/model/TraktPeopleResponse.kt",
    "chars": 1714,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.S"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/model/TraktPersonalListsResponse.kt",
    "chars": 862,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.S"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/model/TraktRemoveShowFromListResponse.kt",
    "chars": 497,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.S"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/model/TraktSeasonEpisodesResponse.kt",
    "chars": 1478,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.S"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/model/TraktSeasonsResponse.kt",
    "chars": 756,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.S"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/model/TraktShowsResponse.kt",
    "chars": 2407,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.S"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/model/TraktSyncModels.kt",
    "chars": 2778,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.S"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/model/TraktUserResponse.kt",
    "chars": 650,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.S"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/model/TraktUserStatsResponse.kt",
    "chars": 1054,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.S"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/model/TraktVideosResponse.kt",
    "chars": 641,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.S"
  },
  {
    "path": "api/trakt/api/src/commonMain/kotlin/com/thomaskioko/tvmaniac/trakt/api/model/TraktWatchedProgressResponse.kt",
    "chars": 580,
    "preview": "package com.thomaskioko.tvmaniac.trakt.api.model\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.S"
  },
  {
    "path": "api/trakt/implementation/build.gradle.kts",
    "chars": 1868,
    "preview": "plugins {\n    alias(libs.plugins.app.kmp)\n}\n\nscaffold {\n    addAndroidTarget()\n    useMetro()\n    useSerialization()\n}\n\n"
  },
  {
    "path": "api/trakt/implementation/src/androidMain/kotlin/com/thomaskioko/trakt/service/implementation/TraktPlatformBindingContainer.kt",
    "chars": 628,
    "preview": "package com.thomaskioko.trakt.service.implementation\n\nimport com.thomaskioko.tvmaniac.core.base.TraktApi\nimport dev.zacs"
  },
  {
    "path": "api/trakt/implementation/src/commonMain/kotlin/com/thomaskioko/trakt/service/implementation/TraktAuthPlugin.kt",
    "chars": 1878,
    "preview": "package com.thomaskioko.trakt.service.implementation\n\nimport com.thomaskioko.tvmaniac.core.networkutil.api.extensions.Re"
  },
  {
    "path": "api/trakt/implementation/src/commonMain/kotlin/com/thomaskioko/trakt/service/implementation/TraktBindingContainer.kt",
    "chars": 1655,
    "preview": "package com.thomaskioko.trakt.service.implementation\n\nimport com.thomaskioko.tvmaniac.appconfig.ApplicationInfo\nimport c"
  },
  {
    "path": "api/trakt/implementation/src/commonMain/kotlin/com/thomaskioko/trakt/service/implementation/TraktHttpClient.kt",
    "chars": 6313,
    "preview": "package com.thomaskioko.trakt.service.implementation\n\nimport com.thomaskioko.tvmaniac.core.connectivity.api.InternetConn"
  },
  {
    "path": "api/trakt/implementation/src/commonMain/kotlin/com/thomaskioko/trakt/service/implementation/api/DefaultTraktCalendarRemoteDataSource.kt",
    "chars": 1220,
    "preview": "package com.thomaskioko.trakt.service.implementation.api\n\nimport com.thomaskioko.tvmaniac.core.base.TraktApi\nimport com."
  },
  {
    "path": "api/trakt/implementation/src/commonMain/kotlin/com/thomaskioko/trakt/service/implementation/api/DefaultTraktEpisodeRemoteDataSource.kt",
    "chars": 2140,
    "preview": "package com.thomaskioko.trakt.service.implementation.api\n\nimport com.thomaskioko.tvmaniac.core.base.TraktApi\nimport com."
  },
  {
    "path": "api/trakt/implementation/src/commonMain/kotlin/com/thomaskioko/trakt/service/implementation/api/DefaultTraktListRemoteDataSource.kt",
    "chars": 6559,
    "preview": "package com.thomaskioko.trakt.service.implementation.api\n\nimport com.thomaskioko.tvmaniac.core.base.TraktApi\nimport com."
  },
  {
    "path": "api/trakt/implementation/src/commonMain/kotlin/com/thomaskioko/trakt/service/implementation/api/DefaultTraktShowsRemoteDataSource.kt",
    "chars": 7329,
    "preview": "package com.thomaskioko.trakt.service.implementation.api\n\nimport com.thomaskioko.tvmaniac.core.base.TraktApi\nimport com."
  },
  {
    "path": "api/trakt/implementation/src/commonMain/kotlin/com/thomaskioko/trakt/service/implementation/api/DefaultTraktSyncRemoteDataSource.kt",
    "chars": 1065,
    "preview": "package com.thomaskioko.trakt.service.implementation.api\n\nimport com.thomaskioko.tvmaniac.core.base.TraktApi\nimport com."
  },
  {
    "path": "api/trakt/implementation/src/commonMain/kotlin/com/thomaskioko/trakt/service/implementation/api/DefaultTraktTokenRemoteDataSource.kt",
    "chars": 3135,
    "preview": "package com.thomaskioko.trakt.service.implementation.api\n\nimport com.thomaskioko.tvmaniac.core.base.TraktApi\nimport com."
  },
  {
    "path": "api/trakt/implementation/src/commonMain/kotlin/com/thomaskioko/trakt/service/implementation/api/DefaultTraktUserRemoteDataSource.kt",
    "chars": 1740,
    "preview": "package com.thomaskioko.trakt.service.implementation.api\n\nimport com.thomaskioko.tvmaniac.core.base.TraktApi\nimport com."
  },
  {
    "path": "api/trakt/implementation/src/commonTest/kotlin/com/thomaskioko/trakt/service/implementation/TraktAuthGuardPluginTest.kt",
    "chars": 2556,
    "preview": "package com.thomaskioko.trakt.service.implementation\n\nimport com.thomaskioko.tvmaniac.core.networkutil.api.extensions.Re"
  },
  {
    "path": "api/trakt/implementation/src/iosMain/kotlin/com/thomaskioko/trakt/service/implementation/TraktPlatformBindingContainer.kt",
    "chars": 628,
    "preview": "package com.thomaskioko.trakt.service.implementation\n\nimport com.thomaskioko.tvmaniac.core.base.TraktApi\nimport dev.zacs"
  },
  {
    "path": "api/trakt/implementation/src/jvmTest/kotlin/com/thomaskioko/trakt/service/implementation/TestResourceLoader.jvm.kt",
    "chars": 187,
    "preview": "package com.thomaskioko.trakt.service.implementation\n\ninternal fun loadJson(fileName: String): String =\n    Thread.curre"
  },
  {
    "path": "api/trakt/implementation/src/jvmTest/kotlin/com/thomaskioko/trakt/service/implementation/api/DefaultTraktListRemoteDataSourceTest.kt",
    "chars": 7269,
    "preview": "package com.thomaskioko.trakt.service.implementation.api\n\nimport com.thomaskioko.trakt.service.implementation.TraktAuthG"
  },
  {
    "path": "api/trakt/implementation/src/jvmTest/resources/trakt_add_show_response.json",
    "chars": 193,
    "preview": "{\n  \"added\": {\n    \"shows\": 1\n  },\n  \"existing\": {\n    \"shows\": 0\n  },\n  \"not_found\": {\n    \"shows\": []\n  },\n  \"list\": {"
  },
  {
    "path": "api/trakt/implementation/src/jvmTest/resources/trakt_error_response.json",
    "chars": 77,
    "preview": "{\n  \"error\": \"unauthorized\",\n  \"error_description\": \"invalid access token\"\n}\n"
  },
  {
    "path": "api/trakt/implementation/src/jvmTest/resources/trakt_user_response.json",
    "chars": 174,
    "preview": "{\n  \"username\": \"sean\",\n  \"name\": \"Sean Rudford\",\n  \"images\": {\n    \"avatar\": {\n      \"full\": \"https://example.com/avata"
  },
  {
    "path": "app/benchmark-rules.pro",
    "chars": 15,
    "preview": "-dontobfuscate\n"
  },
  {
    "path": "app/build.gradle.kts",
    "chars": 11792,
    "preview": "import com.google.firebase.crashlytics.buildtools.gradle.CrashlyticsExtension\n\nplugins {\n    alias(libs.plugins.app.appl"
  },
  {
    "path": "app/lint-baseline.xml",
    "chars": 82265,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<issues format=\"6\" by=\"lint 8.12.0\" type=\"baseline\" client=\"gradle\" dependencies="
  },
  {
    "path": "app/proguard-rules.pro",
    "chars": 3572,
    "preview": "-verbose\n-allowaccessmodification\n-repackageclasses\n\n# AndroidX + support library contains references to newer platform "
  },
  {
    "path": "app/src/androidTest/kotlin/com/thomaskioko/tvmaniac/app/test/runner/TvManiacInstrumentationRunner.kt",
    "chars": 1219,
    "preview": "package com.thomaskioko.tvmaniac.app.test.runner\n\nimport android.app.Application\nimport android.content.Context\nimport a"
  },
  {
    "path": "app/src/debug/AndroidManifest.xml",
    "chars": 1785,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:to"
  },
  {
    "path": "app/src/debug/kotlin/com/thomaskioko/tvmaniac/app/debug/DebugNotificationIconProvider.kt",
    "chars": 715,
    "preview": "package com.thomaskioko.tvmaniac.app.debug\n\nimport com.thomaskioko.tvmaniac.app.R\nimport com.thomaskioko.tvmaniac.app.ut"
  },
  {
    "path": "app/src/debug/kotlin/com/thomaskioko/tvmaniac/app/debug/DebugNotificationInitializer.kt",
    "chars": 1219,
    "preview": "package com.thomaskioko.tvmaniac.app.debug\n\nimport com.thomaskioko.tvmaniac.core.base.IoCoroutineScope\nimport com.thomas"
  },
  {
    "path": "app/src/debug/kotlin/com/thomaskioko/tvmaniac/app/debug/di/DebugNotificationInitializerBindingContainer.kt",
    "chars": 708,
    "preview": "package com.thomaskioko.tvmaniac.app.debug.di\n\nimport com.thomaskioko.tvmaniac.app.debug.DebugNotificationInitializer\nim"
  },
  {
    "path": "app/src/debug/res/drawable/ic_app_launcher.xml",
    "chars": 29892,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"512dp\"\n    android:height=\"512dp\"\n"
  },
  {
    "path": "app/src/debug/res/drawable/ic_debug_bug.xml",
    "chars": 711,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n  android:widt"
  },
  {
    "path": "app/src/debug/res/drawable/ic_launcher_foreground.xml",
    "chars": 30026,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "chars": 2107,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n  xmlns:tool"
  },
  {
    "path": "app/src/main/generated/baselineProfiles/baseline-prof.txt",
    "chars": 2713177,
    "preview": "Lamazon/lastmile/inject/ComThomaskiokoTvmaniacCoreBaseDiBaseAndroidComponent;\nLamazon/lastmile/inject/ComThomaskiokoTvma"
  },
  {
    "path": "app/src/main/generated/baselineProfiles/startup-prof.txt",
    "chars": 2713177,
    "preview": "Lamazon/lastmile/inject/ComThomaskiokoTvmaniacCoreBaseDiBaseAndroidComponent;\nLamazon/lastmile/inject/ComThomaskiokoTvma"
  },
  {
    "path": "app/src/main/kotlin/com/thomaskioko/tvmaniac/app/MainActivity.kt",
    "chars": 5214,
    "preview": "package com.thomaskioko.tvmaniac.app\n\nimport android.animation.ObjectAnimator\nimport android.content.Intent\nimport andro"
  },
  {
    "path": "app/src/main/kotlin/com/thomaskioko/tvmaniac/app/TvManicApplication.kt",
    "chars": 1810,
    "preview": "package com.thomaskioko.tvmaniac.app\n\nimport android.app.Application\nimport android.os.Build\nimport android.os.StrictMod"
  },
  {
    "path": "app/src/main/kotlin/com/thomaskioko/tvmaniac/app/di/ActivityGraph.kt",
    "chars": 2282,
    "preview": "package com.thomaskioko.tvmaniac.app.di\n\nimport androidx.activity.ComponentActivity\nimport com.arkivanov.decompose.Compo"
  },
  {
    "path": "app/src/main/kotlin/com/thomaskioko/tvmaniac/app/di/ApplicationGraph.kt",
    "chars": 727,
    "preview": "package com.thomaskioko.tvmaniac.app.di\n\nimport android.app.Application\nimport com.thomaskioko.tvmaniac.app.util.TvMania"
  },
  {
    "path": "app/src/main/kotlin/com/thomaskioko/tvmaniac/app/util/AppNotificationIconProvider.kt",
    "chars": 489,
    "preview": "package com.thomaskioko.tvmaniac.app.util\n\nimport com.thomaskioko.tvmaniac.app.R\nimport com.thomaskioko.tvmaniac.core.no"
  },
  {
    "path": "app/src/main/kotlin/com/thomaskioko/tvmaniac/app/util/TvManiacWorkerFactory.kt",
    "chars": 924,
    "preview": "package com.thomaskioko.tvmaniac.app.util\n\nimport android.content.Context\nimport androidx.work.ListenableWorker\nimport a"
  },
  {
    "path": "app/src/main/res/drawable/ic_app_launcher.xml",
    "chars": 29892,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"512dp\"\n    android:height=\"512dp\"\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_launcher_background.xml",
    "chars": 4867,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector\n    android:height=\"108dp\"\n    android:width=\"108dp\"\n    android:viewport"
  },
  {
    "path": "app/src/main/res/drawable/ic_launcher_foreground.xml",
    "chars": 30026,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_launcher_monochrome.xml",
    "chars": 24904,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n  android:widt"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "chars": 340,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <b"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "chars": 340,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <b"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "chars": 116,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"splash_background\">#F8FDFF</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/themes.xml",
    "chars": 901,
    "preview": "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n\n  <style name=\"NightAdjusted.Theme.TvManiac\" parent=\"android"
  },
  {
    "path": "app/src/main/res/values-night/colors.xml",
    "chars": 116,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"splash_background\">#373737</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-night/themes.xml",
    "chars": 829,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources xmlns:tools=\"http://schemas.android.com/tools\">\n\n  <style name=\"NightA"
  },
  {
    "path": "app/src/main/res/xml/backup_rules.xml",
    "chars": 172,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<full-backup-content>\n    <exclude domain=\"sharedpref\" path=\".\" />\n    <exclude d"
  },
  {
    "path": "app/src/main/res/xml/data_extraction_rules.xml",
    "chars": 328,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<data-extraction-rules>\n    <cloud-backup>\n        <exclude domain=\"sharedpref\" /"
  },
  {
    "path": "app/src/sharedTest/kotlin/com/thomaskioko/tvmaniac/app/test/BaseAppFlowTest.kt",
    "chars": 6965,
    "preview": "package com.thomaskioko.tvmaniac.app.test\n\nimport androidx.compose.ui.test.AndroidComposeUiTest\nimport androidx.compose."
  },
  {
    "path": "app/src/sharedTest/kotlin/com/thomaskioko/tvmaniac/app/test/TestAppComponent.kt",
    "chars": 891,
    "preview": "package com.thomaskioko.tvmaniac.app.test\n\nimport android.app.Application\nimport androidx.datastore.core.DataStore\nimpor"
  },
  {
    "path": "app/src/sharedTest/kotlin/com/thomaskioko/tvmaniac/app/test/TvManiacTestApplication.kt",
    "chars": 1734,
    "preview": "package com.thomaskioko.tvmaniac.app.test\n\nimport android.app.Application\nimport androidx.work.testing.WorkManagerTestIn"
  },
  {
    "path": "app/src/sharedTest/kotlin/com/thomaskioko/tvmaniac/app/test/compose/TvManiacTestActivity.kt",
    "chars": 1595,
    "preview": "package com.thomaskioko.tvmaniac.app.test.compose\n\nimport android.os.Bundle\nimport androidx.activity.ComponentActivity\ni"
  },
  {
    "path": "app/src/sharedTest/kotlin/com/thomaskioko/tvmaniac/app/test/compose/flows/calendar/CalendarFlowTest.kt",
    "chars": 3566,
    "preview": "package com.thomaskioko.tvmaniac.app.test.compose.flows.calendar\n\nimport com.thomaskioko.tvmaniac.app.test.BaseAppFlowTe"
  },
  {
    "path": "app/src/sharedTest/kotlin/com/thomaskioko/tvmaniac/app/test/compose/flows/discover/DiscoverToSeasonDetailsFlowTest.kt",
    "chars": 1695,
    "preview": "package com.thomaskioko.tvmaniac.app.test.compose.flows.discover\n\nimport com.thomaskioko.tvmaniac.app.test.BaseAppFlowTe"
  },
  {
    "path": "app/src/sharedTest/kotlin/com/thomaskioko/tvmaniac/app/test/compose/flows/discover/DiscoverToShowDetailsFollowFlowTest.kt",
    "chars": 1137,
    "preview": "package com.thomaskioko.tvmaniac.app.test.compose.flows.discover\n\nimport com.thomaskioko.tvmaniac.app.test.BaseAppFlowTe"
  },
  {
    "path": "app/src/sharedTest/kotlin/com/thomaskioko/tvmaniac/app/test/compose/flows/library/LibraryFlowTest.kt",
    "chars": 2508,
    "preview": "package com.thomaskioko.tvmaniac.app.test.compose.flows.library\n\nimport com.thomaskioko.tvmaniac.app.test.BaseAppFlowTes"
  },
  {
    "path": "app/src/sharedTest/kotlin/com/thomaskioko/tvmaniac/app/test/compose/flows/search/SearchFlowTest.kt",
    "chars": 1562,
    "preview": "package com.thomaskioko.tvmaniac.app.test.compose.flows.search\n\nimport com.thomaskioko.tvmaniac.app.test.BaseAppFlowTest"
  },
  {
    "path": "app/src/sharedTest/kotlin/com/thomaskioko/tvmaniac/app/test/compose/flows/seasons/SeasonFlowTest.kt",
    "chars": 4088,
    "preview": "package com.thomaskioko.tvmaniac.app.test.compose.flows.seasons\n\nimport com.thomaskioko.tvmaniac.app.test.BaseAppFlowTes"
  },
  {
    "path": "app/src/sharedTest/kotlin/com/thomaskioko/tvmaniac/app/test/compose/flows/settings/SettingsFlowTest.kt",
    "chars": 1980,
    "preview": "package com.thomaskioko.tvmaniac.app.test.compose.flows.settings\n\nimport com.thomaskioko.tvmaniac.app.test.BaseAppFlowTe"
  },
  {
    "path": "app/src/sharedTest/kotlin/com/thomaskioko/tvmaniac/app/test/compose/flows/sheet/EpisodeSheetFlowTest.kt",
    "chars": 4120,
    "preview": "package com.thomaskioko.tvmaniac.app.test.compose.flows.sheet\n\nimport com.thomaskioko.tvmaniac.app.test.AppFlowScope\nimp"
  },
  {
    "path": "app/src/sharedTest/kotlin/com/thomaskioko/tvmaniac/app/test/compose/flows/showdetails/ShowDetailsFeaturesFlowTest.kt",
    "chars": 2076,
    "preview": "package com.thomaskioko.tvmaniac.app.test.compose.flows.showdetails\n\nimport com.thomaskioko.tvmaniac.app.test.BaseAppFlo"
  }
]

// ... and 1516 more files (download for full content)

About this extraction

This page contains the full source code of the thomaskioko/tv-maniac GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 1716 files (9.6 MB), approximately 2.7M tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!