├── iosApp
├── Configuration
│ └── Config.xcconfig
├── iosApp
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── AppIcon.appiconset
│ │ │ ├── AppIcon_iOS.png
│ │ │ └── Contents.json
│ │ └── AccentColor.colorset
│ │ │ └── Contents.json
│ ├── Preview Content
│ │ └── Preview Assets.xcassets
│ │ │ └── Contents.json
│ ├── iOSApp.swift
│ ├── Util
│ │ └── Extensions.swift
│ ├── ContentView.swift
│ └── Info.plist
└── iosApp.xcodeproj
│ └── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── composeApp
├── src
│ ├── androidMain
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── themes.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ ├── ic_launcher_round.webp
│ │ │ │ └── ic_launcher_foreground.webp
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ ├── ic_launcher_round.webp
│ │ │ │ └── ic_launcher_foreground.webp
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ ├── ic_launcher_round.webp
│ │ │ │ └── ic_launcher_foreground.webp
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ ├── ic_launcher_round.webp
│ │ │ │ └── ic_launcher_foreground.webp
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ ├── ic_launcher_round.webp
│ │ │ │ └── ic_launcher_foreground.webp
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ └── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ ├── kotlin
│ │ │ ├── network
│ │ │ │ ├── di
│ │ │ │ │ └── ApiModule.android.kt
│ │ │ │ └── NetworkClient.android.kt
│ │ │ ├── com
│ │ │ │ └── projects
│ │ │ │ │ └── cinetracker
│ │ │ │ │ ├── core
│ │ │ │ │ └── CoreApplication.kt
│ │ │ │ │ └── MainActivity.kt
│ │ │ ├── common
│ │ │ │ └── util
│ │ │ │ │ └── platform
│ │ │ │ │ ├── PlatformUtils.kt
│ │ │ │ │ ├── ScreenSizeInfo.android.kt
│ │ │ │ │ ├── DateUtils.kt
│ │ │ │ │ ├── StatusBarUpdate.android.kt
│ │ │ │ │ └── StringFormat.kt
│ │ │ ├── core
│ │ │ │ └── di
│ │ │ │ │ └── KoinInitializer.android.kt
│ │ │ └── database
│ │ │ │ └── di
│ │ │ │ └── DatabaseModule.android.kt
│ │ └── AndroidManifest.xml
│ ├── commonMain
│ │ ├── kotlin
│ │ │ ├── core
│ │ │ │ ├── di
│ │ │ │ │ ├── KoinInitializer.kt
│ │ │ │ │ └── modules
│ │ │ │ │ │ ├── InteractorModule.kt
│ │ │ │ │ │ └── ViewModelModule.kt
│ │ │ │ ├── ImageLoader.kt
│ │ │ │ └── LanguageManager.kt
│ │ │ ├── network
│ │ │ │ ├── NetworkClient.kt
│ │ │ │ ├── di
│ │ │ │ │ ├── ApiModule.kt
│ │ │ │ │ ├── ServiceModule.kt
│ │ │ │ │ └── RepositoryModule.kt
│ │ │ │ ├── models
│ │ │ │ │ ├── content
│ │ │ │ │ │ ├── common
│ │ │ │ │ │ │ ├── ContentGenre.kt
│ │ │ │ │ │ │ ├── ProductionCountry.kt
│ │ │ │ │ │ │ ├── VideosByIdResponse.kt
│ │ │ │ │ │ │ ├── ContentCreditsResponse.kt
│ │ │ │ │ │ │ ├── VideoResponse.kt
│ │ │ │ │ │ │ ├── WatchProvidersResponse.kt
│ │ │ │ │ │ │ └── ContentCastResponse.kt
│ │ │ │ │ │ ├── person
│ │ │ │ │ │ │ ├── PersonImagesResponse.kt
│ │ │ │ │ │ │ ├── PersonCreditsResponse.kt
│ │ │ │ │ │ │ ├── PersonProfileResponse.kt
│ │ │ │ │ │ │ └── CrewResponse.kt
│ │ │ │ │ │ └── search
│ │ │ │ │ │ │ └── ContentPagingResponse.kt
│ │ │ │ │ └── ApiError.kt
│ │ │ │ ├── util
│ │ │ │ │ ├── Parameters.kt
│ │ │ │ │ ├── Either.kt
│ │ │ │ │ └── ApiResult.kt
│ │ │ │ ├── repository
│ │ │ │ │ ├── person
│ │ │ │ │ │ ├── PersonRepository.kt
│ │ │ │ │ │ └── PersonRepositoryImpl.kt
│ │ │ │ │ ├── home
│ │ │ │ │ │ ├── HomeRepository.kt
│ │ │ │ │ │ └── HomeRepositoryImpl.kt
│ │ │ │ │ ├── search
│ │ │ │ │ │ ├── SearchRepository.kt
│ │ │ │ │ │ └── SearchRepositoryImpl.kt
│ │ │ │ │ ├── show
│ │ │ │ │ │ └── ShowRepository.kt
│ │ │ │ │ └── movie
│ │ │ │ │ │ └── MovieRepository.kt
│ │ │ │ └── services
│ │ │ │ │ ├── person
│ │ │ │ │ ├── PersonService.kt
│ │ │ │ │ └── PersonServiceImpl.kt
│ │ │ │ │ ├── home
│ │ │ │ │ └── HomeService.kt
│ │ │ │ │ ├── search
│ │ │ │ │ └── SearchService.kt
│ │ │ │ │ ├── show
│ │ │ │ │ └── ShowService.kt
│ │ │ │ │ └── movie
│ │ │ │ │ └── MovieService.kt
│ │ │ ├── database
│ │ │ │ ├── di
│ │ │ │ │ ├── DatabaseModule.kt
│ │ │ │ │ ├── DatabaseRepositoryModule.kt
│ │ │ │ │ └── DaoModule.kt
│ │ │ │ ├── model
│ │ │ │ │ ├── ListEntity.kt
│ │ │ │ │ └── ContentEntity.kt
│ │ │ │ ├── AppDatabase.kt
│ │ │ │ ├── dao
│ │ │ │ │ ├── ListEntityDao.kt
│ │ │ │ │ └── ContentEntityDao.kt
│ │ │ │ └── repository
│ │ │ │ │ └── DatabaseRepository.kt
│ │ │ ├── features
│ │ │ │ ├── watchlist
│ │ │ │ │ ├── ui
│ │ │ │ │ │ ├── model
│ │ │ │ │ │ │ ├── WatchlistItemAction.kt
│ │ │ │ │ │ │ └── DefaultLists.kt
│ │ │ │ │ │ ├── state
│ │ │ │ │ │ │ ├── WatchlistSnackbarState.kt
│ │ │ │ │ │ │ └── WatchlistState.kt
│ │ │ │ │ │ └── components
│ │ │ │ │ │ │ ├── WatchlistSortTypeItem.kt
│ │ │ │ │ │ │ ├── ListRemovePopupMenu.kt
│ │ │ │ │ │ │ └── WatchlistTabItem.kt
│ │ │ │ │ ├── WatchlistScreen.kt
│ │ │ │ │ └── events
│ │ │ │ │ │ └── WatchlistEvent.kt
│ │ │ │ ├── home
│ │ │ │ │ ├── HomeScreen.kt
│ │ │ │ │ ├── events
│ │ │ │ │ │ └── HomeEvent.kt
│ │ │ │ │ └── ui
│ │ │ │ │ │ ├── state
│ │ │ │ │ │ └── HomeState.kt
│ │ │ │ │ │ └── components
│ │ │ │ │ │ └── carousel
│ │ │ │ │ │ └── ComingSoonContainer.kt
│ │ │ │ ├── browse
│ │ │ │ │ ├── BrowseScreen.kt
│ │ │ │ │ ├── events
│ │ │ │ │ │ └── BrowseEvent.kt
│ │ │ │ │ ├── ui
│ │ │ │ │ │ └── components
│ │ │ │ │ │ │ └── MediaTypeTabItem.kt
│ │ │ │ │ └── domain
│ │ │ │ │ │ └── BrowseInteractor.kt
│ │ │ │ ├── search
│ │ │ │ │ ├── SearchScreen.kt
│ │ │ │ │ ├── events
│ │ │ │ │ │ └── SearchEvent.kt
│ │ │ │ │ ├── domain
│ │ │ │ │ │ └── SearchInteractor.kt
│ │ │ │ │ └── ui
│ │ │ │ │ │ └── components
│ │ │ │ │ │ └── SearchTypeFilterItem.kt
│ │ │ │ └── details
│ │ │ │ │ ├── state
│ │ │ │ │ ├── DetailsSnackbarState.kt
│ │ │ │ │ └── DetailsState.kt
│ │ │ │ │ ├── events
│ │ │ │ │ └── DetailsEvents.kt
│ │ │ │ │ ├── DetailsScreen.kt
│ │ │ │ │ ├── util
│ │ │ │ │ └── Extensions.kt
│ │ │ │ │ └── ui
│ │ │ │ │ └── components
│ │ │ │ │ └── moreoptions
│ │ │ │ │ ├── MoreOptionsTabItem.kt
│ │ │ │ │ └── MoreOptionsTab.kt
│ │ │ ├── common
│ │ │ │ ├── util
│ │ │ │ │ ├── platform
│ │ │ │ │ │ ├── StringFormat.kt
│ │ │ │ │ │ ├── PlatformUtils.kt
│ │ │ │ │ │ ├── StatusBarUpdate.kt
│ │ │ │ │ │ ├── DateUtils.kt
│ │ │ │ │ │ └── ScreenSizeInfo.kt
│ │ │ │ │ ├── ConversionUtil.kt
│ │ │ │ │ ├── NestedScrollConnection.kt
│ │ │ │ │ ├── CardsUtil.kt
│ │ │ │ │ └── Constants.kt
│ │ │ │ ├── ui
│ │ │ │ │ ├── screen
│ │ │ │ │ │ └── ErrorScreen.kt
│ │ │ │ │ ├── components
│ │ │ │ │ │ ├── tab
│ │ │ │ │ │ │ └── TabItem.kt
│ │ │ │ │ │ ├── popup
│ │ │ │ │ │ │ ├── PopupMenuItem.kt
│ │ │ │ │ │ │ └── GenericPopupMenu.kt
│ │ │ │ │ │ ├── ClassicLoadingIndicator.kt
│ │ │ │ │ │ ├── SystemUtil.kt
│ │ │ │ │ │ ├── button
│ │ │ │ │ │ │ ├── SimpleButton.kt
│ │ │ │ │ │ │ ├── GenericButton.kt
│ │ │ │ │ │ │ └── SortIconButton.kt
│ │ │ │ │ │ ├── NetworkImage.kt
│ │ │ │ │ │ ├── ClassicGradientBrush.kt
│ │ │ │ │ │ ├── RatingComponent.kt
│ │ │ │ │ │ ├── card
│ │ │ │ │ │ │ ├── MediaTypeTag.kt
│ │ │ │ │ │ │ ├── ImageContentCard.kt
│ │ │ │ │ │ │ └── PersonImages.kt
│ │ │ │ │ │ ├── bottomsheet
│ │ │ │ │ │ │ ├── SortBottomSheetComponents.kt
│ │ │ │ │ │ │ └── ModalComponents.kt
│ │ │ │ │ │ └── PlaceholderView.kt
│ │ │ │ │ └── theme
│ │ │ │ │ │ ├── Color.kt
│ │ │ │ │ │ ├── Shape.kt
│ │ │ │ │ │ └── Theme.kt
│ │ │ │ └── domain
│ │ │ │ │ └── models
│ │ │ │ │ ├── content
│ │ │ │ │ ├── BaseMediaContent.kt
│ │ │ │ │ ├── Videos.kt
│ │ │ │ │ ├── StreamProvider.kt
│ │ │ │ │ └── ContentCast.kt
│ │ │ │ │ ├── list
│ │ │ │ │ └── ListItem.kt
│ │ │ │ │ ├── util
│ │ │ │ │ ├── ContentListType.kt
│ │ │ │ │ ├── SnackbarState.kt
│ │ │ │ │ ├── MediaType.kt
│ │ │ │ │ └── DataLoadStatus.kt
│ │ │ │ │ └── person
│ │ │ │ │ ├── PersonImage.kt
│ │ │ │ │ └── PersonDetails.kt
│ │ │ └── navigation
│ │ │ │ ├── Screen.kt
│ │ │ │ ├── ScreenUI.kt
│ │ │ │ ├── screens
│ │ │ │ ├── ErrorScreenUI.kt
│ │ │ │ ├── BrowseScreenUI.kt
│ │ │ │ ├── SearchScreenUI.kt
│ │ │ │ ├── WatchlistScreenUI.kt
│ │ │ │ ├── DetailsScreenUI.kt
│ │ │ │ └── HomeScreenUI.kt
│ │ │ │ ├── MainNavGraph.kt
│ │ │ │ └── components
│ │ │ │ └── MainNavBarItem.kt
│ │ └── composeResources
│ │ │ └── drawable
│ │ │ ├── cinetracker_name_logo.webp
│ │ │ ├── ic_check.xml
│ │ │ ├── ic_watchlist_add_list.xml
│ │ │ ├── ic_chevron_right.xml
│ │ │ ├── ic_watchlist.xml
│ │ │ ├── ic_sort.xml
│ │ │ ├── ic_back_arrow.xml
│ │ │ ├── ic_close.xml
│ │ │ ├── ic_star.xml
│ │ │ ├── ic_nav_watchlist.xml
│ │ │ ├── ic_nav_home.xml
│ │ │ ├── ic_more_options.xml
│ │ │ ├── ic_nav_browse.xml
│ │ │ ├── ic_play_video.xml
│ │ │ └── ic_nav_search.xml
│ └── iosMain
│ │ └── kotlin
│ │ ├── network
│ │ ├── di
│ │ │ └── ApiModule.ios.kt
│ │ └── NetworkClient.ios.kt
│ │ ├── common
│ │ └── util
│ │ │ └── platform
│ │ │ ├── StatusBarUpdate.ios.kt
│ │ │ ├── ScreenSizeInfo.ios.kt
│ │ │ ├── PlatformUtils.kt
│ │ │ ├── DateUtils.kt
│ │ │ └── StringFormat.kt
│ │ ├── MainViewController.kt
│ │ └── core
│ │ └── di
│ │ └── KoinInitializer.ios.kt
├── google-services.json
└── proguard-rules.pro
├── .kotlin
└── metadata
│ ├── kotlinTransformedMetadataLibraries
│ ├── org.jetbrains.skiko-skiko-0.8.4-iosMain-1T2ZCw.klib
│ ├── org.jetbrains.compose.ui-ui-1.6.10-skikoMain-Eh674w.klib
│ ├── org.jetbrains.compose.ui-ui-1.6.10-uikitMain-BVAqnw.klib
│ ├── org.jetbrains.skiko-skiko-0.8.4-commonMain-DbI_Jg.klib
│ ├── org.jetbrains.skiko-skiko-0.8.4-darwinMain-1T2ZCw.klib
│ ├── org.jetbrains.skiko-skiko-0.8.4-nativeJsMain-DbI_Jg.klib
│ ├── org.jetbrains.skiko-skiko-0.8.4-nativeMain-DbI_Jg.klib
│ ├── org.jetbrains.compose.ui-ui-1.6.10-commonMain-Eh674w.klib
│ ├── org.jetbrains.compose.ui-ui-1.6.10-darwinMain-BVAqnw.klib
│ ├── org.jetbrains.compose.ui-ui-1.6.10-nativeMain-BVAqnw.klib
│ ├── org.jetbrains.compose.ui-ui-unit-1.6.10-jbMain-vwDMdg.klib
│ ├── org.jetbrains.compose.ui-ui-1.6.10-jsNativeMain-Eh674w.klib
│ ├── org.jetbrains.compose.ui-ui-text-1.6.10-skikoMain-aUvkxg.klib
│ ├── org.jetbrains.compose.ui-ui-util-1.6.10-uikitMain-4Hpl6Q.klib
│ ├── org.jetbrains.kotlinx-atomicfu-0.23.2-commonMain-yBS35w.klib
│ ├── org.jetbrains.kotlinx-atomicfu-0.23.2-nativeMain-yBS35w.klib
│ ├── org.jetbrains.compose.runtime-runtime-1.6.10-jbMain-CVJWAg.klib
│ ├── org.jetbrains.compose.ui-ui-text-1.6.10-commonMain-aUvkxg.klib
│ ├── org.jetbrains.compose.ui-ui-text-1.6.10-darwinMain-DK5x5Q.klib
│ ├── org.jetbrains.compose.ui-ui-text-1.6.10-nativeMain-DK5x5Q.klib
│ ├── org.jetbrains.compose.ui-ui-uikit-1.6.10-uikitMain-DC3XFw.klib
│ ├── org.jetbrains.compose.ui-ui-unit-1.6.10-commonMain-vwDMdg.klib
│ ├── org.jetbrains.compose.ui-ui-util-1.6.10-commonMain-LLOBPg.klib
│ ├── org.jetbrains.kotlin-kotlin-stdlib-2.0.0-commonMain-2bbUHA.klib
│ ├── org.jetbrains.compose.runtime-runtime-1.6.10-uikitMain-LSh9lw.klib
│ ├── org.jetbrains.compose.ui-ui-geometry-1.6.10-commonMain-zDj2GQ.klib
│ ├── org.jetbrains.compose.ui-ui-graphics-1.6.10-commonMain-jqr5iw.klib
│ ├── org.jetbrains.compose.ui-ui-graphics-1.6.10-nativeMain-M9RlEw.klib
│ ├── org.jetbrains.compose.ui-ui-graphics-1.6.10-skikoMain-jqr5iw.klib
│ ├── org.jetbrains.compose.ui-ui-text-1.6.10-jsNativeMain-aUvkxg.klib
│ ├── org.jetbrains.compose.ui-ui-unit-1.6.10-jsNativeMain-vwDMdg.klib
│ ├── org.jetbrains.compose.material-material-1.6.10-skikoMain-tGo7Ag.klib
│ ├── org.jetbrains.compose.runtime-runtime-1.6.10-commonMain-CVJWAg.klib
│ ├── org.jetbrains.compose.runtime-runtime-1.6.10-nativeMain-CVJWAg.klib
│ ├── org.jetbrains.compose.ui-ui-graphics-1.6.10-jsNativeMain-jqr5iw.klib
│ ├── org.jetbrains.compose.animation-animation-1.6.10-commonMain-5jNXZw.klib
│ ├── org.jetbrains.compose.animation-animation-1.6.10-nativeMain-tpXTFg.klib
│ ├── org.jetbrains.compose.material-material-1.6.10-commonMain-tGo7Ag.klib
│ ├── org.jetbrains.compose.material-material-1.6.10-jsNativeMain-tGo7Ag.klib
│ ├── org.jetbrains.compose.material-material-1.6.10-nativeMain-33WlwA.klib
│ ├── org.jetbrains.compose.runtime-runtime-1.6.10-jsNativeMain-CVJWAg.klib
│ ├── org.jetbrains.compose.animation-animation-1.6.10-jsNativeMain-5jNXZw.klib
│ ├── org.jetbrains.compose.animation-animation-core-1.6.10-jbMain-jNz1Aw.klib
│ ├── org.jetbrains.compose.foundation-foundation-1.6.10-commonMain-dXXsCQ.klib
│ ├── org.jetbrains.compose.foundation-foundation-1.6.10-darwinMain-aASdXg.klib
│ ├── org.jetbrains.compose.foundation-foundation-1.6.10-nativeMain-aASdXg.klib
│ ├── org.jetbrains.compose.foundation-foundation-1.6.10-skikoMain-dXXsCQ.klib
│ ├── org.jetbrains.compose.foundation-foundation-1.6.10-uikitMain-aASdXg.klib
│ ├── org.jetbrains.compose.animation-animation-core-1.6.10-commonMain-jNz1Aw.klib
│ ├── org.jetbrains.compose.animation-animation-core-1.6.10-uikitMain-2J6wbg.klib
│ ├── org.jetbrains.compose.foundation-foundation-1.6.10-jsNativeMain-dXXsCQ.klib
│ ├── org.jetbrains.compose.material-material-ripple-1.6.10-commonMain-8kHg7A.klib
│ ├── org.jetbrains.compose.material-material-ripple-1.6.10-nativeMain-zsMeyQ.klib
│ ├── org.jetbrains.compose.runtime-runtime-saveable-1.6.10-commonMain-pCPplQ.klib
│ ├── org.jetbrains.kotlinx-kotlinx-coroutines-core-1.8.0-commonMain-UxhG-g.klib
│ ├── org.jetbrains.kotlinx-kotlinx-coroutines-core-1.8.0-nativeMain-UxhG-g.klib
│ ├── org.jetbrains.androidx.lifecycle-lifecycle-common-2.8.0-commonMain-_oGBew.klib
│ ├── org.jetbrains.androidx.lifecycle-lifecycle-common-2.8.0-nonJvmMain-_oGBew.klib
│ ├── org.jetbrains.compose.animation-animation-core-1.6.10-jsNativeMain-jNz1Aw.klib
│ ├── org.jetbrains.compose.collection-internal-collection-1.6.10-jbMain-hcu3Ug.klib
│ ├── org.jetbrains.compose.ui-ui-graphics-1.6.10-skikoExcludingWebMain-jqr5iw.klib
│ ├── org.jetbrains.kotlinx-kotlinx-coroutines-core-1.8.0-concurrentMain-UxhG-g.klib
│ ├── org.jetbrains.androidx.lifecycle-lifecycle-runtime-2.8.0-commonMain-Cd-IGw.klib
│ ├── org.jetbrains.androidx.lifecycle-lifecycle-runtime-2.8.0-nativeMain-Cd-IGw.klib
│ ├── org.jetbrains.androidx.lifecycle-lifecycle-runtime-2.8.0-nonJvmMain-Cd-IGw.klib
│ ├── org.jetbrains.androidx.lifecycle-lifecycle-viewmodel-2.8.0-commonMain-ydSu5Q.klib
│ ├── org.jetbrains.androidx.lifecycle-lifecycle-viewmodel-2.8.0-nativeMain-ydSu5Q.klib
│ ├── org.jetbrains.androidx.lifecycle-lifecycle-viewmodel-2.8.0-nonJvmMain-ydSu5Q.klib
│ ├── org.jetbrains.compose.components-components-resources-1.6.10-iosMain-mlvQUA.klib
│ ├── org.jetbrains.compose.foundation-foundation-layout-1.6.10-commonMain-89e7lw.klib
│ ├── org.jetbrains.compose.foundation-foundation-layout-1.6.10-skikoMain-89e7lw.klib
│ ├── org.jetbrains.compose.foundation-foundation-layout-1.6.10-uikitMain-BKR0pA.klib
│ ├── org.jetbrains.compose.material-material-icons-core-1.6.10-commonMain-XjyzjQ.klib
│ ├── org.jetbrains.kotlinx-kotlinx-coroutines-core-1.8.0-nativeDarwinMain-sy5nKg.klib
│ ├── org.jetbrains.compose.annotation-internal-annotation-1.6.10-commonMain-cNNKSA.klib
│ ├── org.jetbrains.compose.annotation-internal-annotation-1.6.10-nonJvmMain-cNNKSA.klib
│ ├── org.jetbrains.compose.collection-internal-collection-1.6.10-commonMain-hcu3Ug.klib
│ ├── org.jetbrains.compose.components-components-resources-1.6.10-commonMain-44UCqg.klib
│ ├── org.jetbrains.compose.components-components-resources-1.6.10-nativeMain-mlvQUA.klib
│ ├── org.jetbrains.compose.components-components-resources-1.6.10-skikoMain-44UCqg.klib
│ ├── org.jetbrains.compose.foundation-foundation-layout-1.6.10-jsNativeMain-89e7lw.klib
│ ├── org.jetbrains.compose.collection-internal-collection-1.6.10-jsNativeMain-hcu3Ug.klib
│ ├── org.jetbrains.compose.components-components-resources-1.6.10-blockingMain-44UCqg.klib
│ ├── org.jetbrains.androidx.lifecycle-lifecycle-runtime-compose-2.8.0-commonMain-mvP4Vw.klib
│ └── org.jetbrains.compose.components-components-ui-tooling-preview-1.6.10-commonMain--i3iSw.klib
│ └── kotlinTransformedCInteropMetadataLibraries
│ ├── .composeApp-appleMain.cinteropLibraries
│ ├── .composeApp-appleTest.cinteropLibraries
│ ├── .composeApp-iosMain.cinteropLibraries
│ ├── .composeApp-iosTest.cinteropLibraries
│ ├── .composeApp-nativeMain.cinteropLibraries
│ └── .composeApp-nativeTest.cinteropLibraries
├── gradle
└── wrapper
│ └── gradle-wrapper.properties
├── gradle.properties
├── .fleet
└── receipt.json
├── settings.gradle.kts
└── .gitignore
/iosApp/Configuration/Config.xcconfig:
--------------------------------------------------------------------------------
1 | TEAM_ID=
2 | BUNDLE_ID=com.projects.CineTracker
3 | APP_NAME=CineTracker
4 |
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | CineTracker
3 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/core/di/KoinInitializer.kt:
--------------------------------------------------------------------------------
1 | package core.di
2 |
3 | expect class KoinInitializer {
4 | fun init()
5 | }
6 |
--------------------------------------------------------------------------------
/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/NetworkClient.kt:
--------------------------------------------------------------------------------
1 | package network
2 |
3 | import io.ktor.client.HttpClient
4 |
5 | expect val client: HttpClient
6 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/di/ApiModule.kt:
--------------------------------------------------------------------------------
1 | package network.di
2 |
3 | import org.koin.core.module.Module
4 |
5 | expect val apiModule: Module
6 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/database/di/DatabaseModule.kt:
--------------------------------------------------------------------------------
1 | package database.di
2 |
3 | import org.koin.core.module.Module
4 |
5 | expect fun databaseModule(): Module
6 |
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/AppIcon_iOS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/AppIcon_iOS.png
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/iosApp/iosApp/iOSApp.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | @main
4 | struct iOSApp: App {
5 | var body: some Scene {
6 | WindowGroup {
7 | ContentView()
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/features/watchlist/ui/model/WatchlistItemAction.kt:
--------------------------------------------------------------------------------
1 | package features.watchlist.ui.model
2 |
3 | enum class WatchlistItemAction {
4 | ITEM_REMOVED,
5 | ITEM_MOVED,
6 | }
7 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/composeResources/drawable/cinetracker_name_logo.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/composeApp/src/commonMain/composeResources/drawable/cinetracker_name_logo.webp
--------------------------------------------------------------------------------
/iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/util/platform/StringFormat.kt:
--------------------------------------------------------------------------------
1 | package common.util.platform
2 |
3 | expect object StringFormat {
4 | fun formatRating(number: Double): String
5 | fun Long.toFormattedCurrency(): String
6 | }
7 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/features/home/HomeScreen.kt:
--------------------------------------------------------------------------------
1 | package features.home
2 |
3 | import navigation.Screen
4 |
5 | object HomeScreen : Screen {
6 | private const val HOME_ROUTE = "home"
7 | override fun route(): String = HOME_ROUTE
8 | }
9 |
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.skiko-skiko-0.8.4-iosMain-1T2ZCw.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.skiko-skiko-0.8.4-iosMain-1T2ZCw.klib
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/ui/screen/ErrorScreen.kt:
--------------------------------------------------------------------------------
1 | package common.ui.screen
2 |
3 | import navigation.Screen
4 |
5 | object ErrorScreen : Screen {
6 | private const val ERROR_ROUTE = "error"
7 | override fun route(): String = ERROR_ROUTE
8 | }
9 |
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-1.6.10-skikoMain-Eh674w.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-1.6.10-skikoMain-Eh674w.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-1.6.10-uikitMain-BVAqnw.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-1.6.10-uikitMain-BVAqnw.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.skiko-skiko-0.8.4-commonMain-DbI_Jg.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.skiko-skiko-0.8.4-commonMain-DbI_Jg.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.skiko-skiko-0.8.4-darwinMain-1T2ZCw.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.skiko-skiko-0.8.4-darwinMain-1T2ZCw.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.skiko-skiko-0.8.4-nativeJsMain-DbI_Jg.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.skiko-skiko-0.8.4-nativeJsMain-DbI_Jg.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.skiko-skiko-0.8.4-nativeMain-DbI_Jg.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.skiko-skiko-0.8.4-nativeMain-DbI_Jg.klib
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/features/browse/BrowseScreen.kt:
--------------------------------------------------------------------------------
1 | package features.browse
2 |
3 | import navigation.Screen
4 |
5 | object BrowseScreen : Screen {
6 | private const val BROWSE_ROUTE = "browse"
7 | override fun route(): String = BROWSE_ROUTE
8 | }
9 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/features/home/events/HomeEvent.kt:
--------------------------------------------------------------------------------
1 | package features.home.events
2 |
3 | sealed class HomeEvent {
4 | data object LoadHome : HomeEvent()
5 | data object ReloadWatchlist : HomeEvent()
6 | data object OnError : HomeEvent()
7 | }
8 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/features/search/SearchScreen.kt:
--------------------------------------------------------------------------------
1 | package features.search
2 |
3 | import navigation.Screen
4 |
5 | object SearchScreen : Screen {
6 | private const val SEARCH_ROUTE = "search"
7 | override fun route(): String = SEARCH_ROUTE
8 | }
9 |
--------------------------------------------------------------------------------
/composeApp/src/iosMain/kotlin/network/di/ApiModule.ios.kt:
--------------------------------------------------------------------------------
1 | package network.di
2 |
3 | import io.ktor.client.HttpClient
4 | import network.client
5 | import org.koin.dsl.module
6 |
7 | actual val apiModule = module {
8 | single { client }
9 | }
10 |
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-1.6.10-commonMain-Eh674w.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-1.6.10-commonMain-Eh674w.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-1.6.10-darwinMain-BVAqnw.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-1.6.10-darwinMain-BVAqnw.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-1.6.10-nativeMain-BVAqnw.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-1.6.10-nativeMain-BVAqnw.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-unit-1.6.10-jbMain-vwDMdg.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-unit-1.6.10-jbMain-vwDMdg.klib
--------------------------------------------------------------------------------
/composeApp/src/androidMain/kotlin/network/di/ApiModule.android.kt:
--------------------------------------------------------------------------------
1 | package network.di
2 |
3 | import io.ktor.client.HttpClient
4 | import network.client
5 | import org.koin.dsl.module
6 |
7 | actual val apiModule = module {
8 | single { client }
9 | }
10 |
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFFA9F26
4 | #000000
5 | #FF191A1D
6 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/util/platform/PlatformUtils.kt:
--------------------------------------------------------------------------------
1 | package common.util.platform
2 |
3 | expect object PlatformUtils {
4 | val isIOS: Boolean
5 | fun getUserLanguage(): String
6 | fun getUserCountry(): String
7 | fun getLocale(): String
8 | }
9 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/navigation/Screen.kt:
--------------------------------------------------------------------------------
1 | package navigation
2 |
3 | import androidx.navigation.NamedNavArgument
4 |
5 | interface Screen {
6 | fun route(): String
7 | val arguments: List
8 | get() = emptyList()
9 | }
10 |
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-1.6.10-jsNativeMain-Eh674w.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-1.6.10-jsNativeMain-Eh674w.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-text-1.6.10-skikoMain-aUvkxg.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-text-1.6.10-skikoMain-aUvkxg.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-util-1.6.10-uikitMain-4Hpl6Q.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-util-1.6.10-uikitMain-4Hpl6Q.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-atomicfu-0.23.2-commonMain-yBS35w.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-atomicfu-0.23.2-commonMain-yBS35w.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-atomicfu-0.23.2-nativeMain-yBS35w.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-atomicfu-0.23.2-nativeMain-yBS35w.klib
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/navigation/ScreenUI.kt:
--------------------------------------------------------------------------------
1 | package navigation
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.navigation.NavController
5 |
6 | interface ScreenUI {
7 | @Composable
8 | fun UI(navController: NavController)
9 | }
10 |
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.runtime-runtime-1.6.10-jbMain-CVJWAg.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.runtime-runtime-1.6.10-jbMain-CVJWAg.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-text-1.6.10-commonMain-aUvkxg.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-text-1.6.10-commonMain-aUvkxg.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-text-1.6.10-darwinMain-DK5x5Q.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-text-1.6.10-darwinMain-DK5x5Q.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-text-1.6.10-nativeMain-DK5x5Q.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-text-1.6.10-nativeMain-DK5x5Q.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-uikit-1.6.10-uikitMain-DC3XFw.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-uikit-1.6.10-uikitMain-DC3XFw.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-unit-1.6.10-commonMain-vwDMdg.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-unit-1.6.10-commonMain-vwDMdg.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-util-1.6.10-commonMain-LLOBPg.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-util-1.6.10-commonMain-LLOBPg.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlin-kotlin-stdlib-2.0.0-commonMain-2bbUHA.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlin-kotlin-stdlib-2.0.0-commonMain-2bbUHA.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.runtime-runtime-1.6.10-uikitMain-LSh9lw.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.runtime-runtime-1.6.10-uikitMain-LSh9lw.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-geometry-1.6.10-commonMain-zDj2GQ.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-geometry-1.6.10-commonMain-zDj2GQ.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-graphics-1.6.10-commonMain-jqr5iw.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-graphics-1.6.10-commonMain-jqr5iw.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-graphics-1.6.10-nativeMain-M9RlEw.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-graphics-1.6.10-nativeMain-M9RlEw.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-graphics-1.6.10-skikoMain-jqr5iw.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-graphics-1.6.10-skikoMain-jqr5iw.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-text-1.6.10-jsNativeMain-aUvkxg.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-text-1.6.10-jsNativeMain-aUvkxg.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-unit-1.6.10-jsNativeMain-vwDMdg.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-unit-1.6.10-jsNativeMain-vwDMdg.klib
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/features/watchlist/WatchlistScreen.kt:
--------------------------------------------------------------------------------
1 | package features.watchlist
2 |
3 | import navigation.Screen
4 |
5 | object WatchlistScreen : Screen {
6 | private const val WATCHLIST_ROUTE = "watchlist"
7 | override fun route(): String = WATCHLIST_ROUTE
8 | }
9 |
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.material-material-1.6.10-skikoMain-tGo7Ag.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.material-material-1.6.10-skikoMain-tGo7Ag.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.runtime-runtime-1.6.10-commonMain-CVJWAg.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.runtime-runtime-1.6.10-commonMain-CVJWAg.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.runtime-runtime-1.6.10-nativeMain-CVJWAg.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.runtime-runtime-1.6.10-nativeMain-CVJWAg.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-graphics-1.6.10-jsNativeMain-jqr5iw.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-graphics-1.6.10-jsNativeMain-jqr5iw.klib
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/models/content/common/ContentGenre.kt:
--------------------------------------------------------------------------------
1 | package network.models.content.common
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class ContentGenre(
7 | val id: Int? = null,
8 | val name: String? = null,
9 | )
10 |
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.animation-animation-1.6.10-commonMain-5jNXZw.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.animation-animation-1.6.10-commonMain-5jNXZw.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.animation-animation-1.6.10-nativeMain-tpXTFg.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.animation-animation-1.6.10-nativeMain-tpXTFg.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.material-material-1.6.10-commonMain-tGo7Ag.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.material-material-1.6.10-commonMain-tGo7Ag.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.material-material-1.6.10-jsNativeMain-tGo7Ag.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.material-material-1.6.10-jsNativeMain-tGo7Ag.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.material-material-1.6.10-nativeMain-33WlwA.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.material-material-1.6.10-nativeMain-33WlwA.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.runtime-runtime-1.6.10-jsNativeMain-CVJWAg.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.runtime-runtime-1.6.10-jsNativeMain-CVJWAg.klib
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/ui/components/tab/TabItem.kt:
--------------------------------------------------------------------------------
1 | package common.ui.components.tab
2 |
3 | import org.jetbrains.compose.resources.StringResource
4 |
5 | interface TabItem {
6 | val tabResId: StringResource?
7 | val tabName: String?
8 | var tabIndex: Int
9 | }
10 |
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.animation-animation-1.6.10-jsNativeMain-5jNXZw.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.animation-animation-1.6.10-jsNativeMain-5jNXZw.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.animation-animation-core-1.6.10-jbMain-jNz1Aw.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.animation-animation-core-1.6.10-jbMain-jNz1Aw.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.foundation-foundation-1.6.10-commonMain-dXXsCQ.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.foundation-foundation-1.6.10-commonMain-dXXsCQ.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.foundation-foundation-1.6.10-darwinMain-aASdXg.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.foundation-foundation-1.6.10-darwinMain-aASdXg.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.foundation-foundation-1.6.10-nativeMain-aASdXg.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.foundation-foundation-1.6.10-nativeMain-aASdXg.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.foundation-foundation-1.6.10-skikoMain-dXXsCQ.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.foundation-foundation-1.6.10-skikoMain-dXXsCQ.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.foundation-foundation-1.6.10-uikitMain-aASdXg.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.foundation-foundation-1.6.10-uikitMain-aASdXg.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.animation-animation-core-1.6.10-commonMain-jNz1Aw.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.animation-animation-core-1.6.10-commonMain-jNz1Aw.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.animation-animation-core-1.6.10-uikitMain-2J6wbg.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.animation-animation-core-1.6.10-uikitMain-2J6wbg.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.foundation-foundation-1.6.10-jsNativeMain-dXXsCQ.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.foundation-foundation-1.6.10-jsNativeMain-dXXsCQ.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.material-material-ripple-1.6.10-commonMain-8kHg7A.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.material-material-ripple-1.6.10-commonMain-8kHg7A.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.material-material-ripple-1.6.10-nativeMain-zsMeyQ.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.material-material-ripple-1.6.10-nativeMain-zsMeyQ.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.runtime-runtime-saveable-1.6.10-commonMain-pCPplQ.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.runtime-runtime-saveable-1.6.10-commonMain-pCPplQ.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-coroutines-core-1.8.0-commonMain-UxhG-g.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-coroutines-core-1.8.0-commonMain-UxhG-g.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-coroutines-core-1.8.0-nativeMain-UxhG-g.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-coroutines-core-1.8.0-nativeMain-UxhG-g.klib
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.lifecycle-lifecycle-common-2.8.0-commonMain-_oGBew.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.lifecycle-lifecycle-common-2.8.0-commonMain-_oGBew.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.lifecycle-lifecycle-common-2.8.0-nonJvmMain-_oGBew.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.lifecycle-lifecycle-common-2.8.0-nonJvmMain-_oGBew.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.animation-animation-core-1.6.10-jsNativeMain-jNz1Aw.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.animation-animation-core-1.6.10-jsNativeMain-jNz1Aw.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.collection-internal-collection-1.6.10-jbMain-hcu3Ug.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.collection-internal-collection-1.6.10-jbMain-hcu3Ug.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-graphics-1.6.10-skikoExcludingWebMain-jqr5iw.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.ui-ui-graphics-1.6.10-skikoExcludingWebMain-jqr5iw.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-coroutines-core-1.8.0-concurrentMain-UxhG-g.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-coroutines-core-1.8.0-concurrentMain-UxhG-g.klib
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/ui/components/popup/PopupMenuItem.kt:
--------------------------------------------------------------------------------
1 | package common.ui.components.popup
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | data class PopupMenuItem(
6 | val title: String,
7 | val textColor: Color = Color.White,
8 | val onClick: () -> Unit,
9 | )
10 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/models/content/common/ProductionCountry.kt:
--------------------------------------------------------------------------------
1 | package network.models.content.common
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class ProductionCountry(
7 | val iso_3166_1: String? = null,
8 | val name: String? = null,
9 | )
10 |
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.lifecycle-lifecycle-runtime-2.8.0-commonMain-Cd-IGw.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.lifecycle-lifecycle-runtime-2.8.0-commonMain-Cd-IGw.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.lifecycle-lifecycle-runtime-2.8.0-nativeMain-Cd-IGw.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.lifecycle-lifecycle-runtime-2.8.0-nativeMain-Cd-IGw.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.lifecycle-lifecycle-runtime-2.8.0-nonJvmMain-Cd-IGw.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.lifecycle-lifecycle-runtime-2.8.0-nonJvmMain-Cd-IGw.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.lifecycle-lifecycle-viewmodel-2.8.0-commonMain-ydSu5Q.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.lifecycle-lifecycle-viewmodel-2.8.0-commonMain-ydSu5Q.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.lifecycle-lifecycle-viewmodel-2.8.0-nativeMain-ydSu5Q.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.lifecycle-lifecycle-viewmodel-2.8.0-nativeMain-ydSu5Q.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.lifecycle-lifecycle-viewmodel-2.8.0-nonJvmMain-ydSu5Q.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.lifecycle-lifecycle-viewmodel-2.8.0-nonJvmMain-ydSu5Q.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.components-components-resources-1.6.10-iosMain-mlvQUA.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.components-components-resources-1.6.10-iosMain-mlvQUA.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.foundation-foundation-layout-1.6.10-commonMain-89e7lw.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.foundation-foundation-layout-1.6.10-commonMain-89e7lw.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.foundation-foundation-layout-1.6.10-skikoMain-89e7lw.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.foundation-foundation-layout-1.6.10-skikoMain-89e7lw.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.foundation-foundation-layout-1.6.10-uikitMain-BKR0pA.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.foundation-foundation-layout-1.6.10-uikitMain-BKR0pA.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.material-material-icons-core-1.6.10-commonMain-XjyzjQ.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.material-material-icons-core-1.6.10-commonMain-XjyzjQ.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-coroutines-core-1.8.0-nativeDarwinMain-sy5nKg.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-coroutines-core-1.8.0-nativeDarwinMain-sy5nKg.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.annotation-internal-annotation-1.6.10-commonMain-cNNKSA.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.annotation-internal-annotation-1.6.10-commonMain-cNNKSA.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.annotation-internal-annotation-1.6.10-nonJvmMain-cNNKSA.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.annotation-internal-annotation-1.6.10-nonJvmMain-cNNKSA.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.collection-internal-collection-1.6.10-commonMain-hcu3Ug.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.collection-internal-collection-1.6.10-commonMain-hcu3Ug.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.components-components-resources-1.6.10-commonMain-44UCqg.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.components-components-resources-1.6.10-commonMain-44UCqg.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.components-components-resources-1.6.10-nativeMain-mlvQUA.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.components-components-resources-1.6.10-nativeMain-mlvQUA.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.components-components-resources-1.6.10-skikoMain-44UCqg.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.components-components-resources-1.6.10-skikoMain-44UCqg.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.foundation-foundation-layout-1.6.10-jsNativeMain-89e7lw.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.foundation-foundation-layout-1.6.10-jsNativeMain-89e7lw.klib
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/models/content/common/VideosByIdResponse.kt:
--------------------------------------------------------------------------------
1 | package network.models.content.common
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class VideosByIdResponse(
7 | val id: Int? = null,
8 | val results: List? = null,
9 | )
10 |
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.collection-internal-collection-1.6.10-jsNativeMain-hcu3Ug.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.collection-internal-collection-1.6.10-jsNativeMain-hcu3Ug.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.components-components-resources-1.6.10-blockingMain-44UCqg.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.components-components-resources-1.6.10-blockingMain-44UCqg.klib
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/util/platform/StatusBarUpdate.kt:
--------------------------------------------------------------------------------
1 | package common.util.platform
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.graphics.Color
5 | import common.ui.theme.MainBarGreyColor
6 |
7 | @Composable
8 | expect fun SetStatusBarColor(newColor: Color = MainBarGreyColor)
9 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/models/ApiError.kt:
--------------------------------------------------------------------------------
1 | package network.models
2 |
3 | import kotlinx.serialization.Contextual
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class ApiError(
8 | val code: String? = null,
9 | @Contextual val exception: Throwable? = null,
10 | )
11 |
--------------------------------------------------------------------------------
/iosApp/iosApp/Util/Extensions.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | extension Color {
4 | init(hex: UInt) {
5 | self.init(
6 | red: Double((hex >> 16) & 0xFF) / 255.0,
7 | green: Double((hex >> 8) & 0xFF) / 255.0,
8 | blue: Double(hex & 0xFF) / 255.0
9 | )
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.lifecycle-lifecycle-runtime-compose-2.8.0-commonMain-mvP4Vw.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.androidx.lifecycle-lifecycle-runtime-compose-2.8.0-commonMain-mvP4Vw.klib
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/database/model/ListEntity.kt:
--------------------------------------------------------------------------------
1 | package database.model
2 |
3 | import androidx.room.Entity
4 | import androidx.room.PrimaryKey
5 |
6 | @Entity(tableName = "list_entity")
7 | data class ListEntity(
8 | @PrimaryKey(autoGenerate = true) val listId: Int = 0,
9 | val listName: String,
10 | )
11 |
--------------------------------------------------------------------------------
/composeApp/src/iosMain/kotlin/common/util/platform/StatusBarUpdate.ios.kt:
--------------------------------------------------------------------------------
1 | package common.util.platform
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.graphics.Color
5 |
6 | @Composable
7 | actual fun SetStatusBarColor(newColor: Color) {
8 | // TODO status bar color update on IOS
9 | }
10 |
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/models/content/common/ContentCreditsResponse.kt:
--------------------------------------------------------------------------------
1 | package network.models.content.common
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class ContentCreditsResponse(
7 | val id: Int = 0,
8 | val cast: List? = emptyList(),
9 | )
10 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/models/content/person/PersonImagesResponse.kt:
--------------------------------------------------------------------------------
1 | package network.models.content.person
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class PersonImagesResponse(
7 | val id: Int? = null,
8 | val profiles: List? = null,
9 | )
10 |
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "AppIcon_iOS.png",
5 | "idiom" : "universal",
6 | "platform" : "ios",
7 | "size" : "1024x1024"
8 | }
9 | ],
10 | "info" : {
11 | "author" : "xcode",
12 | "version" : 1
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.components-components-ui-tooling-preview-1.6.10-commonMain--i3iSw.klib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gustavopeq/CineTracker-KMP/HEAD/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.compose.components-components-ui-tooling-preview-1.6.10-commonMain--i3iSw.klib
--------------------------------------------------------------------------------
/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/util/platform/DateUtils.kt:
--------------------------------------------------------------------------------
1 | package common.util.platform
2 |
3 | import common.util.Constants
4 |
5 | expect object DateUtils {
6 | fun getComingSoonDates(
7 | monthPeriod: Int = Constants.MONTH_PERIOD_COMING_SOON,
8 | ): Pair
9 | fun getCurrentTimeMillis(): Long
10 | }
11 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/core/ImageLoader.kt:
--------------------------------------------------------------------------------
1 | package core
2 |
3 | import coil3.ImageLoader
4 | import coil3.PlatformContext
5 | import coil3.request.crossfade
6 | import coil3.util.DebugLogger
7 |
8 | fun getAsyncImageLoader(context: PlatformContext) =
9 | ImageLoader.Builder(context).crossfade(true).logger(DebugLogger()).build()
10 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/domain/models/content/BaseMediaContent.kt:
--------------------------------------------------------------------------------
1 | package common.domain.models.content
2 |
3 | import common.domain.models.util.MediaType
4 |
5 | interface BaseMediaContent {
6 | val id: Int
7 | val title: String
8 | val overview: String
9 | val poster_path: String
10 | val mediaType: MediaType
11 | }
12 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/database/di/DatabaseRepositoryModule.kt:
--------------------------------------------------------------------------------
1 | package database.di
2 |
3 | import database.repository.DatabaseRepository
4 | import database.repository.DatabaseRepositoryImpl
5 | import org.koin.dsl.module
6 |
7 | val databaseRepositoryModule = module {
8 | single { DatabaseRepositoryImpl(get(), get()) }
9 | }
10 |
--------------------------------------------------------------------------------
/composeApp/src/androidMain/kotlin/com/projects/cinetracker/core/CoreApplication.kt:
--------------------------------------------------------------------------------
1 | package com.projects.cinetracker.core
2 |
3 | import android.app.Application
4 | import core.di.KoinInitializer
5 |
6 | class CoreApplication : Application() {
7 | override fun onCreate() {
8 | super.onCreate()
9 | KoinInitializer(this@CoreApplication).init()
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/features/details/state/DetailsSnackbarState.kt:
--------------------------------------------------------------------------------
1 | package features.details.state
2 |
3 | import common.domain.models.util.SnackbarState
4 | import features.watchlist.ui.model.DefaultLists
5 |
6 | data class DetailsSnackbarState(
7 | val listId: Int = DefaultLists.WATCHLIST.listId,
8 | val addedItem: Boolean = false,
9 | ) : SnackbarState()
10 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/composeResources/drawable/ic_check.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/composeResources/drawable/ic_watchlist_add_list.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/domain/models/list/ListItem.kt:
--------------------------------------------------------------------------------
1 | package common.domain.models.list
2 |
3 | import database.model.ListEntity
4 |
5 | data class ListItem(
6 | val id: Int,
7 | val name: String,
8 | )
9 |
10 | fun ListEntity.toListItem(): ListItem {
11 | return ListItem(
12 | id = this.listId,
13 | name = this.listName,
14 | )
15 | }
16 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/composeResources/drawable/ic_chevron_right.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/util/ConversionUtil.kt:
--------------------------------------------------------------------------------
1 | package common.util
2 |
3 | import androidx.compose.ui.unit.Density
4 | import androidx.compose.ui.unit.Dp
5 |
6 | fun pxToDp(
7 | pixels: Int,
8 | density: Density,
9 | ): Dp = with(density) { pixels.toDp() }
10 |
11 | fun dpToPx(
12 | dp: Dp,
13 | density: Density,
14 | ): Int = with(density) { dp.roundToPx() }
15 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/features/details/events/DetailsEvents.kt:
--------------------------------------------------------------------------------
1 | package features.details.events
2 |
3 | sealed class DetailsEvents {
4 | data object FetchDetails : DetailsEvents()
5 | data object OnError : DetailsEvents()
6 | data object OnSnackbarDismiss : DetailsEvents()
7 | data class ToggleContentFromList(
8 | val listId: Int,
9 | ) : DetailsEvents()
10 | }
11 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/composeResources/drawable/ic_watchlist.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/util/platform/ScreenSizeInfo.kt:
--------------------------------------------------------------------------------
1 | package common.util.platform
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.unit.Dp
5 |
6 | data class ScreenSizeInfo(
7 | val widthPx: Int,
8 | val heightPx: Int,
9 | val widthDp: Dp,
10 | val heightDp: Dp,
11 | )
12 |
13 | @Composable
14 | expect fun getScreenSizeInfo(): ScreenSizeInfo
15 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/features/watchlist/ui/state/WatchlistSnackbarState.kt:
--------------------------------------------------------------------------------
1 | package features.watchlist.ui.state
2 |
3 | import common.domain.models.util.SnackbarState
4 | import features.watchlist.ui.model.WatchlistItemAction
5 |
6 | data class WatchlistSnackbarState(
7 | val listId: Int? = null,
8 | val itemAction: WatchlistItemAction = WatchlistItemAction.ITEM_REMOVED,
9 | ) : SnackbarState()
10 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/composeResources/drawable/ic_sort.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/models/content/person/PersonCreditsResponse.kt:
--------------------------------------------------------------------------------
1 | package network.models.content.person
2 |
3 | import kotlinx.serialization.Serializable
4 | import network.models.content.common.CastResponse
5 |
6 | @Serializable
7 | data class PersonCreditsResponse(
8 | val cast: List? = null,
9 | val crew: List? = null,
10 | val id: Int? = null,
11 | )
12 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/composeResources/drawable/ic_back_arrow.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/composeResources/drawable/ic_close.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/composeResources/drawable/ic_star.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/util/Parameters.kt:
--------------------------------------------------------------------------------
1 | package com.projects.moviemanager.network.util
2 |
3 | object Parameters {
4 | const val PAGE_INDEX = "page"
5 | const val LANGUAGE = "language"
6 | const val RELEASE_DATE_GTE = "primary_release_date.gte"
7 | const val RELEASE_DATE_LTE = "primary_release_date.lte"
8 | const val SEARCH_QUERY = "query"
9 | const val MATURE_ENABLED = "include_adult"
10 | }
11 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | kotlin.code.style=official
2 |
3 | #Gradle
4 | org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2048M"
5 |
6 | #Android
7 | android.nonTransitiveRClass=true
8 | android.useAndroidX=true
9 |
10 | #Kotlin Multiplatform
11 | kotlin.mpp.enableCInteropCommonization=true
12 | kotlin.mpp.androidGradlePluginCompatibility.nowarn=true
13 |
14 | #Room
15 | kotlin.native.disableCompilerDaemon=true
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/ui/components/ClassicLoadingIndicator.kt:
--------------------------------------------------------------------------------
1 | package common.ui.components
2 |
3 | import androidx.compose.material3.CircularProgressIndicator
4 | import androidx.compose.material3.MaterialTheme
5 | import androidx.compose.runtime.Composable
6 |
7 | @Composable
8 | fun ClassicLoadingIndicator() {
9 | CircularProgressIndicator(
10 | color = MaterialTheme.colorScheme.secondary,
11 | )
12 | }
13 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/composeResources/drawable/ic_nav_watchlist.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/composeResources/drawable/ic_nav_home.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/features/home/ui/state/HomeState.kt:
--------------------------------------------------------------------------------
1 | package features.home.ui.state
2 |
3 | import androidx.compose.runtime.MutableState
4 | import androidx.compose.runtime.mutableStateOf
5 | import common.domain.models.content.GenericContent
6 | import common.domain.models.util.LoadStatusState
7 |
8 | data class HomeState(
9 | val trendingList: MutableState> = mutableStateOf(emptyList()),
10 | ) : LoadStatusState()
11 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/models/content/search/ContentPagingResponse.kt:
--------------------------------------------------------------------------------
1 | package network.models.content.search
2 |
3 | import kotlinx.serialization.Serializable
4 | import network.models.content.common.BaseContentResponse
5 |
6 | @Serializable
7 | data class ContentPagingResponse(
8 | val page: Int,
9 | val results: List = emptyList(),
10 | val total_pages: Int,
11 | val total_results: Int,
12 | )
13 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/util/Either.kt:
--------------------------------------------------------------------------------
1 | package network.util
2 |
3 | sealed class Either {
4 | val isLeft get() = this is Left
5 | val isRight get() = this is Right
6 | companion object
7 | }
8 |
9 | data class Left(val value: L) : Either()
10 | data class Right(val error: R) : Either()
11 |
12 | fun left(value: L) = Left(value)
13 | fun right(error: R) = Right(error)
14 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/domain/models/content/Videos.kt:
--------------------------------------------------------------------------------
1 | package common.domain.models.content
2 |
3 | import network.models.content.common.VideoResponse
4 |
5 | data class Videos(
6 | val name: String,
7 | val key: String,
8 | val publishedAt: String,
9 | )
10 |
11 | fun VideoResponse.toVideos(): Videos =
12 | Videos(
13 | name = this.name,
14 | key = this.key,
15 | publishedAt = this.published_at,
16 | )
17 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/models/content/person/PersonProfileResponse.kt:
--------------------------------------------------------------------------------
1 | package network.models.content.person
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class PersonProfileResponse(
7 | val aspect_ratio: Double? = null,
8 | val file_path: String? = null,
9 | val height: Int? = null,
10 | val vote_average: Double? = null,
11 | val vote_count: Int? = null,
12 | val width: Int? = null,
13 | )
14 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/features/watchlist/ui/state/WatchlistState.kt:
--------------------------------------------------------------------------------
1 | package features.watchlist.ui.state
2 |
3 | import androidx.compose.runtime.MutableState
4 | import androidx.compose.runtime.mutableStateOf
5 | import common.domain.models.content.GenericContent
6 | import common.domain.models.util.LoadStatusState
7 |
8 | class WatchlistState(
9 | var listItems: MutableState> = mutableStateOf(mutableListOf()),
10 | ) : LoadStatusState()
11 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/domain/models/content/StreamProvider.kt:
--------------------------------------------------------------------------------
1 | package common.domain.models.content
2 |
3 | import network.models.content.common.ProviderResponse
4 |
5 | data class StreamProvider(
6 | val providerName: String,
7 | val logoPath: String,
8 | )
9 |
10 | fun ProviderResponse.toStreamProvider(): StreamProvider =
11 | StreamProvider(
12 | providerName = this.provider_name.orEmpty(),
13 | logoPath = this.logo_path.orEmpty(),
14 | )
15 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/domain/models/util/ContentListType.kt:
--------------------------------------------------------------------------------
1 | package common.domain.models.util
2 |
3 | enum class ContentListType(
4 | val type: String,
5 | ) {
6 | MOVIE_NOW_PLAYING("now_playing"),
7 | MOVIE_POPULAR("popular"),
8 | MOVIE_TOP_RATED("top_rated"),
9 | MOVIE_UPCOMING("upcoming"),
10 | SHOW_AIRING_TODAY("airing_today"),
11 | SHOW_POPULAR("popular"),
12 | SHOW_TOP_RATED("top_rated"),
13 | SHOW_ON_THE_AIR("on_the_air"),
14 | }
15 |
--------------------------------------------------------------------------------
/.fleet/receipt.json:
--------------------------------------------------------------------------------
1 | // Project generated by Kotlin Multiplatform Wizard
2 | {
3 | "spec": {
4 | "template_id": "kmt",
5 | "targets": {
6 | "android": {
7 | "ui": [
8 | "compose"
9 | ]
10 | },
11 | "ios": {
12 | "ui": [
13 | "compose"
14 | ]
15 | }
16 | }
17 | },
18 | "timestamp": "2024-06-22T05:45:36.803742218Z"
19 | }
--------------------------------------------------------------------------------
/composeApp/src/androidMain/kotlin/com/projects/cinetracker/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.projects.cinetracker
2 |
3 | import MainAppView
4 | import android.os.Bundle
5 | import androidx.activity.ComponentActivity
6 | import androidx.activity.compose.setContent
7 |
8 | class MainActivity : ComponentActivity() {
9 | override fun onCreate(savedInstanceState: Bundle?) {
10 | super.onCreate(savedInstanceState)
11 |
12 | setContent {
13 | MainAppView()
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/domain/models/util/SnackbarState.kt:
--------------------------------------------------------------------------------
1 | package common.domain.models.util
2 |
3 | import androidx.compose.runtime.MutableState
4 | import androidx.compose.runtime.mutableStateOf
5 |
6 | open class SnackbarState {
7 | var displaySnackbar: MutableState = mutableStateOf(false)
8 |
9 | fun setSnackbarVisible() {
10 | displaySnackbar.value = true
11 | }
12 |
13 | fun setSnackbarGone() {
14 | displaySnackbar.value = false
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/features/search/events/SearchEvent.kt:
--------------------------------------------------------------------------------
1 | package features.search.events
2 |
3 | import features.search.ui.components.SearchTypeFilterItem
4 |
5 | sealed class SearchEvent {
6 | data object ClearSearchBar : SearchEvent()
7 | data object OnError : SearchEvent()
8 | data class SearchQuery(
9 | val query: String,
10 | ) : SearchEvent()
11 | data class FilterTypeSelected(
12 | val searchFilter: SearchTypeFilterItem,
13 | ) : SearchEvent()
14 | }
15 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/composeResources/drawable/ic_more_options.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/ui/components/SystemUtil.kt:
--------------------------------------------------------------------------------
1 | package common.ui.components
2 |
3 | import androidx.compose.foundation.layout.Spacer
4 | import androidx.compose.foundation.layout.height
5 | import androidx.compose.runtime.Composable
6 | import androidx.compose.ui.Modifier
7 | import androidx.compose.ui.unit.dp
8 | import common.util.UiConstants.SYSTEM_BOTTOM_NAV_PADDING
9 |
10 | @Composable
11 | fun SystemNavBarSpacer() {
12 | Spacer(modifier = Modifier.height(SYSTEM_BOTTOM_NAV_PADDING.dp))
13 | }
14 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/features/browse/events/BrowseEvent.kt:
--------------------------------------------------------------------------------
1 | package features.browse.events
2 |
3 | import common.domain.models.util.MediaType
4 | import common.domain.models.util.SortTypeItem
5 |
6 | sealed class BrowseEvent {
7 | data class UpdateSortType(
8 | val movieListType: SortTypeItem,
9 | val mediaType: MediaType,
10 | ) : BrowseEvent()
11 | data class UpdateMediaType(
12 | val mediaType: MediaType,
13 | ) : BrowseEvent()
14 | data object OnError : BrowseEvent()
15 | }
16 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/navigation/screens/ErrorScreenUI.kt:
--------------------------------------------------------------------------------
1 | package navigation.screens
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.navigation.NavController
5 | import common.ui.screen.ErrorScreen
6 | import navigation.ScreenUI
7 |
8 | class ErrorScreenUI : ScreenUI {
9 | @Composable
10 | override fun UI(navController: NavController) {
11 | ErrorScreen(
12 | onTryAgain = {
13 | navController.popBackStack()
14 | },
15 | )
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/domain/models/util/MediaType.kt:
--------------------------------------------------------------------------------
1 | package common.domain.models.util
2 |
3 | enum class MediaType {
4 | MOVIE,
5 | SHOW,
6 | PERSON,
7 | UNKNOWN,
8 | ;
9 |
10 | companion object {
11 | fun getType(typeName: String?): MediaType =
12 | when (typeName?.lowercase()) {
13 | "movie" -> MOVIE
14 | "show", "tv" -> SHOW
15 | "person", "people" -> PERSON
16 | else -> UNKNOWN
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/composeApp/src/androidMain/kotlin/common/util/platform/PlatformUtils.kt:
--------------------------------------------------------------------------------
1 | package common.util.platform
2 |
3 | import java.util.Locale
4 |
5 | actual object PlatformUtils {
6 | actual val isIOS: Boolean = false
7 | actual fun getUserLanguage(): String = Locale.getDefault().language
8 | actual fun getUserCountry(): String = Locale.getDefault().country
9 | actual fun getLocale(): String {
10 | val language = getUserLanguage()
11 | val country = getUserCountry()
12 |
13 | return "$language-$country"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/composeResources/drawable/ic_nav_browse.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/models/content/common/VideoResponse.kt:
--------------------------------------------------------------------------------
1 | package network.models.content.common
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class VideoResponse(
7 | val name: String,
8 | val key: String,
9 | val published_at: String,
10 | val id: String? = null,
11 | val iso_3166_1: String? = null,
12 | val iso_639_1: String? = null,
13 | val official: Boolean? = null,
14 | val site: String? = null,
15 | val size: Int? = null,
16 | val type: String? = null,
17 | )
18 |
--------------------------------------------------------------------------------
/composeApp/src/iosMain/kotlin/MainViewController.kt:
--------------------------------------------------------------------------------
1 |
2 | import androidx.compose.ui.window.ComposeUIViewController
3 | import core.di.KoinInitializer
4 | import dev.gitlive.firebase.Firebase
5 | import dev.gitlive.firebase.crashlytics.crashlytics
6 | import dev.gitlive.firebase.initialize
7 |
8 | fun MainViewController() = ComposeUIViewController(
9 | configure = {
10 | KoinInitializer().init()
11 | Firebase.initialize()
12 | Firebase.crashlytics.setCrashlyticsCollectionEnabled(true)
13 | },
14 | ) {
15 | MainAppView()
16 | }
17 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/composeResources/drawable/ic_play_video.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/composeResources/drawable/ic_nav_search.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/domain/models/person/PersonImage.kt:
--------------------------------------------------------------------------------
1 | package common.domain.models.person
2 |
3 | import network.models.content.person.PersonProfileResponse
4 |
5 | data class PersonImage(
6 | val aspectRatio: Double?,
7 | val filePath: String?,
8 | val height: Int?,
9 | val width: Int?,
10 | )
11 |
12 | fun PersonProfileResponse.toPersonImage(): PersonImage =
13 | PersonImage(
14 | aspectRatio = this.aspect_ratio,
15 | filePath = this.file_path,
16 | height = this.height,
17 | width = this.width,
18 | )
19 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/models/content/common/WatchProvidersResponse.kt:
--------------------------------------------------------------------------------
1 | package network.models.content.common
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class WatchProvidersResponse(
7 | val id: Int,
8 | val results: Map? = null,
9 | )
10 |
11 | @Serializable
12 | data class CountryProviderResponse(
13 | val flatrate: List? = null,
14 | )
15 |
16 | @Serializable
17 | data class ProviderResponse(
18 | val logo_path: String? = null,
19 | val provider_name: String? = null,
20 | )
21 |
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
17 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/database/di/DaoModule.kt:
--------------------------------------------------------------------------------
1 | package database.di
2 |
3 | import database.AppDatabase
4 | import database.dao.ContentEntityDao
5 | import database.dao.ListEntityDao
6 | import org.koin.dsl.module
7 |
8 | val daoModule = module {
9 | single { provideContentEntityDao(get()) }
10 | single { provideListEntityDao(get()) }
11 | }
12 |
13 | private fun provideContentEntityDao(appDatabase: AppDatabase): ContentEntityDao {
14 | return appDatabase.contentEntityDao()
15 | }
16 |
17 | private fun provideListEntityDao(appDatabase: AppDatabase): ListEntityDao {
18 | return appDatabase.listEntityDao()
19 | }
20 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package common.ui.theme
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val PrimaryYellowColor = Color(0xFFFA9F26)
6 | val PrimaryYellowColor_90 = Color(0xE6FA9F26)
7 | val PrimaryBlackColor = Color(0xFF000000)
8 | val PrimaryGreyColor = Color(0xFFDCDCDC)
9 | val PrimaryGreyColor_55 = Color(0x8C24252A)
10 | val PrimaryWhiteColor = Color(0xFFFFFFFF)
11 | val SecondaryGreyColor = Color(0xFF9D9D9D)
12 | val MainBarGreyColor = Color(0xFF191A1D)
13 | val PrimaryRedColor = Color(0xFFFA2626)
14 |
15 | val placeholderGrey = Color(0xFFB8B5B5)
16 | val placeholderGrey2 = Color(0xFF8F8B8B)
17 | val DividerGrey = Color(0x592C2E33)
18 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/util/ApiResult.kt:
--------------------------------------------------------------------------------
1 | package network.util
2 |
3 | import kotlinx.coroutines.flow.Flow
4 | import kotlinx.coroutines.flow.flow
5 | import network.models.ApiError
6 |
7 | sealed class ApiResult {
8 | class Success(val data: T, val code: Int) : ApiResult()
9 | class Error(val data: ApiError, val code: Int? = null) : ApiResult()
10 | }
11 |
12 | fun ApiResult.asFlow(): Flow> {
13 | val result = this
14 | return flow {
15 | when (result) {
16 | is ApiResult.Success -> emit(left(result.data))
17 | is ApiResult.Error -> emit(right(result.data))
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/features/details/state/DetailsState.kt:
--------------------------------------------------------------------------------
1 | package features.details.state
2 |
3 | import androidx.compose.runtime.MutableState
4 | import androidx.compose.runtime.mutableStateOf
5 | import common.domain.models.content.ContentCast
6 | import common.domain.models.content.DetailedContent
7 | import common.domain.models.content.Videos
8 | import common.domain.models.util.LoadStatusState
9 |
10 | data class DetailsState(
11 | var detailsInfo: MutableState = mutableStateOf(null),
12 | var detailsCast: MutableState> = mutableStateOf(listOf()),
13 | var detailsVideos: MutableState> = mutableStateOf(listOf()),
14 | ) : LoadStatusState()
15 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/database/AppDatabase.kt:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | import androidx.room.Database
4 | import androidx.room.RoomDatabase
5 | import database.dao.ContentEntityDao
6 | import database.dao.ListEntityDao
7 | import database.model.ContentEntity
8 | import database.model.ListEntity
9 |
10 | @Database(
11 | entities = [ContentEntity::class, ListEntity::class],
12 | version = 5,
13 | )
14 | abstract class AppDatabase : RoomDatabase(), DB {
15 | abstract fun contentEntityDao(): ContentEntityDao
16 | abstract fun listEntityDao(): ListEntityDao
17 |
18 | override fun clearAllTables() {
19 | super.clearAllTables()
20 | }
21 | }
22 |
23 | interface DB {
24 | fun clearAllTables() {}
25 | }
26 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/domain/models/content/ContentCast.kt:
--------------------------------------------------------------------------------
1 | package common.domain.models.content
2 |
3 | import network.models.content.common.ContentCastResponse
4 |
5 | data class ContentCast(
6 | val id: Int,
7 | val name: String,
8 | val character: String,
9 | val profilePoster: String,
10 | val order: Int?,
11 | )
12 |
13 | fun ContentCastResponse.toContentCast(): ContentCast =
14 | ContentCast(
15 | id = this.id,
16 | name = this.name,
17 | character =
18 | this.character ?: this.roles
19 | ?.firstOrNull()
20 | ?.character
21 | .orEmpty(),
22 | profilePoster = this.profile_path.orEmpty(),
23 | order = this.order,
24 | )
25 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/ui/theme/Shape.kt:
--------------------------------------------------------------------------------
1 | package common.ui.theme
2 |
3 | import androidx.compose.foundation.shape.RoundedCornerShape
4 | import androidx.compose.material3.Shapes
5 | import androidx.compose.ui.unit.dp
6 | import common.util.UiConstants.CARD_ROUND_CORNER
7 |
8 | val RoundCornerShapes = Shapes(
9 | // Extra small = round on bottom
10 | extraSmall = RoundedCornerShape(
11 | bottomStart = CARD_ROUND_CORNER.dp,
12 | bottomEnd = CARD_ROUND_CORNER.dp,
13 | ),
14 | // Small = round on top
15 | small = RoundedCornerShape(topStart = CARD_ROUND_CORNER.dp, topEnd = CARD_ROUND_CORNER.dp),
16 | // Medium = completely round
17 | medium = RoundedCornerShape(6.dp),
18 | large = RoundedCornerShape(8.dp),
19 | )
20 |
--------------------------------------------------------------------------------
/composeApp/src/androidMain/kotlin/common/util/platform/ScreenSizeInfo.android.kt:
--------------------------------------------------------------------------------
1 | package common.util.platform
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.platform.LocalConfiguration
5 | import androidx.compose.ui.platform.LocalDensity
6 | import androidx.compose.ui.unit.dp
7 |
8 | @Composable
9 | actual fun getScreenSizeInfo(): ScreenSizeInfo {
10 | val density = LocalDensity.current
11 | val config = LocalConfiguration.current
12 | val hDp = config.screenHeightDp.dp
13 | val wDp = config.screenWidthDp.dp
14 |
15 | return ScreenSizeInfo(
16 | widthPx = with(density) { wDp.roundToPx() },
17 | heightPx = with(density) { hDp.roundToPx() },
18 | widthDp = wDp,
19 | heightDp = hDp,
20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/core/di/modules/InteractorModule.kt:
--------------------------------------------------------------------------------
1 | package core.di.modules
2 |
3 | import features.browse.domain.BrowseInteractor
4 | import features.details.domain.DetailsInteractor
5 | import features.home.domain.HomeInteractor
6 | import features.search.domain.SearchInteractor
7 | import features.watchlist.domain.WatchlistInteractor
8 | import org.koin.dsl.module
9 |
10 | val interactorModule = module {
11 | single { BrowseInteractor(get(), get()) }
12 | single { DetailsInteractor(get(), get(), get(), get()) }
13 | single { WatchlistInteractor(get(), get(), get()) }
14 | single { SearchInteractor(get()) }
15 | single { HomeInteractor(get(), get(), get(), get()) }
16 | }
17 |
--------------------------------------------------------------------------------
/composeApp/src/iosMain/kotlin/common/util/platform/ScreenSizeInfo.ios.kt:
--------------------------------------------------------------------------------
1 | package common.util.platform
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.ExperimentalComposeUiApi
5 | import androidx.compose.ui.platform.LocalDensity
6 | import androidx.compose.ui.platform.LocalWindowInfo
7 |
8 | @OptIn(ExperimentalComposeUiApi::class)
9 | @Composable
10 | actual fun getScreenSizeInfo(): ScreenSizeInfo {
11 | val density = LocalDensity.current
12 | val config = LocalWindowInfo.current.containerSize
13 |
14 | return ScreenSizeInfo(
15 | widthPx = config.width,
16 | heightPx = config.height,
17 | widthDp = with(density) { config.width.toDp() },
18 | heightDp = with(density) { config.height.toDp() },
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/database/dao/ListEntityDao.kt:
--------------------------------------------------------------------------------
1 | package database.dao
2 |
3 | import androidx.room.Dao
4 | import androidx.room.Insert
5 | import androidx.room.OnConflictStrategy
6 | import androidx.room.Query
7 | import database.model.ListEntity
8 |
9 | @Dao
10 | interface ListEntityDao {
11 |
12 | @Insert(onConflict = OnConflictStrategy.ABORT)
13 | suspend fun insertList(listEntity: ListEntity)
14 |
15 | @Query("DELETE FROM list_entity WHERE listId = :listId")
16 | suspend fun deleteList(listId: Int)
17 |
18 | @Query("SELECT * FROM list_entity")
19 | suspend fun getAllLists(): List
20 |
21 | @Query("SELECT COUNT(*) FROM list_entity WHERE listName = :listName COLLATE NOCASE")
22 | suspend fun getListCountByName(listName: String): Int
23 | }
24 |
--------------------------------------------------------------------------------
/composeApp/src/androidMain/kotlin/common/util/platform/DateUtils.kt:
--------------------------------------------------------------------------------
1 | package common.util.platform
2 |
3 | import java.text.SimpleDateFormat
4 | import java.util.Calendar
5 | import java.util.Locale
6 |
7 | actual object DateUtils {
8 | actual fun getComingSoonDates(
9 | monthPeriod: Int,
10 | ): Pair {
11 | val calendar = Calendar.getInstance()
12 |
13 | val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
14 | val releaseDateGte = dateFormat.format(calendar.time)
15 | calendar.add(Calendar.MONTH, monthPeriod)
16 | val releaseDateLte = dateFormat.format(calendar.time)
17 | return Pair(releaseDateGte, releaseDateLte)
18 | }
19 |
20 | actual fun getCurrentTimeMillis(): Long = System.currentTimeMillis()
21 | }
22 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/database/model/ContentEntity.kt:
--------------------------------------------------------------------------------
1 | package database.model
2 |
3 | import androidx.room.Entity
4 | import androidx.room.ForeignKey
5 | import androidx.room.PrimaryKey
6 | import common.util.platform.DateUtils
7 |
8 | @Entity(
9 | tableName = "content_entity",
10 | foreignKeys = [
11 | ForeignKey(
12 | entity = ListEntity::class,
13 | parentColumns = ["listId"],
14 | childColumns = ["listId"],
15 | onDelete = ForeignKey.CASCADE,
16 | ),
17 | ],
18 | )
19 | data class ContentEntity(
20 | @PrimaryKey(autoGenerate = true) val contentEntityDbId: Int = 0,
21 | val contentId: Int,
22 | val mediaType: String,
23 | val listId: Int,
24 | val createdAt: Long = DateUtils.getCurrentTimeMillis(),
25 | )
26 |
--------------------------------------------------------------------------------
/composeApp/google-services.json:
--------------------------------------------------------------------------------
1 | {
2 | "project_info": {
3 | "project_number": "824837098950",
4 | "project_id": "cinetracker-kmp",
5 | "storage_bucket": "cinetracker-kmp.appspot.com"
6 | },
7 | "client": [
8 | {
9 | "client_info": {
10 | "mobilesdk_app_id": "1:824837098950:android:5db2048021a3956ad4b6c7",
11 | "android_client_info": {
12 | "package_name": "gustavo.projects.restapi"
13 | }
14 | },
15 | "oauth_client": [],
16 | "api_key": [
17 | {
18 | "current_key": "AIzaSyASQ7FzQT0ETK2f4wniPHxTeoLjUvd7zNQ"
19 | }
20 | ],
21 | "services": {
22 | "appinvite_service": {
23 | "other_platform_oauth_client": []
24 | }
25 | }
26 | }
27 | ],
28 | "configuration_version": "1"
29 | }
--------------------------------------------------------------------------------
/composeApp/src/androidMain/kotlin/common/util/platform/StatusBarUpdate.android.kt:
--------------------------------------------------------------------------------
1 | package common.util.platform
2 |
3 | import android.app.Activity
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.runtime.DisposableEffect
6 | import androidx.compose.ui.graphics.Color
7 | import androidx.compose.ui.graphics.toArgb
8 | import androidx.compose.ui.platform.LocalContext
9 | import common.ui.theme.PrimaryBlackColor
10 |
11 | @Composable
12 | actual fun SetStatusBarColor(newColor: Color) {
13 | val context = LocalContext.current
14 | val window = (context as? Activity)?.window
15 |
16 | DisposableEffect(Unit) {
17 | window?.statusBarColor = newColor.toArgb()
18 |
19 | onDispose {
20 | window?.statusBarColor = PrimaryBlackColor.toArgb()
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/util/NestedScrollConnection.kt:
--------------------------------------------------------------------------------
1 | package common.util
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.remember
5 | import androidx.compose.ui.geometry.Offset
6 | import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
7 | import androidx.compose.ui.input.nestedscroll.NestedScrollSource
8 |
9 | @Composable
10 | fun rememberNestedScrollConnection(onScroll: () -> Unit): NestedScrollConnection {
11 | return remember {
12 | object : NestedScrollConnection {
13 | override fun onPreScroll(
14 | available: Offset,
15 | source: NestedScrollSource,
16 | ): Offset {
17 | onScroll()
18 | return Offset.Zero
19 | }
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/repository/person/PersonRepository.kt:
--------------------------------------------------------------------------------
1 | package network.repository.person
2 |
3 | import kotlinx.coroutines.flow.Flow
4 | import network.models.ApiError
5 | import network.models.content.common.PersonResponse
6 | import network.models.content.person.PersonCreditsResponse
7 | import network.models.content.person.PersonImagesResponse
8 | import network.util.Either
9 |
10 | interface PersonRepository {
11 | suspend fun getPersonDetailsById(
12 | personId: Int,
13 | ): Flow>
14 |
15 | suspend fun getPersonCreditsById(
16 | personId: Int,
17 | ): Flow>
18 |
19 | suspend fun getPersonImagesById(
20 | personId: Int,
21 | ): Flow>
22 | }
23 |
--------------------------------------------------------------------------------
/composeApp/src/iosMain/kotlin/network/NetworkClient.ios.kt:
--------------------------------------------------------------------------------
1 | package network
2 |
3 | import common.util.Constants.BASE_URL_MOVIEDB
4 | import io.ktor.client.HttpClient
5 | import io.ktor.client.engine.darwin.Darwin
6 | import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
7 | import io.ktor.client.plugins.defaultRequest
8 | import io.ktor.serialization.kotlinx.json.json
9 | import kotlinx.serialization.json.Json
10 |
11 | actual val client: HttpClient
12 | get() = HttpClient(Darwin) {
13 | defaultRequest {
14 | url(BASE_URL_MOVIEDB)
15 | }
16 | install(ContentNegotiation) {
17 | json(
18 | Json {
19 | ignoreUnknownKeys = true
20 | isLenient = true
21 | },
22 | )
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/composeApp/src/androidMain/kotlin/network/NetworkClient.android.kt:
--------------------------------------------------------------------------------
1 | package network
2 |
3 | import common.util.Constants.BASE_URL_MOVIEDB
4 | import io.ktor.client.HttpClient
5 | import io.ktor.client.engine.okhttp.OkHttp
6 | import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
7 | import io.ktor.client.plugins.defaultRequest
8 | import io.ktor.serialization.kotlinx.json.json
9 | import kotlinx.serialization.json.Json
10 |
11 | actual val client: HttpClient
12 | get() = HttpClient(OkHttp) {
13 | install(ContentNegotiation) {
14 | json(
15 | Json {
16 | ignoreUnknownKeys = true
17 | isLenient = true
18 | },
19 | )
20 | }
21 | defaultRequest {
22 | url(BASE_URL_MOVIEDB)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/features/browse/ui/components/MediaTypeTabItem.kt:
--------------------------------------------------------------------------------
1 | package features.browse.ui.components
2 |
3 | import cinetracker_kmp.composeapp.generated.resources.Res
4 | import cinetracker_kmp.composeapp.generated.resources.movies_tab
5 | import cinetracker_kmp.composeapp.generated.resources.shows_tab
6 | import common.domain.models.util.MediaType
7 | import org.jetbrains.compose.resources.StringResource
8 |
9 | sealed class MediaTypeTabItem(
10 | val tabResId: StringResource,
11 | val mediaType: MediaType,
12 | ) {
13 | data object Movies : MediaTypeTabItem(
14 | tabResId = Res.string.movies_tab,
15 | mediaType = MediaType.MOVIE,
16 | )
17 | data object Shows : MediaTypeTabItem(
18 | tabResId = Res.string.shows_tab,
19 | mediaType = MediaType.SHOW,
20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/models/content/common/ContentCastResponse.kt:
--------------------------------------------------------------------------------
1 | package network.models.content.common
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class ContentCastResponse(
7 | val id: Int,
8 | val name: String,
9 | val character: String? = null,
10 | val profile_path: String? = null,
11 | val adult: Boolean? = null,
12 | val credit_id: String? = null,
13 | val gender: Int? = null,
14 | val known_for_department: String? = null,
15 | val order: Int? = null,
16 | val original_name: String? = null,
17 | val popularity: Double? = null,
18 | val roles: List? = null,
19 | )
20 |
21 | @Serializable
22 | data class CastRoles(
23 | val credit_id: String? = null,
24 | val character: String? = null,
25 | val episode_count: Int? = null,
26 | )
27 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/features/watchlist/ui/components/WatchlistSortTypeItem.kt:
--------------------------------------------------------------------------------
1 | package features.watchlist.ui.components
2 |
3 | import cinetracker_kmp.composeapp.generated.resources.Res
4 | import cinetracker_kmp.composeapp.generated.resources.movie_tag
5 | import cinetracker_kmp.composeapp.generated.resources.show_tag
6 | import common.domain.models.util.MediaType
7 | import org.jetbrains.compose.resources.StringResource
8 |
9 | sealed class WatchlistSortTypeItem(
10 | val titleRes: StringResource,
11 | val mediaType: MediaType,
12 | ) {
13 | data object MovieOnly : WatchlistSortTypeItem(
14 | titleRes = Res.string.movie_tag,
15 | mediaType = MediaType.MOVIE,
16 | )
17 | data object ShowOnly : WatchlistSortTypeItem(
18 | titleRes = Res.string.show_tag,
19 | mediaType = MediaType.SHOW,
20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/util/CardsUtil.kt:
--------------------------------------------------------------------------------
1 | package common.util
2 |
3 | import androidx.compose.ui.unit.Density
4 | import androidx.compose.ui.unit.Dp
5 | import kotlin.math.floor
6 |
7 | fun calculateCardsPerRow(
8 | screenWidth: Int,
9 | minCardSize: Int,
10 | spacing: Int,
11 | density: Density,
12 | ): Pair {
13 | // Calculate the number of cards that can fit in a row
14 | val numCardsPerRow =
15 | floor(
16 | (screenWidth + spacing).toFloat() /
17 | (minCardSize),
18 | ).coerceAtLeast(1f).toInt()
19 |
20 | // Calculate the adjusted size for each card
21 | val totalSpacing = (numCardsPerRow - 1) * spacing
22 | val adjustedCardSize = density.run { ((screenWidth - totalSpacing) / numCardsPerRow).toDp() }
23 |
24 | return Pair(numCardsPerRow, adjustedCardSize)
25 | }
26 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/repository/home/HomeRepository.kt:
--------------------------------------------------------------------------------
1 | package network.repository.home
2 |
3 | import kotlinx.coroutines.flow.Flow
4 | import network.models.ApiError
5 | import network.models.content.common.MovieResponse
6 | import network.models.content.common.MultiResponse
7 | import network.models.content.common.PersonResponse
8 | import network.models.content.search.ContentPagingResponse
9 | import network.util.Either
10 |
11 | interface HomeRepository {
12 | suspend fun getTrendingMulti(): Flow, ApiError>>
13 | suspend fun getTrendingPerson(): Flow, ApiError>>
14 | suspend fun getMoviesComingSoon(
15 | releaseDateGte: String,
16 | releaseDateLte: String,
17 | ): Flow, ApiError>>
18 | }
19 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/models/content/person/CrewResponse.kt:
--------------------------------------------------------------------------------
1 | package network.models.content.person
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class CrewResponse(
7 | val adult: Boolean? = null,
8 | val backdrop_path: String? = null,
9 | val credit_id: String? = null,
10 | val department: String? = null,
11 | val genre_ids: List? = null,
12 | val id: Int? = null,
13 | val job: String? = null,
14 | val original_language: String? = null,
15 | val original_title: String? = null,
16 | val overview: String? = null,
17 | val popularity: Double? = null,
18 | val poster_path: String? = null,
19 | val release_date: String? = null,
20 | val title: String? = null,
21 | val video: Boolean? = null,
22 | val vote_average: Double? = null,
23 | val vote_count: Int? = null,
24 | )
25 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/util/Constants.kt:
--------------------------------------------------------------------------------
1 | package common.util
2 |
3 | object Constants {
4 | const val BASE_URL_MOVIEDB = "https://api.themoviedb.org/3/"
5 | const val BASE_500_IMAGE_URL = "https://image.tmdb.org/t/p/w500"
6 | const val BASE_300_IMAGE_URL = "https://image.tmdb.org/t/p/w300"
7 | const val BASE_ORIGINAL_IMAGE_URL = "https://image.tmdb.org/t/p/original"
8 | const val BASE_YOUTUBE_THUMBAIL_URL = "https://img.youtube.com/vi/"
9 | const val YOUTUBE_THUMBAIL_RESOLUTION = "/maxresdefault.jpg"
10 |
11 | const val BASE_URL_YOUTUBE_VIDEO = "http://www.youtube.com/watch?v="
12 |
13 | const val PAGE_SIZE = 20
14 |
15 | const val UNSELECTED_OPTION_INDEX = -1
16 | const val ADD_NEW_TAB_ID = -2
17 | const val MAX_WATCHLIST_LIST_NUMBER = 12
18 | const val DEFAULT_LISTS_SIZE = 2
19 | const val MONTH_PERIOD_COMING_SOON = 1
20 | }
21 |
--------------------------------------------------------------------------------
/composeApp/src/iosMain/kotlin/core/di/KoinInitializer.ios.kt:
--------------------------------------------------------------------------------
1 | package core.di
2 |
3 | import core.di.modules.interactorModule
4 | import core.di.modules.viewModelModule
5 | import database.di.daoModule
6 | import database.di.databaseModule
7 | import database.di.databaseRepositoryModule
8 | import network.di.apiModule
9 | import network.di.repositoryModule
10 | import network.di.serviceModule
11 | import org.koin.core.context.startKoin
12 |
13 | actual class KoinInitializer {
14 | actual fun init() {
15 | startKoin {
16 | modules(
17 | interactorModule,
18 | viewModelModule,
19 | databaseModule(),
20 | daoModule,
21 | databaseRepositoryModule,
22 | apiModule,
23 | serviceModule,
24 | repositoryModule,
25 | )
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/core/di/modules/ViewModelModule.kt:
--------------------------------------------------------------------------------
1 | package core.di.modules
2 |
3 | import common.domain.models.util.MediaType
4 | import common.ui.MainViewModel
5 | import features.browse.ui.BrowseViewModel
6 | import features.details.ui.DetailsViewModel
7 | import features.home.ui.HomeViewModel
8 | import features.search.ui.SearchViewModel
9 | import features.watchlist.ui.WatchlistViewModel
10 | import org.koin.compose.viewmodel.dsl.viewModel
11 | import org.koin.dsl.module
12 |
13 | val viewModelModule = module {
14 | single { MainViewModel(get()) }
15 | viewModel { HomeViewModel(get()) }
16 | viewModel { BrowseViewModel(get()) }
17 | viewModel { WatchlistViewModel(get()) }
18 | viewModel { SearchViewModel(get()) }
19 | viewModel {
20 | (contentId: Int, mediaType: MediaType) ->
21 | DetailsViewModel(contentId, mediaType, get())
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/services/person/PersonService.kt:
--------------------------------------------------------------------------------
1 | package network.services.person
2 |
3 | import core.LanguageManager.getUserLanguageTag
4 | import network.models.content.common.PersonResponse
5 | import network.models.content.person.PersonCreditsResponse
6 | import network.models.content.person.PersonImagesResponse
7 | import network.util.ApiResult
8 |
9 | interface PersonService {
10 | suspend fun getPersonDetailsById(
11 | personId: Int,
12 | language: String = getUserLanguageTag(),
13 | ): ApiResult
14 |
15 | suspend fun getPersonCreditsById(
16 | personId: Int,
17 | language: String = getUserLanguageTag(),
18 | ): ApiResult
19 |
20 | suspend fun getPersonImagesById(
21 | personId: Int,
22 | language: String = getUserLanguageTag(),
23 | ): ApiResult
24 | }
25 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/di/ServiceModule.kt:
--------------------------------------------------------------------------------
1 | package network.di
2 |
3 | import network.services.home.HomeService
4 | import network.services.home.HomeServiceImpl
5 | import network.services.movie.MovieService
6 | import network.services.movie.MovieServiceImpl
7 | import network.services.person.PersonService
8 | import network.services.person.PersonServiceImpl
9 | import network.services.search.SearchService
10 | import network.services.search.SearchServiceImpl
11 | import network.services.show.ShowService
12 | import network.services.show.ShowServiceImpl
13 | import org.koin.dsl.module
14 |
15 | val serviceModule = module {
16 | single { MovieServiceImpl(get()) }
17 | single { ShowServiceImpl(get()) }
18 | single { PersonServiceImpl(get()) }
19 | single { SearchServiceImpl(get()) }
20 | single { HomeServiceImpl(get()) }
21 | }
22 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/ui/components/button/SimpleButton.kt:
--------------------------------------------------------------------------------
1 | package common.ui.components.button
2 |
3 | import androidx.compose.material3.MaterialTheme
4 | import androidx.compose.material3.Text
5 | import androidx.compose.material3.TextButton
6 | import androidx.compose.runtime.Composable
7 | import androidx.compose.ui.Modifier
8 | import androidx.compose.ui.graphics.Color
9 | import androidx.compose.ui.text.TextStyle
10 |
11 | @Composable
12 | fun SimpleButton(
13 | modifier: Modifier = Modifier,
14 | text: String,
15 | textColor: Color = MaterialTheme.colorScheme.secondary,
16 | textStyle: TextStyle = MaterialTheme.typography.titleMedium,
17 | onClick: () -> Unit,
18 | ) {
19 | TextButton(
20 | modifier = modifier,
21 | onClick = onClick,
22 | ) {
23 | Text(
24 | text = text,
25 | style = textStyle,
26 | color = textColor,
27 | )
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/features/search/domain/SearchInteractor.kt:
--------------------------------------------------------------------------------
1 | package features.search.domain
2 |
3 | import androidx.paging.Pager
4 | import androidx.paging.PagingConfig
5 | import androidx.paging.PagingData
6 | import common.domain.models.content.GenericContent
7 | import common.domain.models.util.MediaType
8 | import common.util.Constants
9 | import features.search.ui.paging.SearchPagingSource
10 | import kotlinx.coroutines.flow.Flow
11 | import network.repository.search.SearchRepository
12 |
13 | class SearchInteractor(
14 | private val searchRepository: SearchRepository,
15 | ) {
16 | fun onSearchQuery(
17 | query: String,
18 | mediaType: MediaType?,
19 | ): Flow> {
20 | return Pager(PagingConfig(pageSize = Constants.PAGE_SIZE)) {
21 | SearchPagingSource(
22 | searchRepository,
23 | query,
24 | mediaType,
25 | )
26 | }.flow
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/services/home/HomeService.kt:
--------------------------------------------------------------------------------
1 | package network.services.home
2 |
3 | import core.LanguageManager.getUserLanguageTag
4 | import network.models.content.common.MovieResponse
5 | import network.models.content.common.MultiResponse
6 | import network.models.content.common.PersonResponse
7 | import network.models.content.search.ContentPagingResponse
8 | import network.util.ApiResult
9 |
10 | interface HomeService {
11 | suspend fun getDayTrendingMulti(
12 | language: String = getUserLanguageTag(),
13 | ): ApiResult>
14 |
15 | suspend fun getDayTrendingPerson(
16 | language: String = getUserLanguageTag(),
17 | ): ApiResult>
18 |
19 | suspend fun getMoviesComingSoon(
20 | language: String = getUserLanguageTag(),
21 | releaseDateGte: String,
22 | releaseDateLte: String,
23 | ): ApiResult>
24 | }
25 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/di/RepositoryModule.kt:
--------------------------------------------------------------------------------
1 | package network.di
2 |
3 | import network.repository.home.HomeRepository
4 | import network.repository.home.HomeRepositoryImpl
5 | import network.repository.movie.MovieRepository
6 | import network.repository.movie.MovieRepositoryImpl
7 | import network.repository.person.PersonRepository
8 | import network.repository.person.PersonRepositoryImpl
9 | import network.repository.search.SearchRepository
10 | import network.repository.search.SearchRepositoryImpl
11 | import network.repository.show.ShowRepository
12 | import network.repository.show.ShowRepositoryImpl
13 | import org.koin.dsl.module
14 |
15 | val repositoryModule = module {
16 | single { MovieRepositoryImpl(get()) }
17 | single { ShowRepositoryImpl(get()) }
18 | single { PersonRepositoryImpl(get()) }
19 | single { SearchRepositoryImpl(get()) }
20 | single { HomeRepositoryImpl(get()) }
21 | }
22 |
--------------------------------------------------------------------------------
/composeApp/src/iosMain/kotlin/common/util/platform/PlatformUtils.kt:
--------------------------------------------------------------------------------
1 | package common.util.platform
2 |
3 | import platform.Foundation.NSLocale
4 | import platform.Foundation.countryCode
5 | import platform.Foundation.currentLocale
6 | import platform.Foundation.preferredLanguages
7 |
8 | const val DEFAULT_LANGUAGE = "en"
9 | const val DEFAULT_COUNTRY = "US"
10 |
11 | actual object PlatformUtils {
12 | actual val isIOS: Boolean = true
13 | actual fun getUserLanguage(): String {
14 | val languageTag = (NSLocale.preferredLanguages.firstOrNull() as String?)
15 | return languageTag?.split("-")?.first() ?: DEFAULT_LANGUAGE
16 | }
17 | actual fun getUserCountry(): String = NSLocale.currentLocale.countryCode ?: DEFAULT_COUNTRY
18 | actual fun getLocale(): String {
19 | val locale = (NSLocale.preferredLanguages.firstOrNull() as String?)
20 | val language = getUserLanguage()
21 | val country = getUserCountry()
22 |
23 | return locale ?: "$language-$country"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/database/repository/DatabaseRepository.kt:
--------------------------------------------------------------------------------
1 | package database.repository
2 |
3 | import common.domain.models.util.MediaType
4 | import database.model.ContentEntity
5 | import database.model.ListEntity
6 |
7 | interface DatabaseRepository {
8 | suspend fun insertItem(contentId: Int, mediaType: MediaType, listId: Int)
9 |
10 | suspend fun deleteItem(contentId: Int, mediaType: MediaType, listId: Int): ContentEntity?
11 |
12 | suspend fun getAllItemsByListId(listId: Int): List
13 |
14 | suspend fun searchItems(contentId: Int, mediaType: MediaType): List
15 |
16 | suspend fun moveItemToList(
17 | contentId: Int,
18 | mediaType: MediaType,
19 | currentListId: Int,
20 | newListId: Int,
21 | ): ContentEntity?
22 |
23 | suspend fun reinsertItem(contentEntity: ContentEntity)
24 |
25 | suspend fun getAllLists(): List
26 |
27 | suspend fun addNewList(listName: String): Boolean
28 |
29 | suspend fun deleteList(listId: Int)
30 | }
31 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/features/watchlist/events/WatchlistEvent.kt:
--------------------------------------------------------------------------------
1 | package features.watchlist.events
2 |
3 | import common.domain.models.util.MediaType
4 | import features.watchlist.ui.components.WatchlistTabItem
5 |
6 | sealed class WatchlistEvent {
7 | data object LoadWatchlistData : WatchlistEvent()
8 | data object OnSnackbarDismiss : WatchlistEvent()
9 | data object UndoItemAction : WatchlistEvent()
10 | data class RemoveItem(
11 | val contentId: Int,
12 | val mediaType: MediaType,
13 | ) : WatchlistEvent()
14 | data class SelectList(
15 | val tabItem: WatchlistTabItem,
16 | ) : WatchlistEvent()
17 | data class UpdateSortType(
18 | val mediaType: MediaType?,
19 | ) : WatchlistEvent()
20 | data class UpdateItemListId(
21 | val contentId: Int,
22 | val mediaType: MediaType,
23 | val listId: Int,
24 | ) : WatchlistEvent()
25 | data object LoadAllLists : WatchlistEvent()
26 | data class DeleteList(
27 | val listId: Int,
28 | ) : WatchlistEvent()
29 | }
30 |
--------------------------------------------------------------------------------
/composeApp/src/androidMain/kotlin/core/di/KoinInitializer.android.kt:
--------------------------------------------------------------------------------
1 | package core.di
2 |
3 | import android.content.Context
4 | import core.di.modules.interactorModule
5 | import core.di.modules.viewModelModule
6 | import database.di.daoModule
7 | import database.di.databaseModule
8 | import database.di.databaseRepositoryModule
9 | import network.di.apiModule
10 | import network.di.repositoryModule
11 | import network.di.serviceModule
12 | import org.koin.android.ext.koin.androidContext
13 | import org.koin.core.context.startKoin
14 |
15 | actual class KoinInitializer(
16 | private val context: Context,
17 | ) {
18 | actual fun init() {
19 | startKoin {
20 | androidContext(context)
21 | modules(
22 | interactorModule,
23 | viewModelModule,
24 | databaseModule(),
25 | daoModule,
26 | databaseRepositoryModule,
27 | apiModule,
28 | serviceModule,
29 | repositoryModule,
30 | )
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/domain/models/util/DataLoadStatus.kt:
--------------------------------------------------------------------------------
1 | package common.domain.models.util
2 |
3 | sealed class DataLoadStatus {
4 | data object Empty : DataLoadStatus()
5 |
6 | data object Loading : DataLoadStatus()
7 |
8 | data object Success : DataLoadStatus()
9 |
10 | data object Failed : DataLoadStatus()
11 | }
12 |
13 | open class LoadStatusState {
14 | var loadStatus: DataLoadStatus = DataLoadStatus.Loading
15 | var errorCode: String? = null
16 |
17 | fun setSuccess() {
18 | this.loadStatus = DataLoadStatus.Success
19 | }
20 |
21 | fun setError(errorCode: String?) {
22 | this.loadStatus = DataLoadStatus.Failed
23 | this.errorCode = errorCode
24 | }
25 |
26 | fun setLoading() {
27 | this.loadStatus = DataLoadStatus.Loading
28 | }
29 |
30 | fun isLoaded(): Boolean = this.loadStatus == DataLoadStatus.Success
31 |
32 | fun isLoading(): Boolean = this.loadStatus == DataLoadStatus.Loading
33 |
34 | fun isFailed(): Boolean = this.loadStatus == DataLoadStatus.Failed
35 | }
36 |
--------------------------------------------------------------------------------
/composeApp/src/androidMain/kotlin/common/util/platform/StringFormat.kt:
--------------------------------------------------------------------------------
1 | package common.util.platform
2 |
3 | import java.text.DecimalFormat
4 | import java.text.DecimalFormatSymbols
5 | import java.text.NumberFormat
6 | import java.util.Locale
7 |
8 | actual object StringFormat {
9 | actual fun formatRating(number: Double): String {
10 | val symbols = DecimalFormatSymbols.getInstance().apply {
11 | decimalSeparator = '.'
12 | }
13 | var formattedRating = DecimalFormat("#.#", symbols).format(number)
14 | if (formattedRating.length == 1) {
15 | formattedRating += "${symbols.decimalSeparator}0"
16 | }
17 | return formattedRating
18 | }
19 |
20 | actual fun Long.toFormattedCurrency(): String {
21 | return try {
22 | val decimalFormat = (NumberFormat.getCurrencyInstance(Locale.US)).apply {
23 | maximumFractionDigits = 0
24 | }
25 | decimalFormat.format(this)
26 | } catch (e: IllegalArgumentException) {
27 | ""
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/domain/models/person/PersonDetails.kt:
--------------------------------------------------------------------------------
1 | package common.domain.models.person
2 |
3 | import common.domain.models.util.MediaType
4 | import network.models.content.common.MultiResponse
5 | import network.models.content.common.PersonResponse
6 |
7 | data class PersonDetails(
8 | val id: Int,
9 | val title: String,
10 | val overview: String,
11 | val posterPath: String,
12 | val mediaType: MediaType,
13 | val birthday: String?,
14 | val deathday: String?,
15 | val placeOfBirth: String?,
16 | val knownForDepartment: String?,
17 | val knownFor: List,
18 | )
19 |
20 | fun PersonResponse.toPersonDetails(): PersonDetails =
21 | PersonDetails(
22 | id = this.id,
23 | title = this.name.orEmpty(),
24 | overview = this.overview.orEmpty(),
25 | posterPath = this.profile_path.orEmpty(),
26 | mediaType = MediaType.PERSON,
27 | birthday = null,
28 | deathday = null,
29 | placeOfBirth = null,
30 | knownForDepartment = this.known_for_department,
31 | knownFor = this.known_for.orEmpty(),
32 | )
33 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/features/details/DetailsScreen.kt:
--------------------------------------------------------------------------------
1 | package features.details
2 |
3 | import androidx.navigation.NamedNavArgument
4 | import androidx.navigation.NavType
5 | import androidx.navigation.navArgument
6 | import navigation.Screen
7 |
8 | object DetailsScreen : Screen {
9 | private const val DETAILS_ROUTE = "details"
10 | const val ARG_CONTENT_ID = "contentId"
11 | const val ARG_MEDIA_TYPE = "mediaType"
12 | private const val FULL_DETAILS_ROUTE =
13 | "$DETAILS_ROUTE/{$ARG_CONTENT_ID}?$ARG_MEDIA_TYPE={$ARG_MEDIA_TYPE}"
14 | override fun route(): String = FULL_DETAILS_ROUTE
15 |
16 | override val arguments: List
17 | get() = listOf(
18 | navArgument(ARG_CONTENT_ID) {
19 | type = NavType.IntType
20 | },
21 | )
22 |
23 | fun routeWithArguments(
24 | contentId: Int,
25 | mediaType: String,
26 | ): String {
27 | return buildString {
28 | append(DETAILS_ROUTE)
29 | append("/$contentId?")
30 | append("$ARG_MEDIA_TYPE=$mediaType")
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/features/details/util/Extensions.kt:
--------------------------------------------------------------------------------
1 | package features.details.util
2 |
3 | import androidx.compose.runtime.Composable
4 | import common.util.platform.StringFormat.toFormattedCurrency
5 |
6 | @Composable
7 | fun Int.formatRuntime(): String {
8 | val runtimeHours: Int = this / 60
9 | val runtimeMinutes = this % 60
10 |
11 | return if (runtimeHours != 0 && runtimeMinutes != 0) {
12 | "${runtimeHours}h ${runtimeMinutes}m"
13 | } else if (runtimeHours != 0) {
14 | "${runtimeHours}h"
15 | } else {
16 | "${runtimeMinutes}m"
17 | }
18 | }
19 |
20 | /**
21 | * Function to convert header position into a value between 0.0 and 1.0.
22 | * Used to define the background image alpha based on the header position.
23 | */
24 | fun Float.mapValueToRange(initialHeaderPosY: Float): Float {
25 | val maxValue = 0f
26 | val mappedValue = (this - initialHeaderPosY) / (maxValue - initialHeaderPosY)
27 | return (1.0f - mappedValue).coerceIn(0f, 1f)
28 | }
29 |
30 | fun Long?.isValidValue(): Boolean {
31 | return this != null && this > 0
32 | }
33 |
34 | fun Long.toFormattedCurrency(): String {
35 | return this.toFormattedCurrency()
36 | }
37 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/repository/search/SearchRepository.kt:
--------------------------------------------------------------------------------
1 | package network.repository.search
2 |
3 | import kotlinx.coroutines.flow.Flow
4 | import network.models.ApiError
5 | import network.models.content.common.MovieResponse
6 | import network.models.content.common.MultiResponse
7 | import network.models.content.common.PersonResponse
8 | import network.models.content.common.ShowResponse
9 | import network.models.content.search.ContentPagingResponse
10 | import network.util.Either
11 |
12 | interface SearchRepository {
13 | suspend fun onSearchMultiByQuery(
14 | query: String,
15 | page: Int,
16 | ): Flow, ApiError>>
17 |
18 | suspend fun onSearchMovieByQuery(
19 | query: String,
20 | page: Int,
21 | ): Flow, ApiError>>
22 |
23 | suspend fun onSearchShowByQuery(
24 | query: String,
25 | page: Int,
26 | ): Flow, ApiError>>
27 |
28 | suspend fun onSearchPersonByQuery(
29 | query: String,
30 | page: Int,
31 | ): Flow, ApiError>>
32 | }
33 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/navigation/screens/BrowseScreenUI.kt:
--------------------------------------------------------------------------------
1 | package navigation.screens
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.getValue
5 | import androidx.navigation.NavController
6 | import androidx.navigation.compose.currentBackStackEntryAsState
7 | import common.ui.screen.ErrorScreen
8 | import features.browse.ui.Browse
9 | import features.details.DetailsScreen
10 | import navigation.ScreenUI
11 |
12 | class BrowseScreenUI : ScreenUI {
13 | @Composable
14 | override fun UI(navController: NavController) {
15 | val currentBackStackEntry by navController.currentBackStackEntryAsState()
16 | val currentScreen = currentBackStackEntry?.destination?.route
17 |
18 | Browse(
19 | goToDetails = { contentId, mediaType ->
20 | navController.navigate(
21 | DetailsScreen.routeWithArguments(contentId, mediaType.name),
22 | )
23 | },
24 | goToErrorScreen = {
25 | if (currentScreen != ErrorScreen.route()) {
26 | navController.navigate(ErrorScreen.route())
27 | }
28 | },
29 | )
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/navigation/screens/SearchScreenUI.kt:
--------------------------------------------------------------------------------
1 | package navigation.screens
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.getValue
5 | import androidx.navigation.NavController
6 | import androidx.navigation.compose.currentBackStackEntryAsState
7 | import common.ui.screen.ErrorScreen
8 | import features.details.DetailsScreen
9 | import features.search.ui.Search
10 | import navigation.ScreenUI
11 |
12 | class SearchScreenUI : ScreenUI {
13 | @Composable
14 | override fun UI(navController: NavController) {
15 | val currentBackStackEntry by navController.currentBackStackEntryAsState()
16 | val currentScreen = currentBackStackEntry?.destination?.route
17 |
18 | Search(
19 | goToDetails = { contentId, mediaType ->
20 | navController.navigate(
21 | DetailsScreen.routeWithArguments(contentId, mediaType.name),
22 | )
23 | },
24 | goToErrorScreen = {
25 | if (currentScreen != ErrorScreen.route()) {
26 | navController.navigate(ErrorScreen.route())
27 | }
28 | },
29 | )
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/features/browse/domain/BrowseInteractor.kt:
--------------------------------------------------------------------------------
1 | package features.browse.domain
2 |
3 | import androidx.paging.Pager
4 | import androidx.paging.PagingConfig
5 | import androidx.paging.PagingData
6 | import common.domain.models.content.GenericContent
7 | import common.domain.models.util.ContentListType
8 | import common.domain.models.util.MediaType
9 | import common.util.Constants.PAGE_SIZE
10 | import features.browse.ui.paging.MediaContentPagingSource
11 | import kotlinx.coroutines.flow.Flow
12 | import network.repository.movie.MovieRepository
13 | import network.repository.show.ShowRepository
14 |
15 | class BrowseInteractor(
16 | private val movieRepository: MovieRepository,
17 | private val showRepository: ShowRepository,
18 | ) {
19 | fun getMediaContentListPager(
20 | contentListType: ContentListType,
21 | mediaType: MediaType,
22 | ): Flow> {
23 | return Pager(PagingConfig(pageSize = PAGE_SIZE)) {
24 | MediaContentPagingSource(
25 | movieRepository,
26 | showRepository,
27 | contentListType,
28 | mediaType,
29 | )
30 | }.flow
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/navigation/screens/WatchlistScreenUI.kt:
--------------------------------------------------------------------------------
1 | package navigation.screens
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.getValue
5 | import androidx.navigation.NavController
6 | import androidx.navigation.compose.currentBackStackEntryAsState
7 | import common.ui.screen.ErrorScreen
8 | import features.details.DetailsScreen
9 | import features.watchlist.ui.Watchlist
10 | import navigation.ScreenUI
11 |
12 | class WatchlistScreenUI : ScreenUI {
13 | @Composable
14 | override fun UI(navController: NavController) {
15 | val currentBackStackEntry by navController.currentBackStackEntryAsState()
16 | val currentScreen = currentBackStackEntry?.destination?.route
17 |
18 | Watchlist(
19 | goToDetails = { contentId, mediaType ->
20 | navController.navigate(
21 | DetailsScreen.routeWithArguments(contentId, mediaType.name),
22 | )
23 | },
24 | goToErrorScreen = {
25 | if (currentScreen != ErrorScreen.route()) {
26 | navController.navigate(ErrorScreen.route())
27 | }
28 | },
29 | )
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/composeApp/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.kts.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
23 | -dontwarn org.slf4j.impl.StaticLoggerBinder
24 |
25 | # With R8 full mode generic signatures are stripped for classes that are not
26 | # kept. Suspend functions are wrapped in continuations where the type argument
27 | # is used.
28 | -keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
29 |
30 | -keep class com.projects.moviemanager.network.models.** { *; }
--------------------------------------------------------------------------------
/composeApp/src/iosMain/kotlin/common/util/platform/DateUtils.kt:
--------------------------------------------------------------------------------
1 | package common.util.platform
2 |
3 | import platform.Foundation.NSCalendar
4 | import platform.Foundation.NSCalendarUnitMonth
5 | import platform.Foundation.NSDate
6 | import platform.Foundation.NSDateFormatter
7 | import platform.Foundation.NSLocale
8 | import platform.Foundation.currentLocale
9 | import platform.Foundation.timeIntervalSince1970
10 |
11 | actual object DateUtils {
12 | actual fun getComingSoonDates(
13 | monthPeriod: Int,
14 | ): Pair {
15 | val calendar = NSCalendar.currentCalendar
16 |
17 | val dateFormatter = NSDateFormatter().apply {
18 | dateFormat = "yyyy-MM-dd"
19 | locale = NSLocale.currentLocale
20 | }
21 | val releaseDateGte = dateFormatter.stringFromDate(NSDate())
22 | val releaseDateLte = calendar.dateByAddingUnit(
23 | NSCalendarUnitMonth, monthPeriod.toLong(), NSDate(), 0u,
24 | )?.let {
25 | dateFormatter.stringFromDate(it)
26 | } ?: releaseDateGte
27 | return Pair(releaseDateGte, releaseDateLte)
28 | }
29 |
30 | actual fun getCurrentTimeMillis(): Long = (NSDate().timeIntervalSince1970 * 1000).toLong()
31 | }
32 |
--------------------------------------------------------------------------------
/composeApp/src/androidMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
14 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/ui/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | package common.ui.theme
2 |
3 | import androidx.compose.foundation.isSystemInDarkTheme
4 | import androidx.compose.material3.MaterialTheme
5 | import androidx.compose.material3.darkColorScheme
6 | import androidx.compose.runtime.Composable
7 |
8 | private val DarkColorPalette =
9 | darkColorScheme(
10 | primary = PrimaryBlackColor,
11 | onPrimary = PrimaryWhiteColor,
12 | secondary = PrimaryYellowColor,
13 | onSecondary = PrimaryBlackColor,
14 | tertiary = PrimaryGreyColor,
15 | surface = SecondaryGreyColor,
16 | onSurface = PrimaryWhiteColor,
17 | onSurfaceVariant = PrimaryYellowColor,
18 | inverseSurface = DividerGrey,
19 | surfaceVariant = PrimaryGreyColor_55,
20 | background = PrimaryBlackColor,
21 | error = PrimaryRedColor,
22 | )
23 |
24 | @Composable
25 | fun CineTrackerTheme(
26 | darkTheme: Boolean = isSystemInDarkTheme(),
27 | content:
28 | @Composable()
29 | () -> Unit,
30 | ) {
31 | MaterialTheme(
32 | colorScheme = DarkColorPalette,
33 | typography = AppTypography,
34 | shapes = RoundCornerShapes,
35 | content = content,
36 | )
37 | }
38 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/repository/person/PersonRepositoryImpl.kt:
--------------------------------------------------------------------------------
1 | package network.repository.person
2 |
3 | import kotlinx.coroutines.flow.Flow
4 | import network.models.ApiError
5 | import network.models.content.common.PersonResponse
6 | import network.models.content.person.PersonCreditsResponse
7 | import network.models.content.person.PersonImagesResponse
8 | import network.services.person.PersonService
9 | import network.util.Either
10 | import network.util.asFlow
11 |
12 | class PersonRepositoryImpl(
13 | private val personService: PersonService,
14 | ) : PersonRepository {
15 | override suspend fun getPersonDetailsById(
16 | personId: Int,
17 | ): Flow> {
18 | return personService.getPersonDetailsById(personId).asFlow()
19 | }
20 |
21 | override suspend fun getPersonCreditsById(
22 | personId: Int,
23 | ): Flow> {
24 | return personService.getPersonCreditsById(personId).asFlow()
25 | }
26 |
27 | override suspend fun getPersonImagesById(
28 | personId: Int,
29 | ): Flow> {
30 | return personService.getPersonImagesById(personId).asFlow()
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/composeApp/src/iosMain/kotlin/common/util/platform/StringFormat.kt:
--------------------------------------------------------------------------------
1 | package common.util.platform
2 |
3 | import platform.Foundation.NSLocale
4 | import platform.Foundation.NSNumber
5 | import platform.Foundation.NSNumberFormatter
6 | import platform.Foundation.NSNumberFormatterCurrencyStyle
7 | import platform.Foundation.localeWithLocaleIdentifier
8 |
9 | actual object StringFormat {
10 | actual fun formatRating(number: Double): String {
11 | val formatter = NSNumberFormatter()
12 | formatter.minimumFractionDigits = 1u
13 | formatter.maximumFractionDigits = 1u
14 | formatter.numberStyle = 1u
15 | return formatter.stringFromNumber(NSNumber(number))!!
16 | }
17 |
18 | actual fun Long.toFormattedCurrency(): String {
19 | return try {
20 | val numberFormatter = NSNumberFormatter().apply {
21 | numberStyle = NSNumberFormatterCurrencyStyle
22 | locale = NSLocale.localeWithLocaleIdentifier("en_US")
23 | maximumFractionDigits = 0u
24 | }
25 | val nsNumber = NSNumber(this.toDouble())
26 | numberFormatter.stringFromNumber(nsNumber) ?: ""
27 | } catch (e: Exception) {
28 | ""
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/ui/components/button/GenericButton.kt:
--------------------------------------------------------------------------------
1 | package common.ui.components.button
2 |
3 | import androidx.compose.foundation.shape.RoundedCornerShape
4 | import androidx.compose.material3.Button
5 | import androidx.compose.material3.ButtonDefaults
6 | import androidx.compose.material3.MaterialTheme
7 | import androidx.compose.material3.Text
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.unit.dp
11 | import common.util.UiConstants.CLASSIC_BUTTON_BORDER_SIZE
12 |
13 | @Composable
14 | fun GenericButton(
15 | modifier: Modifier = Modifier,
16 | buttonText: String,
17 | enabled: Boolean = true,
18 | onClick: () -> Unit,
19 | ) {
20 | Button(
21 | modifier = modifier,
22 | onClick = onClick,
23 | colors = ButtonDefaults.buttonColors(
24 | containerColor = MaterialTheme.colorScheme.secondary,
25 | ),
26 | shape = RoundedCornerShape(CLASSIC_BUTTON_BORDER_SIZE.dp),
27 | enabled = enabled,
28 | ) {
29 | Text(
30 | text = buttonText,
31 | style = MaterialTheme.typography.titleMedium,
32 | color = MaterialTheme.colorScheme.primary,
33 | )
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/core/LanguageManager.kt:
--------------------------------------------------------------------------------
1 | package core
2 |
3 | import common.util.platform.PlatformUtils.getLocale
4 | import common.util.platform.PlatformUtils.getUserCountry
5 | import common.util.platform.PlatformUtils.getUserLanguage
6 |
7 | object LanguageManager {
8 | private val supportedLanguages = listOf(
9 | "en-US",
10 | "en-CA",
11 | "pt-BR",
12 | "es-ES",
13 | "es-MX",
14 | "es-BO",
15 | "es-CL",
16 | "es-CO",
17 | "es-EC",
18 | "es-PY",
19 | "es-PE",
20 | "es-PR",
21 | "es-UY",
22 | "es-VE",
23 | "es-CR",
24 | )
25 | private const val DEFAULT_LANGUAGE = "en-US"
26 |
27 | fun getUserLanguageTag(): String {
28 | val languageTag = getLocale()
29 | val language = getUserLanguage()
30 |
31 | return when {
32 | languageTag.isSupported() -> languageTag
33 | language == "pt" -> "pt-BR"
34 | language == "es" -> "es-ES"
35 | else -> DEFAULT_LANGUAGE
36 | }
37 | }
38 |
39 | private fun String.isSupported(): Boolean {
40 | return supportedLanguages.contains(this)
41 | }
42 |
43 | fun getUserCountryCode(): String = getUserCountry()
44 | }
45 |
--------------------------------------------------------------------------------
/iosApp/iosApp/ContentView.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import SwiftUI
3 | import ComposeApp
4 |
5 | struct ComposeView: UIViewControllerRepresentable {
6 | func makeUIViewController(context: Context) -> UIViewController {
7 | MainViewControllerKt.MainViewController()
8 | }
9 |
10 | func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
11 | }
12 |
13 | struct ContentView: View {
14 | var statusBarHeight = {
15 | return UIApplication.shared.windows.first?.safeAreaInsets.top
16 | }
17 | var systemNavBarHeight = {
18 | return UIApplication.shared.windows.first?.safeAreaInsets.bottom
19 | }
20 |
21 | var body: some View {
22 | GeometryReader { geometry in
23 | VStack(spacing: 0) {
24 | Color.black
25 | .frame(height: geometry.size.height / 2)
26 | Color(hex: 0x191A1D)
27 | }
28 | .ignoresSafeArea()
29 |
30 | ComposeView()
31 | .padding(.bottom, systemNavBarHeight())
32 | .padding(.top, statusBarHeight())
33 | .ignoresSafeArea(edges: .all)
34 | .ignoresSafeArea(.keyboard) // Compose has own keyboard handler
35 | }
36 | }
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/database/dao/ContentEntityDao.kt:
--------------------------------------------------------------------------------
1 | package database.dao
2 |
3 | import androidx.room.Dao
4 | import androidx.room.Insert
5 | import androidx.room.OnConflictStrategy
6 | import androidx.room.Query
7 | import database.model.ContentEntity
8 |
9 | @Dao
10 | interface ContentEntityDao {
11 | @Query("SELECT * FROM content_entity WHERE listId = :listId ORDER BY createdAt DESC")
12 | suspend fun getAllItems(listId: Int): List
13 |
14 | @Insert(onConflict = OnConflictStrategy.IGNORE)
15 | suspend fun insert(contentEntity: ContentEntity)
16 |
17 | @Query(
18 | "DELETE FROM content_entity " +
19 | "WHERE contentId=:contentId AND mediaType = :mediaType AND listId = :listId",
20 | )
21 | suspend fun delete(contentId: Int, mediaType: String, listId: Int)
22 |
23 | @Query(
24 | "SELECT * FROM content_entity WHERE contentId = :contentId AND mediaType = :mediaType",
25 | )
26 | suspend fun searchItems(contentId: Int, mediaType: String): List
27 |
28 | @Query(
29 | "SELECT * FROM content_entity WHERE " +
30 | "contentId = :contentId AND mediaType = :mediaType AND listId = :listId",
31 | )
32 | suspend fun getItem(contentId: Int, mediaType: String, listId: Int): ContentEntity?
33 | }
34 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/features/search/ui/components/SearchTypeFilterItem.kt:
--------------------------------------------------------------------------------
1 | package features.search.ui.components
2 |
3 | import cinetracker_kmp.composeapp.generated.resources.Res
4 | import cinetracker_kmp.composeapp.generated.resources.search_movies_tab
5 | import cinetracker_kmp.composeapp.generated.resources.search_person_tab
6 | import cinetracker_kmp.composeapp.generated.resources.search_shows_tab
7 | import cinetracker_kmp.composeapp.generated.resources.search_top_results_tab
8 | import common.domain.models.util.MediaType
9 | import org.jetbrains.compose.resources.StringResource
10 |
11 | sealed class SearchTypeFilterItem(
12 | val tabResId: StringResource,
13 | val mediaType: MediaType?,
14 | ) {
15 | data object TopResults : SearchTypeFilterItem(
16 | tabResId = Res.string.search_top_results_tab,
17 | mediaType = null,
18 | )
19 | data object Movies : SearchTypeFilterItem(
20 | tabResId = Res.string.search_movies_tab,
21 | mediaType = MediaType.MOVIE,
22 | )
23 | data object Shows : SearchTypeFilterItem(
24 | tabResId = Res.string.search_shows_tab,
25 | mediaType = MediaType.SHOW,
26 | )
27 | data object Person : SearchTypeFilterItem(
28 | tabResId = Res.string.search_person_tab,
29 | mediaType = MediaType.PERSON,
30 | )
31 | }
32 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/ui/components/NetworkImage.kt:
--------------------------------------------------------------------------------
1 | package common.ui.components
2 |
3 | import androidx.compose.foundation.layout.Box
4 | import androidx.compose.foundation.layout.height
5 | import androidx.compose.foundation.layout.size
6 | import androidx.compose.foundation.layout.width
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.Modifier
9 | import androidx.compose.ui.graphics.DefaultAlpha
10 | import androidx.compose.ui.layout.ContentScale
11 | import androidx.compose.ui.unit.Dp
12 | import coil3.compose.SubcomposeAsyncImage
13 |
14 | @Composable
15 | fun NetworkImage(
16 | modifier: Modifier = Modifier,
17 | imageUrl: String,
18 | widthDp: Dp = Dp.Unspecified,
19 | heightDp: Dp = Dp.Unspecified,
20 | contentScale: ContentScale = ContentScale.Crop,
21 | alpha: Float = DefaultAlpha,
22 | ) {
23 | Box {
24 | SubcomposeAsyncImage(
25 | modifier = modifier.size(widthDp, heightDp),
26 | model = imageUrl,
27 | contentDescription = null,
28 | contentScale = contentScale,
29 | alpha = alpha,
30 | loading = {
31 | ComponentPlaceholder(
32 | modifier = modifier.width(widthDp).height(heightDp),
33 | )
34 | },
35 | )
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | rootProject.name = "CineTracker-KMP"
2 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
3 |
4 | pluginManagement {
5 | repositories {
6 | google {
7 | mavenContent {
8 | includeGroupAndSubgroups("androidx")
9 | includeGroupAndSubgroups("com.android")
10 | includeGroupAndSubgroups("com.google")
11 | }
12 | }
13 | mavenCentral()
14 | gradlePluginPortal()
15 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
16 | maven("https://androidx.dev/storage/compose-compiler/repository")
17 | maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev")
18 | }
19 | }
20 |
21 | dependencyResolutionManagement {
22 | repositories {
23 | google {
24 | mavenContent {
25 | includeGroupAndSubgroups("androidx")
26 | includeGroupAndSubgroups("com.android")
27 | includeGroupAndSubgroups("com.google")
28 | }
29 | }
30 | mavenCentral()
31 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
32 | maven("https://androidx.dev/storage/compose-compiler/repository")
33 | maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev")
34 | }
35 | }
36 |
37 | include(":composeApp")
38 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/repository/home/HomeRepositoryImpl.kt:
--------------------------------------------------------------------------------
1 | package network.repository.home
2 |
3 | import kotlinx.coroutines.flow.Flow
4 | import network.models.ApiError
5 | import network.models.content.common.MovieResponse
6 | import network.models.content.common.MultiResponse
7 | import network.models.content.common.PersonResponse
8 | import network.models.content.search.ContentPagingResponse
9 | import network.services.home.HomeService
10 | import network.util.Either
11 | import network.util.asFlow
12 |
13 | class HomeRepositoryImpl(
14 | private val homeService: HomeService,
15 | ) : HomeRepository {
16 | override suspend fun getTrendingMulti(): Flow, ApiError>> {
17 | return homeService.getDayTrendingMulti().asFlow()
18 | }
19 |
20 | override suspend fun getTrendingPerson(): Flow, ApiError>> {
21 | return homeService.getDayTrendingPerson().asFlow()
22 | }
23 |
24 | override suspend fun getMoviesComingSoon(
25 | releaseDateGte: String,
26 | releaseDateLte: String,
27 | ): Flow, ApiError>> {
28 | return homeService.getMoviesComingSoon(
29 | releaseDateGte = releaseDateGte,
30 | releaseDateLte = releaseDateLte,
31 | ).asFlow()
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/iosApp/iosApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CADisableMinimumFrameDurationOnPhone
6 |
7 | CFBundleDevelopmentRegion
8 | $(DEVELOPMENT_LANGUAGE)
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UIApplicationSceneManifest
26 |
27 | UIApplicationSupportsMultipleScenes
28 |
29 |
30 | UILaunchScreen
31 |
32 | UIRequiredDeviceCapabilities
33 |
34 | armv7
35 |
36 | UIRequiresFullScreen
37 |
38 | UISupportedInterfaceOrientations
39 |
40 | UIInterfaceOrientationPortrait
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/ui/components/popup/GenericPopupMenu.kt:
--------------------------------------------------------------------------------
1 | package common.ui.components.popup
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.material3.DropdownMenu
5 | import androidx.compose.material3.DropdownMenuItem
6 | import androidx.compose.material3.MaterialTheme
7 | import androidx.compose.material3.Text
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.graphics.Color
11 |
12 | @Composable
13 | fun GenericPopupMenu(
14 | showMenu: Boolean,
15 | backgroundColor: Color = MaterialTheme.colorScheme.primary,
16 | onDismissRequest: () -> Unit,
17 | menuItems: List,
18 | ) {
19 | DropdownMenu(
20 | expanded = showMenu,
21 | onDismissRequest = onDismissRequest,
22 | modifier = Modifier.background(color = backgroundColor),
23 | ) {
24 | menuItems.forEach { menuItem ->
25 | DropdownMenuItem(
26 | text = {
27 | Text(
28 | text = menuItem.title,
29 | color = menuItem.textColor,
30 | style = MaterialTheme.typography.bodySmall,
31 | )
32 | },
33 | onClick = {
34 | menuItem.onClick()
35 | onDismissRequest()
36 | },
37 | )
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/features/details/ui/components/moreoptions/MoreOptionsTabItem.kt:
--------------------------------------------------------------------------------
1 | package features.details.ui.components.moreoptions
2 |
3 | import cinetracker_kmp.composeapp.generated.resources.Res
4 | import cinetracker_kmp.composeapp.generated.resources.images_tab
5 | import cinetracker_kmp.composeapp.generated.resources.more_options_similar
6 | import cinetracker_kmp.composeapp.generated.resources.more_options_videos
7 | import cinetracker_kmp.composeapp.generated.resources.movies_tab
8 | import cinetracker_kmp.composeapp.generated.resources.shows_tab
9 | import common.ui.components.tab.TabItem
10 | import common.util.Constants.UNSELECTED_OPTION_INDEX
11 | import org.jetbrains.compose.resources.StringResource
12 |
13 | sealed class MoreOptionsTabItem(
14 | override val tabResId: StringResource,
15 | override val tabName: String? = "",
16 | override var tabIndex: Int = UNSELECTED_OPTION_INDEX,
17 | ) : TabItem {
18 | data object VideosTab : MoreOptionsTabItem(
19 | tabResId = Res.string.more_options_videos,
20 | )
21 | data object MoreLikeThisTab : MoreOptionsTabItem(
22 | tabResId = Res.string.more_options_similar,
23 | )
24 | data object MoviesTab : MoreOptionsTabItem(
25 | tabResId = Res.string.movies_tab,
26 | )
27 | data object ShowsTab : MoreOptionsTabItem(
28 | tabResId = Res.string.shows_tab,
29 | )
30 | data object ImagesTab : MoreOptionsTabItem(
31 | tabResId = Res.string.images_tab,
32 | )
33 | }
34 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/features/watchlist/ui/components/ListRemovePopupMenu.kt:
--------------------------------------------------------------------------------
1 | package features.watchlist.ui.components
2 |
3 | import androidx.compose.foundation.layout.Box
4 | import androidx.compose.foundation.layout.absoluteOffset
5 | import androidx.compose.runtime.Composable
6 | import androidx.compose.ui.Modifier
7 | import androidx.compose.ui.geometry.Offset
8 | import androidx.compose.ui.unit.dp
9 | import cinetracker_kmp.composeapp.generated.resources.Res
10 | import cinetracker_kmp.composeapp.generated.resources.delete_list_pop_up_item
11 | import common.ui.components.popup.GenericPopupMenu
12 | import common.ui.components.popup.PopupMenuItem
13 | import common.ui.theme.MainBarGreyColor
14 | import org.jetbrains.compose.resources.stringResource
15 |
16 | @Composable
17 | fun ListRemovePopUpMenu(
18 | showRemoveMenu: Boolean,
19 | menuOffset: Offset,
20 | onRemoveList: () -> Unit,
21 | onDismiss: () -> Unit,
22 | ) {
23 | val menuItems = listOf(
24 | PopupMenuItem(
25 | stringResource(resource = Res.string.delete_list_pop_up_item),
26 | onClick = onRemoveList,
27 | ),
28 | )
29 | Box(
30 | modifier = Modifier
31 | .absoluteOffset(x = (menuOffset.x / 2).dp),
32 | ) {
33 | GenericPopupMenu(
34 | showMenu = showRemoveMenu,
35 | backgroundColor = MainBarGreyColor,
36 | onDismissRequest = onDismiss,
37 | menuItems = menuItems,
38 | )
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/features/watchlist/ui/model/DefaultLists.kt:
--------------------------------------------------------------------------------
1 | package features.watchlist.ui.model
2 |
3 | import cinetracker_kmp.composeapp.generated.resources.Res
4 | import cinetracker_kmp.composeapp.generated.resources.unknown
5 | import cinetracker_kmp.composeapp.generated.resources.watched_tab
6 | import cinetracker_kmp.composeapp.generated.resources.watchlist_tab
7 | import common.util.Constants.ADD_NEW_TAB_ID
8 | import common.util.capitalized
9 | import org.jetbrains.compose.resources.StringResource
10 |
11 | enum class DefaultLists(val listId: Int) {
12 | WATCHLIST(1),
13 | WATCHED(2),
14 | ADD_NEW(ADD_NEW_TAB_ID),
15 | ;
16 |
17 | override fun toString(): String {
18 | return super.toString().lowercase().capitalized()
19 | }
20 | companion object {
21 | fun getListById(listId: Int): DefaultLists? {
22 | return values().firstOrNull { it.listId == listId }
23 | }
24 | fun getOtherList(listId: Int): DefaultLists {
25 | return when (listId) {
26 | WATCHLIST.listId -> WATCHED
27 | else -> WATCHLIST
28 | }
29 | }
30 |
31 | fun getListLocalizedName(
32 | list: DefaultLists?,
33 | ): StringResource {
34 | return when (list) {
35 | WATCHLIST -> Res.string.watchlist_tab
36 | WATCHED -> Res.string.watched_tab
37 | else -> Res.string.unknown
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/repository/show/ShowRepository.kt:
--------------------------------------------------------------------------------
1 | package network.repository.show
2 |
3 | import kotlinx.coroutines.flow.Flow
4 | import network.models.ApiError
5 | import network.models.content.common.ContentCreditsResponse
6 | import network.models.content.common.ShowResponse
7 | import network.models.content.common.VideosByIdResponse
8 | import network.models.content.common.WatchProvidersResponse
9 | import network.models.content.search.ContentPagingResponse
10 | import network.util.Either
11 |
12 | interface ShowRepository {
13 | suspend fun getShowList(
14 | contentListType: String,
15 | pageIndex: Int,
16 | ): Flow, ApiError>>
17 |
18 | suspend fun getShowDetailsById(
19 | showId: Int,
20 | ): Flow>
21 |
22 | suspend fun getShowCreditsById(
23 | showId: Int,
24 | ): Flow>
25 |
26 | suspend fun getShowVideosById(
27 | showId: Int,
28 | ): Flow>
29 |
30 | suspend fun getRecommendationsShowsById(
31 | showId: Int,
32 | ): Flow, ApiError>>
33 |
34 | suspend fun getSimilarShowsById(
35 | showId: Int,
36 | ): Flow, ApiError>>
37 |
38 | suspend fun getStreamingProviders(
39 | showId: Int,
40 | ): Flow>
41 | }
42 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/navigation/screens/DetailsScreenUI.kt:
--------------------------------------------------------------------------------
1 | package navigation.screens
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.getValue
5 | import androidx.compose.runtime.remember
6 | import androidx.navigation.NavController
7 | import androidx.navigation.compose.currentBackStackEntryAsState
8 | import common.ui.screen.ErrorScreen
9 | import features.details.DetailsScreen
10 | import features.details.ui.Details
11 | import navigation.ScreenUI
12 |
13 | class DetailsScreenUI : ScreenUI {
14 | @Composable
15 | override fun UI(navController: NavController) {
16 | val currentBackStackEntry by navController.currentBackStackEntryAsState()
17 | val currentScreen = currentBackStackEntry?.destination?.route
18 |
19 | val backStackEntry = remember {
20 | navController.getBackStackEntry(DetailsScreen.route())
21 | }
22 |
23 | Details(
24 | navBackStackEntry = backStackEntry,
25 | onBackPress = {
26 | navController.popBackStack()
27 | },
28 | goToDetails = { id, type ->
29 | navController.navigate(
30 | DetailsScreen.routeWithArguments(id, type.name),
31 | )
32 | },
33 | goToErrorScreen = {
34 | if (currentScreen != ErrorScreen.route()) {
35 | navController.navigate(ErrorScreen.route())
36 | }
37 | },
38 | )
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled source #
2 | ###################
3 | *.com
4 | *.class
5 | *.dll
6 | *.exe
7 | *.o
8 | *.so
9 |
10 | # Packages #
11 | ############
12 | # it's better to unpack these files and commit the raw source
13 | # git has its own built in compression methods
14 | *.7z
15 | *.dmg
16 | *.gz
17 | *.iso
18 | *.jar
19 | *.rar
20 | *.tar
21 | *.zip
22 |
23 | # Logs and databases #
24 | ######################
25 | *.log
26 | *.sql
27 | *.sqlite
28 |
29 | # OS generated files #
30 | ######################
31 | .DS_Store
32 | .DS_Store?
33 | ._*
34 | .Spotlight-V100
35 | .Trashes
36 | ehthumbs.db
37 | Thumbs.db
38 |
39 | # Built application files
40 | *.apk
41 | *ap_
42 | app/release
43 |
44 | # Gradle files
45 | .gradle/
46 | build/
47 |
48 | # Local configuration file (sdk path, etc)
49 | local.properties
50 |
51 | # Proguard folder generated by Eclipse
52 | proguard/
53 |
54 | # Log Files
55 | .log
56 |
57 | # Android Studio Navigation editor temp files
58 | .navigation/
59 |
60 | # Android Studio captures folder
61 | captures/
62 |
63 | # release
64 | composeApp/release/
65 |
66 | # IntelliJ
67 | .idea/
68 |
69 | # External native build folder generated in Android Studio 2.2 and later
70 | .externalNativeBuild
71 |
72 | # KMP
73 | *.iml
74 | .kotlin
75 | **/build/
76 | xcuserdata
77 | !src/**/build/
78 | .cxx
79 | *.xcodeproj/*
80 | !*.xcodeproj/project.pbxproj
81 | !*.xcodeproj/xcshareddata/
82 | !*.xcodeproj/project.xcworkspace/
83 | !*.xcworkspace/contents.xcworkspacedata
84 | **/xcshareddata/WorkspaceSettings.xcsettings
85 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/services/search/SearchService.kt:
--------------------------------------------------------------------------------
1 | package network.services.search
2 |
3 | import core.LanguageManager
4 | import network.models.content.common.MovieResponse
5 | import network.models.content.common.MultiResponse
6 | import network.models.content.common.PersonResponse
7 | import network.models.content.common.ShowResponse
8 | import network.models.content.search.ContentPagingResponse
9 | import network.util.ApiResult
10 |
11 | interface SearchService {
12 | suspend fun searchMultiByQuery(
13 | query: String,
14 | matureEnabled: Boolean = false,
15 | language: String = LanguageManager.getUserLanguageTag(),
16 | pageIndex: Int,
17 | ): ApiResult>
18 |
19 | suspend fun searchMovieByQuery(
20 | query: String,
21 | matureEnabled: Boolean = false,
22 | language: String = LanguageManager.getUserLanguageTag(),
23 | pageIndex: Int,
24 | ): ApiResult>
25 |
26 | suspend fun searchShowByQuery(
27 | query: String,
28 | matureEnabled: Boolean = false,
29 | language: String = LanguageManager.getUserLanguageTag(),
30 | pageIndex: Int,
31 | ): ApiResult>
32 |
33 | suspend fun searchPersonByQuery(
34 | query: String,
35 | matureEnabled: Boolean = false,
36 | language: String = LanguageManager.getUserLanguageTag(),
37 | pageIndex: Int,
38 | ): ApiResult>
39 | }
40 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/repository/movie/MovieRepository.kt:
--------------------------------------------------------------------------------
1 | package network.repository.movie
2 |
3 | import common.domain.models.util.ContentListType
4 | import kotlinx.coroutines.flow.Flow
5 | import network.models.ApiError
6 | import network.models.content.common.ContentCreditsResponse
7 | import network.models.content.common.MovieResponse
8 | import network.models.content.common.VideosByIdResponse
9 | import network.models.content.common.WatchProvidersResponse
10 | import network.models.content.search.ContentPagingResponse
11 | import network.util.Either
12 |
13 | interface MovieRepository {
14 | suspend fun getMovieList(
15 | contentListType: ContentListType,
16 | pageIndex: Int,
17 | ): Flow, ApiError>>
18 |
19 | suspend fun getMovieDetailsById(
20 | movieId: Int,
21 | ): Flow>
22 |
23 | suspend fun getMovieCreditsById(
24 | movieId: Int,
25 | ): Flow>
26 |
27 | suspend fun getMovieVideosById(
28 | movieId: Int,
29 | ): Flow>
30 |
31 | suspend fun getRecommendationsMoviesById(
32 | movieId: Int,
33 | ): Flow, ApiError>>
34 |
35 | suspend fun getSimilarMoviesById(
36 | movieId: Int,
37 | ): Flow, ApiError>>
38 |
39 | suspend fun getStreamingProviders(
40 | movieId: Int,
41 | ): Flow>
42 | }
43 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/features/watchlist/ui/components/WatchlistTabItem.kt:
--------------------------------------------------------------------------------
1 | package features.watchlist.ui.components
2 |
3 | import cinetracker_kmp.composeapp.generated.resources.Res
4 | import cinetracker_kmp.composeapp.generated.resources.watched_tab
5 | import cinetracker_kmp.composeapp.generated.resources.watchlist_tab
6 | import common.ui.components.tab.TabItem
7 | import common.util.Constants.ADD_NEW_TAB_ID
8 | import common.util.Constants.UNSELECTED_OPTION_INDEX
9 | import features.watchlist.ui.model.DefaultLists
10 | import org.jetbrains.compose.resources.StringResource
11 |
12 | sealed class WatchlistTabItem(
13 | override val tabResId: StringResource? = null,
14 | override val tabName: String? = "",
15 | override var tabIndex: Int = UNSELECTED_OPTION_INDEX,
16 | open val listId: Int,
17 | ) : TabItem {
18 | data object WatchlistTab : WatchlistTabItem(
19 | tabResId = Res.string.watchlist_tab,
20 | listId = DefaultLists.WATCHLIST.listId,
21 | )
22 | data object WatchedTab : WatchlistTabItem(
23 | tabResId = Res.string.watched_tab,
24 | listId = DefaultLists.WATCHED.listId,
25 | )
26 | data object AddNewTab : WatchlistTabItem(
27 | tabResId = null,
28 | listId = DefaultLists.ADD_NEW.listId,
29 | tabIndex = ADD_NEW_TAB_ID,
30 | )
31 | data class CustomTab(
32 | override val tabResId: StringResource? = null,
33 | override val tabName: String?,
34 | override var tabIndex: Int = UNSELECTED_OPTION_INDEX,
35 | override var listId: Int,
36 | ) : WatchlistTabItem(tabResId, tabName, tabIndex, listId)
37 | }
38 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/ui/components/ClassicGradientBrush.kt:
--------------------------------------------------------------------------------
1 | package common.ui.components
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.ui.Modifier
5 | import androidx.compose.ui.graphics.Brush
6 | import androidx.compose.ui.graphics.Color
7 |
8 | fun Modifier.classicVerticalGradientBrush(
9 | colorList: List = listOf(Color.Black, Color.Transparent),
10 | direction: GradientDirections = GradientDirections.DOWN,
11 | ): Modifier {
12 | return when (direction) {
13 | GradientDirections.UP -> this.background(
14 | Brush.verticalGradient(
15 | colors = colorList,
16 | startY = Float.POSITIVE_INFINITY,
17 | endY = 0f,
18 | ),
19 | )
20 | GradientDirections.DOWN -> this.background(
21 | Brush.verticalGradient(
22 | colors = colorList,
23 | startY = 0f,
24 | endY = Float.POSITIVE_INFINITY,
25 | ),
26 | )
27 | GradientDirections.LEFT -> this.background(
28 | Brush.horizontalGradient(
29 | colors = colorList,
30 | startX = Float.POSITIVE_INFINITY,
31 | endX = 0f,
32 | ),
33 | )
34 | GradientDirections.RIGHT -> this.background(
35 | Brush.verticalGradient(
36 | colors = colorList,
37 | startY = 0f,
38 | endY = Float.POSITIVE_INFINITY,
39 | ),
40 | )
41 | }
42 | }
43 |
44 | enum class GradientDirections {
45 | UP,
46 | DOWN,
47 | LEFT,
48 | RIGHT,
49 | }
50 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/ui/components/button/SortIconButton.kt:
--------------------------------------------------------------------------------
1 | package common.ui.components.button
2 |
3 | import androidx.compose.foundation.layout.size
4 | import androidx.compose.material3.Icon
5 | import androidx.compose.material3.IconButton
6 | import androidx.compose.material3.MaterialTheme
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.runtime.collectAsState
9 | import androidx.compose.runtime.getValue
10 | import androidx.compose.ui.Modifier
11 | import androidx.compose.ui.unit.dp
12 | import cinetracker_kmp.composeapp.generated.resources.Res
13 | import cinetracker_kmp.composeapp.generated.resources.ic_sort
14 | import common.ui.MainViewModel
15 | import common.util.UiConstants.BROWSE_SORT_ICON_SIZE
16 | import features.watchlist.WatchlistScreen
17 | import org.jetbrains.compose.resources.painterResource
18 |
19 | @Composable
20 | fun SortIconButton(
21 | mainViewModel: MainViewModel,
22 | currentScreen: String,
23 | displaySortScreen: (Boolean) -> Unit,
24 | ) {
25 | val watchlistSortSelected by mainViewModel.watchlistSort.collectAsState()
26 | val iconColor = if (currentScreen == WatchlistScreen.route() && watchlistSortSelected != null) {
27 | MaterialTheme.colorScheme.secondary
28 | } else {
29 | MaterialTheme.colorScheme.onPrimary
30 | }
31 | IconButton(
32 | onClick = { displaySortScreen(true) },
33 | ) {
34 | Icon(
35 | modifier = Modifier.size(BROWSE_SORT_ICON_SIZE.dp),
36 | painter = painterResource(resource = Res.drawable.ic_sort),
37 | tint = iconColor,
38 | contentDescription = null,
39 | )
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/ui/components/RatingComponent.kt:
--------------------------------------------------------------------------------
1 | package common.ui.components
2 |
3 | import androidx.compose.foundation.Image
4 | import androidx.compose.foundation.layout.Row
5 | import androidx.compose.foundation.layout.offset
6 | import androidx.compose.foundation.layout.size
7 | import androidx.compose.material3.MaterialTheme
8 | import androidx.compose.material3.Text
9 | import androidx.compose.runtime.Composable
10 | import androidx.compose.ui.Alignment
11 | import androidx.compose.ui.Modifier
12 | import androidx.compose.ui.text.TextStyle
13 | import androidx.compose.ui.unit.dp
14 | import cinetracker_kmp.composeapp.generated.resources.Res
15 | import cinetracker_kmp.composeapp.generated.resources.ic_star
16 | import common.util.UiConstants.RATING_STAR_DEFAULT_SIZE
17 | import common.util.formatRating
18 | import org.jetbrains.compose.resources.painterResource
19 |
20 | @Composable
21 | fun RatingComponent(
22 | modifier: Modifier = Modifier,
23 | rating: Double?,
24 | ratingIconSize: Int? = RATING_STAR_DEFAULT_SIZE,
25 | textStyle: TextStyle = MaterialTheme.typography.titleMedium,
26 | ) {
27 | Row(
28 | verticalAlignment = Alignment.CenterVertically,
29 | modifier = modifier.offset(x = (-0.5).dp),
30 | ) {
31 | Image(
32 | modifier = Modifier.size((ratingIconSize ?: RATING_STAR_DEFAULT_SIZE).dp),
33 | painter = painterResource(resource = Res.drawable.ic_star),
34 | contentDescription = null,
35 | )
36 | Text(
37 | text = rating.formatRating(),
38 | color = MaterialTheme.colorScheme.onPrimary,
39 | style = textStyle,
40 | )
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/services/show/ShowService.kt:
--------------------------------------------------------------------------------
1 | package network.services.show
2 |
3 | import core.LanguageManager.getUserLanguageTag
4 | import network.models.content.common.ContentCreditsResponse
5 | import network.models.content.common.ShowResponse
6 | import network.models.content.common.VideosByIdResponse
7 | import network.models.content.common.WatchProvidersResponse
8 | import network.models.content.search.ContentPagingResponse
9 | import network.util.ApiResult
10 |
11 | interface ShowService {
12 | suspend fun getShowList(
13 | contentListType: String,
14 | pageIndex: Int,
15 | language: String = getUserLanguageTag(),
16 | ): ApiResult>
17 |
18 | suspend fun getShowDetailsById(
19 | showId: Int,
20 | language: String = getUserLanguageTag(),
21 | ): ApiResult
22 |
23 | suspend fun getShowCreditsById(
24 | showId: Int,
25 | language: String = getUserLanguageTag(),
26 | ): ApiResult
27 |
28 | suspend fun getShowVideosById(
29 | showId: Int,
30 | language: String = getUserLanguageTag(),
31 | ): ApiResult
32 |
33 | suspend fun getRecommendationsShowsById(
34 | showId: Int,
35 | language: String = getUserLanguageTag(),
36 | ): ApiResult>
37 |
38 | suspend fun getSimilarShowsById(
39 | showId: Int,
40 | language: String = getUserLanguageTag(),
41 | ): ApiResult>
42 |
43 | suspend fun getStreamingProviders(
44 | showId: Int,
45 | language: String = getUserLanguageTag(),
46 | ): ApiResult
47 | }
48 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/ui/components/card/MediaTypeTag.kt:
--------------------------------------------------------------------------------
1 | package common.ui.components.card
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.foundation.layout.Box
5 | import androidx.compose.foundation.layout.defaultMinSize
6 | import androidx.compose.foundation.layout.padding
7 | import androidx.compose.material3.MaterialTheme
8 | import androidx.compose.material3.Text
9 | import androidx.compose.runtime.Composable
10 | import androidx.compose.ui.Alignment
11 | import androidx.compose.ui.Modifier
12 | import androidx.compose.ui.unit.dp
13 | import cinetracker_kmp.composeapp.generated.resources.Res
14 | import cinetracker_kmp.composeapp.generated.resources.movie_tag
15 | import cinetracker_kmp.composeapp.generated.resources.show_tag
16 | import common.domain.models.util.MediaType
17 | import common.ui.theme.PrimaryYellowColor_90
18 | import org.jetbrains.compose.resources.stringResource
19 |
20 | @Composable
21 | fun MediaTypeTag(
22 | modifier: Modifier = Modifier,
23 | mediaType: MediaType,
24 | ) {
25 | val mediaTypeTag = if (mediaType == MediaType.MOVIE) {
26 | stringResource(resource = Res.string.movie_tag)
27 | } else {
28 | stringResource(resource = Res.string.show_tag)
29 | }
30 |
31 | Box(
32 | modifier = modifier
33 | .defaultMinSize(minWidth = 50.dp)
34 | .background(color = PrimaryYellowColor_90),
35 | contentAlignment = Alignment.Center,
36 | ) {
37 | Text(
38 | text = mediaTypeTag,
39 | color = MaterialTheme.colorScheme.primary,
40 | style = MaterialTheme.typography.titleSmall,
41 | maxLines = 1,
42 | modifier = Modifier.padding(horizontal = 2.dp),
43 | )
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/services/movie/MovieService.kt:
--------------------------------------------------------------------------------
1 | package network.services.movie
2 |
3 | import core.LanguageManager.getUserLanguageTag
4 | import network.models.content.common.ContentCreditsResponse
5 | import network.models.content.common.MovieResponse
6 | import network.models.content.common.VideosByIdResponse
7 | import network.models.content.common.WatchProvidersResponse
8 | import network.models.content.search.ContentPagingResponse
9 | import network.util.ApiResult
10 |
11 | interface MovieService {
12 | suspend fun getMovieList(
13 | movieListType: String,
14 | pageIndex: Int,
15 | language: String = getUserLanguageTag(),
16 | ): ApiResult>
17 |
18 | suspend fun getMovieDetailsById(
19 | movieId: Int,
20 | language: String = getUserLanguageTag(),
21 | ): ApiResult
22 |
23 | suspend fun getMovieCreditsById(
24 | movieId: Int,
25 | language: String = getUserLanguageTag(),
26 | ): ApiResult
27 |
28 | suspend fun getMovieVideosById(
29 | movieId: Int,
30 | language: String = getUserLanguageTag(),
31 | ): ApiResult
32 |
33 | suspend fun getRecommendationsMoviesById(
34 | movieId: Int,
35 | language: String = getUserLanguageTag(),
36 | ): ApiResult>
37 |
38 | suspend fun getSimilarMoviesById(
39 | movieId: Int,
40 | language: String = getUserLanguageTag(),
41 | ): ApiResult>
42 |
43 | suspend fun getStreamingProviders(
44 | movieId: Int,
45 | language: String = getUserLanguageTag(),
46 | ): ApiResult
47 | }
48 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/features/home/ui/components/carousel/ComingSoonContainer.kt:
--------------------------------------------------------------------------------
1 | package features.home.ui.components.carousel
2 |
3 | import androidx.compose.foundation.layout.Spacer
4 | import androidx.compose.foundation.layout.height
5 | import androidx.compose.foundation.layout.padding
6 | import androidx.compose.runtime.Composable
7 | import androidx.compose.ui.Modifier
8 | import androidx.compose.ui.unit.dp
9 | import common.domain.models.content.GenericContent
10 | import common.domain.models.util.MediaType
11 | import common.ui.components.card.ImageContentCard
12 | import common.util.UiConstants.CAROUSEL_CARDS_WIDTH
13 | import common.util.UiConstants.DEFAULT_PADDING
14 | import org.jetbrains.compose.resources.StringResource
15 |
16 | @Composable
17 | fun ComingSoonCarousel(
18 | carouselHeaderRes: StringResource,
19 | comingSoonList: List,
20 | currentScreenWidth: Float,
21 | goToDetails: (Int, MediaType) -> Unit,
22 | ) {
23 | if (comingSoonList.isNotEmpty()) {
24 | ClassicCarousel(
25 | carouselHeaderRes = carouselHeaderRes,
26 | itemList = comingSoonList,
27 | currentScreenWidth = currentScreenWidth,
28 | goToDetails = goToDetails,
29 | ) { item, goToDetails ->
30 | ImageContentCard(
31 | modifier = Modifier.padding(
32 | top = DEFAULT_PADDING.dp,
33 | bottom = DEFAULT_PADDING.dp,
34 | end = DEFAULT_PADDING.dp,
35 | ),
36 | item = item,
37 | adjustedCardSize = CAROUSEL_CARDS_WIDTH.dp,
38 | goToDetails = goToDetails,
39 | )
40 | }
41 | Spacer(modifier = Modifier.height(DEFAULT_PADDING.dp))
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/services/person/PersonServiceImpl.kt:
--------------------------------------------------------------------------------
1 | package network.services.person
2 |
3 | import com.projects.moviemanager.network.util.Parameters
4 | import io.ktor.client.HttpClient
5 | import network.models.content.common.PersonResponse
6 | import network.models.content.person.PersonCreditsResponse
7 | import network.models.content.person.PersonImagesResponse
8 | import network.util.ApiResult
9 | import network.util.buildUrl
10 | import network.util.getResult
11 |
12 | class PersonServiceImpl(
13 | private val client: HttpClient,
14 | ) : PersonService {
15 | override suspend fun getPersonDetailsById(
16 | personId: Int,
17 | language: String,
18 | ): ApiResult {
19 | val path = "person/$personId"
20 | val url = buildUrl(path) {
21 | mapOf(
22 | Parameters.LANGUAGE to language,
23 | )
24 | }
25 |
26 | return client.getResult(url)
27 | }
28 |
29 | override suspend fun getPersonCreditsById(
30 | personId: Int,
31 | language: String,
32 | ): ApiResult {
33 | val path = "person/$personId/combined_credits"
34 | val url = buildUrl(path) {
35 | mapOf(
36 | Parameters.LANGUAGE to language,
37 | )
38 | }
39 |
40 | return client.getResult(url)
41 | }
42 |
43 | override suspend fun getPersonImagesById(
44 | personId: Int,
45 | language: String,
46 | ): ApiResult {
47 | val path = "person/$personId/images"
48 | val url = buildUrl(path) {
49 | mapOf(
50 | Parameters.LANGUAGE to language,
51 | )
52 | }
53 |
54 | return client.getResult(url)
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/navigation/screens/HomeScreenUI.kt:
--------------------------------------------------------------------------------
1 | package navigation.screens
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.getValue
5 | import androidx.navigation.NavController
6 | import androidx.navigation.compose.currentBackStackEntryAsState
7 | import common.ui.screen.ErrorScreen
8 | import features.browse.BrowseScreen
9 | import features.details.DetailsScreen
10 | import features.home.ui.Home
11 | import features.watchlist.WatchlistScreen
12 | import navigation.ScreenUI
13 | import navigation.components.navigateToTopLevelDestination
14 |
15 | class HomeScreenUI : ScreenUI {
16 | @Composable
17 | override fun UI(navController: NavController) {
18 | val currentBackStackEntry by navController.currentBackStackEntryAsState()
19 | val currentScreen = currentBackStackEntry?.destination?.route
20 |
21 | Home(
22 | goToDetails = { contentId, mediaType ->
23 | navController.navigate(
24 | DetailsScreen.routeWithArguments(contentId, mediaType.name),
25 | )
26 | },
27 | goToWatchlist = {
28 | navigateToTopLevelDestination(
29 | navController = navController,
30 | destination = WatchlistScreen.route(),
31 | )
32 | },
33 | goToBrowse = {
34 | navigateToTopLevelDestination(
35 | navController = navController,
36 | destination = BrowseScreen.route(),
37 | )
38 | },
39 | goToErrorScreen = {
40 | if (currentScreen != ErrorScreen.route()) {
41 | navController.navigate(ErrorScreen.route())
42 | }
43 | },
44 | )
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/composeApp/src/androidMain/kotlin/database/di/DatabaseModule.android.kt:
--------------------------------------------------------------------------------
1 | package database.di
2 |
3 | import android.content.Context
4 | import androidx.room.Room
5 | import androidx.room.RoomDatabase
6 | import androidx.sqlite.db.SupportSQLiteDatabase
7 | import database.AppDatabase
8 | import features.watchlist.ui.model.DefaultLists
9 | import kotlinx.coroutines.Dispatchers
10 | import org.koin.core.module.Module
11 | import org.koin.dsl.module
12 |
13 | actual fun databaseModule(): Module {
14 | return module {
15 | single { createRoomDatabase(get()) }
16 | }
17 | }
18 |
19 | private fun createRoomDatabase(
20 | context: Context,
21 | ): AppDatabase {
22 | val dbFile = context.getDatabasePath("movie_manager_database")
23 | return Room.databaseBuilder(
24 | context,
25 | dbFile.absolutePath,
26 | )
27 | .setQueryCoroutineContext(Dispatchers.IO)
28 | .addCallback(roomCallback)
29 | .fallbackToDestructiveMigration(true)
30 | .build()
31 | }
32 |
33 | val roomCallback = object : RoomDatabase.Callback() {
34 | override fun onCreate(db: SupportSQLiteDatabase) {
35 | super.onCreate(db)
36 | createDefaultLists(db)
37 | }
38 |
39 | private fun createDefaultLists(db: SupportSQLiteDatabase) {
40 | // Execute the SQL to insert the default lists
41 | val defaultLists = listOf(
42 | DefaultLists.WATCHLIST.toString().lowercase(),
43 | DefaultLists.WATCHED.toString().lowercase(),
44 | )
45 | defaultLists.forEach { listName ->
46 | db.execSQL(
47 | """
48 | INSERT INTO list_entity (listName)
49 | VALUES (?)
50 | """,
51 | arrayOf(listName),
52 | )
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/ui/components/bottomsheet/SortBottomSheetComponents.kt:
--------------------------------------------------------------------------------
1 | package common.ui.components.bottomsheet
2 |
3 | import androidx.compose.foundation.layout.PaddingValues
4 | import androidx.compose.foundation.layout.Spacer
5 | import androidx.compose.material3.Button
6 | import androidx.compose.material3.ButtonDefaults
7 | import androidx.compose.material3.Icon
8 | import androidx.compose.material3.MaterialTheme
9 | import androidx.compose.material3.Text
10 | import androidx.compose.runtime.Composable
11 | import androidx.compose.ui.Modifier
12 | import androidx.compose.ui.graphics.Color
13 | import androidx.compose.ui.unit.dp
14 | import cinetracker_kmp.composeapp.generated.resources.Res
15 | import cinetracker_kmp.composeapp.generated.resources.ic_check
16 | import common.util.UiConstants.SMALL_MARGIN
17 | import org.jetbrains.compose.resources.painterResource
18 |
19 | @Composable
20 | fun SortButton(
21 | text: String,
22 | isSelected: Boolean = false,
23 | textColor: Color,
24 | onClick: () -> Unit,
25 | ) {
26 | Button(
27 | contentPadding = PaddingValues(horizontal = SMALL_MARGIN.dp),
28 | colors = ButtonDefaults.buttonColors(
29 | containerColor = Color.Transparent,
30 | ),
31 | onClick = onClick,
32 | ) {
33 | Text(
34 | text = text,
35 | style = MaterialTheme.typography.bodyMedium,
36 | color = if (isSelected) MaterialTheme.colorScheme.onSurfaceVariant else textColor,
37 | )
38 | Spacer(modifier = Modifier.weight(1f))
39 | if (isSelected) {
40 | Icon(
41 | painter = painterResource(resource = Res.drawable.ic_check),
42 | contentDescription = null,
43 | tint = MaterialTheme.colorScheme.secondary,
44 | )
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/.composeApp-appleMain.cinteropLibraries:
--------------------------------------------------------------------------------
1 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/dev.gitlive-firebase-crashlytics-1.12.0-iosMain-cinterop/dev.gitlive_firebase-crashlytics-cinterop-FirebaseCrashlytics-5CvdJw.klib
2 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.github.ismai117-kottie-1.9.6-alpha02-iosMain-cinterop/Kottie_lib-cinterop-Lottie-jSfdpg.klib
3 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/androidx.sqlite-sqlite-bundled-2.5.0-alpha02-nativeMain-cinterop/androidx.sqlite_sqlite-bundled-cinterop-androidXBundledSqlite-0MuvrA.klib
4 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/dev.gitlive-firebase-app-1.12.0-iosMain-cinterop/dev.gitlive_firebase-app-cinterop-FirebaseCore-Pte9Yg.klib
5 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/org.jetbrains.kotlinx-atomicfu-0.23.2-nativeMain-cinterop/org.jetbrains.kotlinx_atomicfu-cinterop-interop-yBS35w.klib
6 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/org.jetbrains.compose.ui-ui-uikit-1.6.11-uikitMain-cinterop/org.jetbrains.compose.ui_ui-uikit-cinterop-utils-oguluQ.klib
7 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/androidx.sqlite-sqlite-framework-2.5.0-alpha02-nativeMain-cinterop/androidx.sqlite_sqlite-framework-cinterop-sqlite3-GE3pgw.klib
8 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.ktor-ktor-utils-2.3.12-iosMain-cinterop/io.ktor_ktor-utils-cinterop-threadUtils-TE4abA.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/.composeApp-appleTest.cinteropLibraries:
--------------------------------------------------------------------------------
1 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/dev.gitlive-firebase-crashlytics-1.12.0-iosMain-cinterop/dev.gitlive_firebase-crashlytics-cinterop-FirebaseCrashlytics-5CvdJw.klib
2 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.github.ismai117-kottie-1.9.6-alpha02-iosMain-cinterop/Kottie_lib-cinterop-Lottie-jSfdpg.klib
3 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/androidx.sqlite-sqlite-bundled-2.5.0-alpha02-nativeMain-cinterop/androidx.sqlite_sqlite-bundled-cinterop-androidXBundledSqlite-0MuvrA.klib
4 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/org.jetbrains.kotlinx-atomicfu-0.23.2-nativeMain-cinterop/org.jetbrains.kotlinx_atomicfu-cinterop-interop-yBS35w.klib
5 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/dev.gitlive-firebase-app-1.12.0-iosMain-cinterop/dev.gitlive_firebase-app-cinterop-FirebaseCore-Pte9Yg.klib
6 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/org.jetbrains.compose.ui-ui-uikit-1.6.11-uikitMain-cinterop/org.jetbrains.compose.ui_ui-uikit-cinterop-utils-oguluQ.klib
7 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/androidx.sqlite-sqlite-framework-2.5.0-alpha02-nativeMain-cinterop/androidx.sqlite_sqlite-framework-cinterop-sqlite3-GE3pgw.klib
8 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.ktor-ktor-utils-2.3.12-iosMain-cinterop/io.ktor_ktor-utils-cinterop-threadUtils-TE4abA.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/.composeApp-iosMain.cinteropLibraries:
--------------------------------------------------------------------------------
1 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/dev.gitlive-firebase-crashlytics-1.12.0-iosMain-cinterop/dev.gitlive_firebase-crashlytics-cinterop-FirebaseCrashlytics-5CvdJw.klib
2 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.github.ismai117-kottie-1.9.6-alpha02-iosMain-cinterop/Kottie_lib-cinterop-Lottie-jSfdpg.klib
3 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/androidx.sqlite-sqlite-bundled-2.5.0-alpha02-nativeMain-cinterop/androidx.sqlite_sqlite-bundled-cinterop-androidXBundledSqlite-0MuvrA.klib
4 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/dev.gitlive-firebase-app-1.12.0-iosMain-cinterop/dev.gitlive_firebase-app-cinterop-FirebaseCore-Pte9Yg.klib
5 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/org.jetbrains.kotlinx-atomicfu-0.23.2-nativeMain-cinterop/org.jetbrains.kotlinx_atomicfu-cinterop-interop-yBS35w.klib
6 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/org.jetbrains.compose.ui-ui-uikit-1.6.11-uikitMain-cinterop/org.jetbrains.compose.ui_ui-uikit-cinterop-utils-oguluQ.klib
7 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/androidx.sqlite-sqlite-framework-2.5.0-alpha02-nativeMain-cinterop/androidx.sqlite_sqlite-framework-cinterop-sqlite3-GE3pgw.klib
8 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.ktor-ktor-utils-2.3.12-iosMain-cinterop/io.ktor_ktor-utils-cinterop-threadUtils-TE4abA.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/.composeApp-iosTest.cinteropLibraries:
--------------------------------------------------------------------------------
1 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/dev.gitlive-firebase-crashlytics-1.12.0-iosMain-cinterop/dev.gitlive_firebase-crashlytics-cinterop-FirebaseCrashlytics-5CvdJw.klib
2 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.github.ismai117-kottie-1.9.6-alpha02-iosMain-cinterop/Kottie_lib-cinterop-Lottie-jSfdpg.klib
3 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/androidx.sqlite-sqlite-bundled-2.5.0-alpha02-nativeMain-cinterop/androidx.sqlite_sqlite-bundled-cinterop-androidXBundledSqlite-0MuvrA.klib
4 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/org.jetbrains.kotlinx-atomicfu-0.23.2-nativeMain-cinterop/org.jetbrains.kotlinx_atomicfu-cinterop-interop-yBS35w.klib
5 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/dev.gitlive-firebase-app-1.12.0-iosMain-cinterop/dev.gitlive_firebase-app-cinterop-FirebaseCore-Pte9Yg.klib
6 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/org.jetbrains.compose.ui-ui-uikit-1.6.11-uikitMain-cinterop/org.jetbrains.compose.ui_ui-uikit-cinterop-utils-oguluQ.klib
7 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/androidx.sqlite-sqlite-framework-2.5.0-alpha02-nativeMain-cinterop/androidx.sqlite_sqlite-framework-cinterop-sqlite3-GE3pgw.klib
8 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.ktor-ktor-utils-2.3.12-iosMain-cinterop/io.ktor_ktor-utils-cinterop-threadUtils-TE4abA.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/.composeApp-nativeMain.cinteropLibraries:
--------------------------------------------------------------------------------
1 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/dev.gitlive-firebase-crashlytics-1.12.0-iosMain-cinterop/dev.gitlive_firebase-crashlytics-cinterop-FirebaseCrashlytics-5CvdJw.klib
2 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.github.ismai117-kottie-1.9.6-alpha02-iosMain-cinterop/Kottie_lib-cinterop-Lottie-jSfdpg.klib
3 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/androidx.sqlite-sqlite-bundled-2.5.0-alpha02-nativeMain-cinterop/androidx.sqlite_sqlite-bundled-cinterop-androidXBundledSqlite-0MuvrA.klib
4 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/dev.gitlive-firebase-app-1.12.0-iosMain-cinterop/dev.gitlive_firebase-app-cinterop-FirebaseCore-Pte9Yg.klib
5 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/org.jetbrains.kotlinx-atomicfu-0.23.2-nativeMain-cinterop/org.jetbrains.kotlinx_atomicfu-cinterop-interop-yBS35w.klib
6 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/org.jetbrains.compose.ui-ui-uikit-1.6.11-uikitMain-cinterop/org.jetbrains.compose.ui_ui-uikit-cinterop-utils-oguluQ.klib
7 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/androidx.sqlite-sqlite-framework-2.5.0-alpha02-nativeMain-cinterop/androidx.sqlite_sqlite-framework-cinterop-sqlite3-GE3pgw.klib
8 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.ktor-ktor-utils-2.3.12-iosMain-cinterop/io.ktor_ktor-utils-cinterop-threadUtils-TE4abA.klib
--------------------------------------------------------------------------------
/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/.composeApp-nativeTest.cinteropLibraries:
--------------------------------------------------------------------------------
1 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/dev.gitlive-firebase-crashlytics-1.12.0-iosMain-cinterop/dev.gitlive_firebase-crashlytics-cinterop-FirebaseCrashlytics-5CvdJw.klib
2 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.github.ismai117-kottie-1.9.6-alpha02-iosMain-cinterop/Kottie_lib-cinterop-Lottie-jSfdpg.klib
3 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/androidx.sqlite-sqlite-bundled-2.5.0-alpha02-nativeMain-cinterop/androidx.sqlite_sqlite-bundled-cinterop-androidXBundledSqlite-0MuvrA.klib
4 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/org.jetbrains.kotlinx-atomicfu-0.23.2-nativeMain-cinterop/org.jetbrains.kotlinx_atomicfu-cinterop-interop-yBS35w.klib
5 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/dev.gitlive-firebase-app-1.12.0-iosMain-cinterop/dev.gitlive_firebase-app-cinterop-FirebaseCore-Pte9Yg.klib
6 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/org.jetbrains.compose.ui-ui-uikit-1.6.11-uikitMain-cinterop/org.jetbrains.compose.ui_ui-uikit-cinterop-utils-oguluQ.klib
7 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/androidx.sqlite-sqlite-framework-2.5.0-alpha02-nativeMain-cinterop/androidx.sqlite_sqlite-framework-cinterop-sqlite3-GE3pgw.klib
8 | /Users/gustavop/Documents/Personal/CineTracker-KMP/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.ktor-ktor-utils-2.3.12-iosMain-cinterop/io.ktor_ktor-utils-cinterop-threadUtils-TE4abA.klib
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/navigation/MainNavGraph.kt:
--------------------------------------------------------------------------------
1 | package navigation
2 |
3 | import androidx.compose.animation.EnterTransition
4 | import androidx.compose.animation.ExitTransition
5 | import androidx.compose.runtime.Composable
6 | import androidx.navigation.NavHostController
7 | import androidx.navigation.compose.NavHost
8 | import androidx.navigation.compose.composable
9 | import androidx.navigation.compose.rememberNavController
10 | import common.ui.screen.ErrorScreen
11 | import features.browse.BrowseScreen
12 | import features.details.DetailsScreen
13 | import features.home.HomeScreen
14 | import features.search.SearchScreen
15 | import features.watchlist.WatchlistScreen
16 | import navigation.screens.BrowseScreenUI
17 | import navigation.screens.DetailsScreenUI
18 | import navigation.screens.ErrorScreenUI
19 | import navigation.screens.HomeScreenUI
20 | import navigation.screens.SearchScreenUI
21 | import navigation.screens.WatchlistScreenUI
22 |
23 | private val mainNavDestinations: Map = mapOf(
24 | HomeScreen to HomeScreenUI(),
25 | BrowseScreen to BrowseScreenUI(),
26 | WatchlistScreen to WatchlistScreenUI(),
27 | SearchScreen to SearchScreenUI(),
28 | DetailsScreen to DetailsScreenUI(),
29 | ErrorScreen to ErrorScreenUI(),
30 | )
31 |
32 | @Composable
33 | fun MainNavGraph(
34 | navController: NavHostController = rememberNavController(),
35 | ) {
36 | NavHost(
37 | navController = navController,
38 | startDestination = HomeScreen.route(),
39 | enterTransition = { EnterTransition.None },
40 | exitTransition = { ExitTransition.None },
41 | ) {
42 | mainNavDestinations.forEach { (screen, screenUI) ->
43 | composable(screen.route(), screen.arguments) {
44 | screenUI.UI(
45 | navController = navController,
46 | )
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/ui/components/bottomsheet/ModalComponents.kt:
--------------------------------------------------------------------------------
1 | package common.ui.components.bottomsheet
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.collectAsState
5 | import androidx.compose.runtime.getValue
6 | import common.ui.MainViewModel
7 | import features.browse.BrowseScreen
8 | import features.browse.ui.components.BrowseSortBottomSheet
9 | import features.watchlist.WatchlistScreen
10 | import features.watchlist.ui.components.WatchlistSortBottomSheet
11 |
12 | @Composable
13 | fun ModalComponents(
14 | mainViewModel: MainViewModel,
15 | showSortBottomSheet: Boolean,
16 | displaySortScreen: (Boolean) -> Unit,
17 | ) {
18 | val selectedMovieSortType by mainViewModel.movieSortType.collectAsState()
19 | val selectedShowSortType by mainViewModel.showSortType.collectAsState()
20 | val selectedMediaType by mainViewModel.currentMediaTypeSelected.collectAsState()
21 | val currentScreen by mainViewModel.currentScreen.collectAsState()
22 | val selectedWatchlistSortType by mainViewModel.watchlistSort.collectAsState()
23 |
24 | if (showSortBottomSheet) {
25 | when (currentScreen) {
26 | BrowseScreen.route() -> {
27 | BrowseSortBottomSheet(
28 | mainViewModel = mainViewModel,
29 | selectedMovieSortType = selectedMovieSortType,
30 | selectedShowSortType = selectedShowSortType,
31 | selectedMediaType = selectedMediaType,
32 | displaySortScreen = displaySortScreen,
33 | )
34 | }
35 | WatchlistScreen.route() -> {
36 | WatchlistSortBottomSheet(
37 | mainViewModel = mainViewModel,
38 | selectedWatchlistSortType = selectedWatchlistSortType,
39 | displaySortScreen = displaySortScreen,
40 | )
41 | }
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/ui/components/card/ImageContentCard.kt:
--------------------------------------------------------------------------------
1 | package common.ui.components.card
2 |
3 | import androidx.compose.foundation.clickable
4 | import androidx.compose.foundation.layout.Column
5 | import androidx.compose.foundation.layout.Spacer
6 | import androidx.compose.foundation.layout.fillMaxSize
7 | import androidx.compose.foundation.layout.height
8 | import androidx.compose.material3.MaterialTheme
9 | import androidx.compose.runtime.Composable
10 | import androidx.compose.ui.Alignment
11 | import androidx.compose.ui.Modifier
12 | import androidx.compose.ui.draw.clip
13 | import androidx.compose.ui.unit.Dp
14 | import androidx.compose.ui.unit.dp
15 | import common.domain.models.content.GenericContent
16 | import common.domain.models.util.MediaType
17 | import common.ui.components.NetworkImage
18 | import common.util.Constants.BASE_300_IMAGE_URL
19 | import common.util.UiConstants.DEFAULT_PADDING
20 | import common.util.UiConstants.POSTER_ASPECT_RATIO_MULTIPLY
21 |
22 | @Composable
23 | fun ImageContentCard(
24 | modifier: Modifier = Modifier,
25 | item: GenericContent,
26 | adjustedCardSize: Dp,
27 | goToDetails: (Int, MediaType) -> Unit,
28 | ) {
29 | val fullImageUrl = BASE_300_IMAGE_URL + item.posterPath
30 | Column(
31 | modifier = modifier
32 | .fillMaxSize()
33 | .clip(MaterialTheme.shapes.medium)
34 | .clickable(
35 | onClick = {
36 | goToDetails(item.id, item.mediaType)
37 | },
38 | ),
39 | horizontalAlignment = Alignment.CenterHorizontally,
40 | ) {
41 | NetworkImage(
42 | modifier = Modifier
43 | .clip(MaterialTheme.shapes.medium),
44 | imageUrl = fullImageUrl,
45 | widthDp = adjustedCardSize,
46 | heightDp = adjustedCardSize * POSTER_ASPECT_RATIO_MULTIPLY,
47 | )
48 | Spacer(modifier = Modifier.height(DEFAULT_PADDING.dp))
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/navigation/components/MainNavBarItem.kt:
--------------------------------------------------------------------------------
1 | package navigation.components
2 |
3 | import cinetracker_kmp.composeapp.generated.resources.Res
4 | import cinetracker_kmp.composeapp.generated.resources.ic_nav_browse
5 | import cinetracker_kmp.composeapp.generated.resources.ic_nav_home
6 | import cinetracker_kmp.composeapp.generated.resources.ic_nav_search
7 | import cinetracker_kmp.composeapp.generated.resources.ic_nav_watchlist
8 | import cinetracker_kmp.composeapp.generated.resources.main_nav_browse
9 | import cinetracker_kmp.composeapp.generated.resources.main_nav_home
10 | import cinetracker_kmp.composeapp.generated.resources.main_nav_search
11 | import cinetracker_kmp.composeapp.generated.resources.main_nav_watchlist
12 | import features.browse.BrowseScreen
13 | import features.home.HomeScreen
14 | import features.search.SearchScreen
15 | import features.watchlist.WatchlistScreen
16 | import navigation.Screen
17 | import org.jetbrains.compose.resources.DrawableResource
18 | import org.jetbrains.compose.resources.StringResource
19 |
20 | sealed class MainNavBarItem(
21 | val screen: Screen,
22 | val labelResId: StringResource,
23 | val iconResId: DrawableResource,
24 | ) {
25 | data object Home : MainNavBarItem(
26 | screen = HomeScreen,
27 | labelResId = Res.string.main_nav_home,
28 | iconResId = Res.drawable.ic_nav_home,
29 | )
30 | data object Browse : MainNavBarItem(
31 | screen = BrowseScreen,
32 | labelResId = Res.string.main_nav_browse,
33 | iconResId = Res.drawable.ic_nav_browse,
34 | )
35 | data object Watchlist : MainNavBarItem(
36 | screen = WatchlistScreen,
37 | labelResId = Res.string.main_nav_watchlist,
38 | iconResId = Res.drawable.ic_nav_watchlist,
39 | )
40 | data object Search : MainNavBarItem(
41 | screen = SearchScreen,
42 | labelResId = Res.string.main_nav_search,
43 | iconResId = Res.drawable.ic_nav_search,
44 | )
45 | }
46 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/network/repository/search/SearchRepositoryImpl.kt:
--------------------------------------------------------------------------------
1 | package network.repository.search
2 |
3 | import kotlinx.coroutines.flow.Flow
4 | import network.models.ApiError
5 | import network.models.content.common.MovieResponse
6 | import network.models.content.common.MultiResponse
7 | import network.models.content.common.PersonResponse
8 | import network.models.content.common.ShowResponse
9 | import network.models.content.search.ContentPagingResponse
10 | import network.services.search.SearchService
11 | import network.util.Either
12 | import network.util.asFlow
13 |
14 | class SearchRepositoryImpl(
15 | private val searchService: SearchService,
16 | ) : SearchRepository {
17 | override suspend fun onSearchMultiByQuery(
18 | query: String,
19 | page: Int,
20 | ): Flow, ApiError>> {
21 | return searchService.searchMultiByQuery(
22 | query = query,
23 | pageIndex = page,
24 | ).asFlow()
25 | }
26 |
27 | override suspend fun onSearchMovieByQuery(
28 | query: String,
29 | page: Int,
30 | ): Flow, ApiError>> {
31 | return searchService.searchMovieByQuery(
32 | query = query,
33 | pageIndex = page,
34 | ).asFlow()
35 | }
36 |
37 | override suspend fun onSearchShowByQuery(
38 | query: String,
39 | page: Int,
40 | ): Flow, ApiError>> {
41 | return searchService.searchShowByQuery(
42 | query = query,
43 | pageIndex = page,
44 | ).asFlow()
45 | }
46 |
47 | override suspend fun onSearchPersonByQuery(
48 | query: String,
49 | page: Int,
50 | ): Flow, ApiError>> {
51 | return searchService.searchPersonByQuery(
52 | query = query,
53 | pageIndex = page,
54 | ).asFlow()
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/features/details/ui/components/moreoptions/MoreOptionsTab.kt:
--------------------------------------------------------------------------------
1 | package features.details.ui.components.moreoptions
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.runtime.Composable
5 | import common.domain.models.content.GenericContent
6 | import common.domain.models.content.Videos
7 | import common.domain.models.util.MediaType
8 | import common.ui.components.GridContentList
9 | import common.ui.components.tab.GenericTabRow
10 | import common.ui.components.tab.setupGenericTabs
11 | import common.util.UiConstants.MAX_COUNT_MORE_LIKE_THIS_CARDS
12 | import features.details.ui.components.moreoptions.MoreOptionsTabItem.MoreLikeThisTab
13 | import features.details.ui.components.moreoptions.MoreOptionsTabItem.VideosTab
14 |
15 | @Composable
16 | fun MoreOptionsTab(
17 | videoList: List,
18 | contentSimilarList: List,
19 | goToDetails: (Int, MediaType) -> Unit,
20 | ) {
21 | val availableTabs = mutableListOf()
22 | if (contentSimilarList.isNotEmpty()) {
23 | availableTabs.add(MoreLikeThisTab)
24 | }
25 | if (videoList.isNotEmpty()) {
26 | availableTabs.add(VideosTab)
27 | }
28 |
29 | val (tabList, selectedTabIndex, updateSelectedTab) = setupGenericTabs(availableTabs)
30 |
31 | if (tabList.isNotEmpty()) {
32 | Column {
33 | GenericTabRow(selectedTabIndex.value, tabList, updateSelectedTab)
34 |
35 | when (tabList[selectedTabIndex.value].tabIndex) {
36 | VideosTab.tabIndex -> {
37 | VideoList(videoList)
38 | }
39 |
40 | MoreLikeThisTab.tabIndex -> {
41 | GridContentList(
42 | mediaContentList = contentSimilarList,
43 | maxCardsNumber = MAX_COUNT_MORE_LIKE_THIS_CARDS,
44 | openContentDetails = goToDetails,
45 | )
46 | }
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/ui/components/card/PersonImages.kt:
--------------------------------------------------------------------------------
1 | package common.ui.components.card
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.padding
5 | import androidx.compose.material3.Card
6 | import androidx.compose.material3.CardDefaults
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.Alignment
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.draw.clip
11 | import androidx.compose.ui.unit.Dp
12 | import androidx.compose.ui.unit.dp
13 | import common.ui.components.NetworkImage
14 | import common.ui.theme.MainBarGreyColor
15 | import common.ui.theme.RoundCornerShapes
16 | import common.util.Constants.BASE_500_IMAGE_URL
17 | import common.util.UiConstants.BROWSE_CARD_DEFAULT_ELEVATION
18 | import common.util.UiConstants.BROWSE_CARD_PADDING_HORIZONTAL
19 | import common.util.UiConstants.BROWSE_CARD_PADDING_VERTICAL
20 | import common.util.UiConstants.POSTER_ASPECT_RATIO_MULTIPLY
21 |
22 | @Composable
23 | fun PersonImages(
24 | modifier: Modifier = Modifier,
25 | cardWidth: Dp,
26 | imageUrl: String?,
27 | ) {
28 | val fullImageUrl = BASE_500_IMAGE_URL + imageUrl
29 | val imageHeight = cardWidth * POSTER_ASPECT_RATIO_MULTIPLY
30 |
31 | Card(
32 | modifier = modifier.padding(
33 | horizontal = BROWSE_CARD_PADDING_HORIZONTAL.dp,
34 | vertical = BROWSE_CARD_PADDING_VERTICAL.dp,
35 | ),
36 | colors = CardDefaults.cardColors(
37 | containerColor = MainBarGreyColor,
38 | ),
39 | elevation = CardDefaults.elevatedCardElevation(
40 | defaultElevation = BROWSE_CARD_DEFAULT_ELEVATION.dp,
41 | ),
42 | ) {
43 | Column(
44 | horizontalAlignment = Alignment.Start,
45 | ) {
46 | NetworkImage(
47 | imageUrl = fullImageUrl,
48 | modifier = Modifier.clip(RoundCornerShapes.medium),
49 | widthDp = cardWidth,
50 | heightDp = imageHeight,
51 | )
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/common/ui/components/PlaceholderView.kt:
--------------------------------------------------------------------------------
1 | package common.ui.components
2 |
3 | import androidx.compose.animation.core.LinearEasing
4 | import androidx.compose.animation.core.RepeatMode
5 | import androidx.compose.animation.core.animateFloat
6 | import androidx.compose.animation.core.infiniteRepeatable
7 | import androidx.compose.animation.core.rememberInfiniteTransition
8 | import androidx.compose.animation.core.tween
9 | import androidx.compose.foundation.background
10 | import androidx.compose.foundation.layout.Spacer
11 | import androidx.compose.runtime.Composable
12 | import androidx.compose.runtime.getValue
13 | import androidx.compose.ui.Modifier
14 | import androidx.compose.ui.geometry.Offset
15 | import androidx.compose.ui.graphics.Brush
16 | import androidx.compose.ui.graphics.Color
17 | import common.ui.theme.placeholderGrey
18 | import common.ui.theme.placeholderGrey2
19 |
20 | @Composable
21 | fun ComponentPlaceholder(
22 | modifier: Modifier = Modifier,
23 | initialValue: Float = -1000F,
24 | targetValue: Float = 2000F,
25 | durationMillis: Int = 3000,
26 | shimmerColorShades: List = listOf(
27 | placeholderGrey,
28 | placeholderGrey,
29 | placeholderGrey,
30 | placeholderGrey2,
31 | placeholderGrey2,
32 | ),
33 | ) {
34 | val transition = rememberInfiniteTransition(label = "transition")
35 | val translateAnim by transition.animateFloat(
36 | initialValue = initialValue,
37 | targetValue = targetValue,
38 | animationSpec = infiniteRepeatable(
39 | tween(
40 | durationMillis = durationMillis,
41 | easing = LinearEasing,
42 | ),
43 | repeatMode = RepeatMode.Restart,
44 | ),
45 | label = "placeholderAnimation",
46 | )
47 |
48 | val brush = Brush.linearGradient(
49 | colors = shimmerColorShades,
50 | start = Offset.Zero,
51 | end = Offset(translateAnim, translateAnim),
52 | )
53 |
54 | Spacer(
55 | modifier = modifier
56 | .background(brush),
57 | )
58 | }
59 |
--------------------------------------------------------------------------------