├── .github
├── ci-gradle.properties
└── workflows
│ ├── check_and_deploy.yml
│ └── ci.yml
├── .gitignore
├── .idea
├── .gitignore
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── compiler.xml
├── gradle.xml
├── inspectionProfiles
│ └── Project_Default.xml
├── misc.xml
└── vcs.xml
├── README.md
├── app
├── .gitignore
├── build.gradle.kts
├── proguard-rules.pro
├── src
│ ├── androidTest
│ │ └── java
│ │ │ └── com
│ │ │ └── jarroyo
│ │ │ └── composeapp
│ │ │ └── ExampleInstrumentedTest.kt
│ ├── debug
│ │ └── AndroidManifest.xml
│ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── jarroyo
│ │ │ │ └── composeapp
│ │ │ │ ├── ComposeApp.kt
│ │ │ │ ├── MainActivity.kt
│ │ │ │ └── ui
│ │ │ │ └── theme
│ │ │ │ ├── Color.kt
│ │ │ │ ├── Theme.kt
│ │ │ │ └── Type.kt
│ │ └── res
│ │ │ ├── drawable-v24
│ │ │ └── ic_launcher_foreground.xml
│ │ │ ├── drawable
│ │ │ └── ic_launcher_background.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ │ ├── values
│ │ │ ├── colors.xml
│ │ │ ├── strings.xml
│ │ │ └── themes.xml
│ │ │ └── xml
│ │ │ ├── backup_rules.xml
│ │ │ └── data_extraction_rules.xml
│ ├── release
│ │ └── AndroidManifest.xml
│ └── test
│ │ └── java
│ │ └── com
│ │ └── jarroyo
│ │ └── composeapp
│ │ └── ExampleUnitTest.kt
└── versions
│ ├── dependencies
│ ├── debugAndroidTestRuntimeClasspathDependencies.txt
│ ├── debugRuntimeClasspathDependencies.txt
│ ├── debugUnitTestRuntimeClasspathDependencies.txt
│ ├── releaseRuntimeClasspathDependencies.txt
│ └── releaseUnitTestRuntimeClasspathDependencies.txt
│ └── mergedManifests
│ ├── debug
│ ├── AndroidManifest.xml
│ └── processDebugManifest
│ │ └── AndroidManifest.xml
│ └── release
│ ├── AndroidManifest.xml
│ └── processReleaseManifest
│ └── AndroidManifest.xml
├── build-conventions
├── build.gradle.kts
├── build
│ ├── libs
│ │ └── build-conventions.jar
│ ├── pluginDescriptors
│ │ ├── composeapp.android-app-conventions.properties
│ │ ├── composeapp.android-conventions.properties
│ │ ├── composeapp.buildlog-conventions.properties
│ │ ├── composeapp.config-conventions.properties
│ │ ├── composeapp.dependencies-conventions.properties
│ │ ├── composeapp.detekt-conventions.properties
│ │ ├── composeapp.merged-manifests-conventions.properties
│ │ ├── composeapp.ruler-conventions.properties
│ │ ├── composeapp.spotless-conventions.properties
│ │ └── composeapp.versions-conventions.properties
│ ├── resources
│ │ └── main
│ │ │ └── META-INF
│ │ │ └── gradle-plugins
│ │ │ ├── composeapp.android-app-conventions.properties
│ │ │ ├── composeapp.android-conventions.properties
│ │ │ ├── composeapp.buildlog-conventions.properties
│ │ │ ├── composeapp.config-conventions.properties
│ │ │ ├── composeapp.dependencies-conventions.properties
│ │ │ ├── composeapp.detekt-conventions.properties
│ │ │ ├── composeapp.merged-manifests-conventions.properties
│ │ │ ├── composeapp.ruler-conventions.properties
│ │ │ ├── composeapp.spotless-conventions.properties
│ │ │ └── composeapp.versions-conventions.properties
│ └── tmp
│ │ └── jar
│ │ └── MANIFEST.MF
├── settings.gradle.kts
└── src
│ └── main
│ └── kotlin
│ ├── com
│ └── jarroyo
│ │ └── composeapp
│ │ ├── ext
│ │ ├── AndroidConfigExt.kt
│ │ ├── AppConfigExt.kt
│ │ ├── ConfigExt.kt
│ │ ├── ExtraPropertiesExtensionExt.kt
│ │ └── ParamsConfigExt.kt
│ │ └── gmd
│ │ └── GradleManagedDevices.kt
│ ├── composeapp.android-app-conventions.gradle.kts
│ ├── composeapp.android-conventions.gradle.kts
│ ├── composeapp.buildlog-conventions.gradle.kts
│ ├── composeapp.config-conventions.gradle.kts
│ ├── composeapp.dependencies-conventions.gradle.kts
│ ├── composeapp.detekt-conventions.gradle.kts
│ ├── composeapp.merged-manifests-conventions.gradle.kts
│ ├── composeapp.multiplatform-feature-conventions.gradle.kts
│ ├── composeapp.multiplatform-library-conventions.gradle.kts
│ ├── composeapp.ruler-conventions.gradle.kts
│ ├── composeapp.spotless-conventions.gradle.kts
│ ├── composeapp.versions-conventions.gradle.kts
│ └── composeapp.violation-comments-to-github-conventions.gradle.kts
├── build.gradle.kts
├── config
├── detekt
│ └── detekt.yml
├── diktat
│ └── diktat-analysis.yml
├── greclipse
│ └── greclipse.properties
├── lint
│ └── lint.xml
├── prettier
│ ├── prettierrc-graphql.yml
│ └── prettierrc-yml.yml
└── ruler
│ └── ownership.yaml
├── desktop
├── .gitignore
├── build.gradle.kts
├── proguard-rules.pro
└── src
│ └── jvmMain
│ └── kotlin
│ └── com
│ └── jarroyo
│ └── composeapp
│ └── Main.kt
├── gradle.properties
├── gradle
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── iosApp
├── iosApp.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ ├── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── swiftpm
│ │ │ │ └── Package.resolved
│ │ └── xcuserdata
│ │ │ └── javierarroyo.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── xcuserdata
│ │ └── javierarroyo.xcuserdatad
│ │ ├── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
├── iosApp.xcworkspace
│ ├── contents.xcworkspacedata
│ ├── xcshareddata
│ │ └── swiftpm
│ │ │ └── Package.resolved
│ └── xcuserdata
│ │ └── javierarroyo.xcuserdatad
│ │ ├── UserInterfaceState.xcuserstate
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── iosApp
│ ├── Assets.xcassets
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
│ ├── ContentView.swift
│ ├── GoogleService-Info.plist
│ ├── Info.plist
│ ├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
│ ├── iosApp.entitlements
│ └── iosAppApp.swift
├── modules
├── feature-common-api
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src
│ │ └── commonMain
│ │ ├── AndroidManifest.xml
│ │ └── kotlin
│ │ └── com.jarroyo.feature.common.api
│ │ └── interactor
│ │ └── OpenUrlInBrowserInteractor.kt
├── feature-common
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidMain
│ │ └── kotlin
│ │ │ └── com.jarroyo.feature.common
│ │ │ └── interactor
│ │ │ └── OpenUrlInBrowserInteractor.kt
│ │ ├── commonMain
│ │ └── kotlin
│ │ │ └── com.jarroyo.feature.common
│ │ │ ├── CommonFeature.kt
│ │ │ └── interactor
│ │ │ └── OpenUrlInBrowserInteractorImpl.kt
│ │ ├── desktopMain
│ │ └── kotlin
│ │ │ └── com.jarroyo.feature.common
│ │ │ └── interactor
│ │ │ └── OpenUrlInBrowserInteractor.kt
│ │ └── nativeMain
│ │ └── kotlin
│ │ └── com.jarroyo.feature.common
│ │ └── interactor
│ │ └── OpenUrlInBrowserInteractor.kt
├── feature-electricity-api
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src
│ │ └── commonMain
│ │ ├── AndroidManifest.xml
│ │ └── kotlin
│ │ └── com
│ │ └── jarroyo
│ │ └── feature
│ │ └── electricity
│ │ └── api
│ │ ├── destination
│ │ └── ElectricityDestination.kt
│ │ └── interactor
│ │ └── GetElectricityDataInteractor.kt
├── feature-electricity
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidMain
│ │ ├── AndroidManifest.xml
│ │ └── kotlin
│ │ │ └── com
│ │ │ └── jarroyo
│ │ │ └── feature
│ │ │ └── electricity
│ │ │ └── di
│ │ │ └── PlatformModule.kt
│ │ ├── commonMain
│ │ ├── AndroidManifest.xml
│ │ └── kotlin
│ │ │ └── com
│ │ │ └── jarroyo
│ │ │ └── feature
│ │ │ └── electricity
│ │ │ ├── ElectricityFeature.kt
│ │ │ ├── di
│ │ │ └── PlatformModule.kt
│ │ │ ├── interactor
│ │ │ └── GetElectricityDataInteractorImpl.kt
│ │ │ └── ui
│ │ │ ├── ElectricityContract.kt
│ │ │ ├── ElectricityScreen.kt
│ │ │ └── ElectricityViewModel.kt
│ │ ├── desktopMain
│ │ └── kotlin
│ │ │ └── com
│ │ │ └── jarroyo
│ │ │ └── feature
│ │ │ └── electricity
│ │ │ └── di
│ │ │ └── PlatformModule.kt
│ │ └── nativeMain
│ │ └── kotlin
│ │ └── com
│ │ └── jarroyo
│ │ └── feature
│ │ └── electricity
│ │ └── di
│ │ └── PlatformModule.kt
├── feature-home-api
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src
│ │ └── commonMain
│ │ └── AndroidManifest.xml
├── feature-home-shared
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidMain
│ │ └── AndroidManifest.xml
│ │ ├── commonMain
│ │ ├── kotlin
│ │ │ └── com
│ │ │ │ └── jarroyo
│ │ │ │ └── feature
│ │ │ │ └── home
│ │ │ │ └── shared
│ │ │ │ ├── di
│ │ │ │ ├── FeaturesModule.kt
│ │ │ │ └── KoinAppModule.kt
│ │ │ │ ├── ext
│ │ │ │ └── LocalDateTimeExt.kt
│ │ │ │ └── ui
│ │ │ │ ├── HomeFeature.kt
│ │ │ │ ├── MainNavigationBar.kt
│ │ │ │ └── RootView.kt
│ │ └── resources
│ │ │ └── drawable
│ │ │ └── ui_ic_arrow_back.xml
│ │ └── nativeMain
│ │ └── kotlin
│ │ └── MainViewController.kt
├── feature-launches-api
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src
│ │ └── commonMain
│ │ ├── AndroidManifest.xml
│ │ └── kotlin
│ │ └── com
│ │ └── jarroyo
│ │ └── feature
│ │ └── launches
│ │ └── api
│ │ ├── destination
│ │ ├── LaunchDestination.kt
│ │ └── LaunchListDestination.kt
│ │ └── interactor
│ │ ├── AddFavoriteInteractor.kt
│ │ ├── GetFavoritesInteractor.kt
│ │ ├── GetLaunchDetailInteractor.kt
│ │ ├── GetLaunchesInteractor.kt
│ │ └── RemoveFavoriteInteractor.kt
├── feature-launches
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidMain
│ │ ├── AndroidManifest.xml
│ │ └── kotlin
│ │ │ └── com
│ │ │ └── jarroyo
│ │ │ └── feature
│ │ │ └── launches
│ │ │ └── di
│ │ │ └── PlatformModule.kt
│ │ ├── commonMain
│ │ ├── AndroidManifest.xml
│ │ ├── kotlin
│ │ │ └── com
│ │ │ │ └── jarroyo
│ │ │ │ └── feature
│ │ │ │ └── launches
│ │ │ │ ├── LaunchesFeature.kt
│ │ │ │ ├── di
│ │ │ │ └── PlatformModule.kt
│ │ │ │ ├── interactor
│ │ │ │ ├── AddFavoriteInteractorImpl.kt
│ │ │ │ ├── GetFavoritesInteractorImpl.kt
│ │ │ │ ├── GetLaunchDetailInteractorImpl.kt
│ │ │ │ ├── GetLaunchesInteractorImpl.kt
│ │ │ │ └── RemoveFavoriteInteractorImpl.kt
│ │ │ │ ├── sqldelight
│ │ │ │ ├── DatabaseWrapper.kt
│ │ │ │ └── dao
│ │ │ │ │ └── FavoriteRocketsDao.kt
│ │ │ │ └── ui
│ │ │ │ ├── launchdetail
│ │ │ │ ├── LaunchDetailContract.kt
│ │ │ │ ├── LaunchDetailScreen.kt
│ │ │ │ └── LaunchDetailViewModel.kt
│ │ │ │ └── launchlist
│ │ │ │ ├── LaunchListContract.kt
│ │ │ │ ├── LaunchListItem.kt
│ │ │ │ ├── LaunchListScreen.kt
│ │ │ │ └── LaunchListViewModel.kt
│ │ └── sqldelight
│ │ │ └── com
│ │ │ └── jarroyo
│ │ │ └── feature
│ │ │ └── launches
│ │ │ └── FavoriteRockets.sq
│ │ ├── commonTest
│ │ └── kotlin
│ │ │ └── com
│ │ │ └── jarroyo
│ │ │ └── feature
│ │ │ └── home
│ │ │ └── shared
│ │ │ └── interactor
│ │ │ └── GetLaunchDetailInteractorImplTest.kt
│ │ ├── desktopMain
│ │ └── kotlin
│ │ │ └── com
│ │ │ └── jarroyo
│ │ │ └── feature
│ │ │ └── launches
│ │ │ └── di
│ │ │ └── PlatformModule.kt
│ │ └── nativeMain
│ │ └── kotlin
│ │ └── com
│ │ └── jarroyo
│ │ └── feature
│ │ └── launches
│ │ └── di
│ │ └── PlatformModule.kt
├── feature-schedules-api
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src
│ │ └── commonMain
│ │ ├── AndroidManifest.xml
│ │ └── kotlin
│ │ └── com
│ │ └── jarroyo
│ │ └── feature
│ │ └── schedules
│ │ └── api
│ │ ├── destination
│ │ ├── ScheduleDetailDetail.kt
│ │ ├── ScheduleListDestination.kt
│ │ └── UserSelectorDestination.kt
│ │ ├── ext
│ │ └── UserExt.kt
│ │ ├── interactor
│ │ ├── AddSchedulesInteractor.kt
│ │ ├── GetScheduleInteractor.kt
│ │ ├── GetSchedulesInteractor.kt
│ │ ├── GetUserInteractor.kt
│ │ ├── GetUserListInteractor.kt
│ │ └── RemoveSchedulesInteractor.kt
│ │ └── model
│ │ ├── Schedule.kt
│ │ └── User.kt
├── feature-schedules
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src
│ │ └── commonMain
│ │ ├── AndroidManifest.xml
│ │ └── kotlin
│ │ └── com
│ │ └── jarroyo
│ │ └── feature
│ │ └── schedules
│ │ ├── SchedulesFeature.kt
│ │ ├── interactor
│ │ ├── AddScheduleInteractorImpl.kt
│ │ ├── GetScheduleInteractorImpl.kt
│ │ ├── GetSchedulesInteractorImpl.kt
│ │ ├── GetUserInteractorImpl.kt
│ │ ├── GetUserListInteractorImpl.kt
│ │ └── RemoveScheduleInteractorImpl.kt
│ │ ├── navigationsuite
│ │ └── ScheduleListNavigationSuiteItem.kt
│ │ └── ui
│ │ ├── list
│ │ ├── ScheduleListContract.kt
│ │ ├── ScheduleListItem.kt
│ │ ├── ScheduleListScreen.kt
│ │ └── ScheduleListViewModel.kt
│ │ ├── scheduledetail
│ │ ├── ScheduleDetailContract.kt
│ │ ├── ScheduleDetailScreen.kt
│ │ └── ScheduleDetailViewModel.kt
│ │ └── userselector
│ │ ├── UserSelectorContract.kt
│ │ ├── UserSelectorItem.kt
│ │ ├── UserSelectorScreen.kt
│ │ └── UserSelectorViewModel.kt
├── library-feature
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src
│ │ └── commonMain
│ │ ├── AndroidManifest.xml
│ │ └── kotlin
│ │ └── com
│ │ └── jarroyo
│ │ └── library
│ │ └── feature
│ │ ├── Feature.kt
│ │ ├── FeatureLifecycle.kt
│ │ └── FeatureManager.kt
├── library-navigation-api
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── proguard-rules.pro
│ └── src
│ │ └── commonMain
│ │ ├── AndroidManifest.xml
│ │ └── kotlin
│ │ └── com
│ │ └── jarroyo
│ │ └── library
│ │ └── navigation
│ │ └── api
│ │ ├── destination
│ │ └── NavigationDestination.kt
│ │ └── navigator
│ │ ├── AppNavigator.kt
│ │ └── NavigatorEvent.kt
├── library-navigation
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── proguard-rules.pro
│ └── src
│ │ └── commonMain
│ │ ├── AndroidManifest.xml
│ │ └── kotlin
│ │ └── com
│ │ └── jarroyo
│ │ └── library
│ │ └── navigation
│ │ ├── di
│ │ └── KoinNavigationModule.kt
│ │ └── navigator
│ │ └── AppNavigatorImpl.kt
├── library-network-api
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src
│ │ └── commonMain
│ │ ├── AndroidManifest.xml
│ │ ├── graphql
│ │ ├── LaunchFragment.graphql
│ │ ├── QueryLaunchDetail.graphql
│ │ ├── QueryLaunches.graphql
│ │ ├── RocketFragment.graphql
│ │ ├── ShipFragment.graphql
│ │ └── schema.graphqls
│ │ └── kotlin
│ │ └── com
│ │ └── jarroyo
│ │ └── library
│ │ └── network
│ │ └── api
│ │ └── ext
│ │ └── ApolloCallExt.kt
├── library-network
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidMain
│ │ └── AndroidManifest.xml
│ │ └── commonMain
│ │ └── kotlin
│ │ └── com
│ │ └── jarroyo
│ │ └── library
│ │ └── network
│ │ └── di
│ │ ├── ElectricityApi.kt
│ │ └── KoinNetworkModule.kt
├── library-test
│ ├── build.gradle.kts
│ └── proguard-test-consumer-rules.pro
└── library-ui-shared
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── proguard-rules.pro
│ └── src
│ ├── androidMain
│ └── kotlin
│ │ └── AndroidManifest.xml
│ └── commonMain
│ └── kotlin
│ └── com
│ └── jarroyo
│ └── library
│ └── ui
│ └── shared
│ ├── BaseViewModel.kt
│ ├── component
│ ├── BottomButtonBar.kt
│ ├── NavHostController.kt
│ ├── NavigationBarItemWithBadge.kt
│ ├── Placeholder.kt
│ ├── ProvidableCompositionLocal.kt
│ ├── SettingsMenuRow.kt
│ └── SettingsTile.kt
│ └── theme
│ └── Spacing.kt
├── screenshots
├── android
│ ├── detail.png
│ ├── graph.png
│ ├── home.png
│ └── loading.png
├── compose_multiplatform_logo.png
├── desktop
│ ├── detail.png
│ ├── graph.png
│ ├── home.png
│ └── loading.png
├── ios
│ ├── buildPhases.png
│ ├── detail.png
│ ├── graph.png
│ ├── home.png
│ └── loading.png
└── summary.png
└── settings.gradle.kts
/.github/ci-gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.daemon=false
2 | org.gradle.workers.max=2
3 |
4 | kotlin.incremental=false
5 | kotlin.compiler.execution.strategy=in-process
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.aab
3 | *.apk
4 | *.ap_
5 |
6 | # Files for the ART/Dalvik VM
7 | *.dex
8 |
9 | # Java class files
10 | *.class
11 |
12 | # Generated files
13 | bin/
14 | gen/
15 | out/
16 |
17 | # Gradle files
18 | .gradle/
19 | build/
20 |
21 | # Local configuration file (sdk path, etc)
22 | local.properties
23 |
24 | # Log Files
25 | *.log
26 |
27 | # Android Studio Navigation editor temp files
28 | .navigation/
29 |
30 | # Android Studio captures folder
31 | captures/
32 |
33 | # Intellij
34 | *.iml
35 | .idea/*
36 | !.idea/codeStyles/
37 | !.idea/copyright/
38 | !.idea/detekt.xml
39 |
40 | # External native build folder generated in Android Studio 2.2 and later
41 | .externalNativeBuild
42 |
43 | /captures
44 | /projectFilesBackup
45 | release_notes_*.txt
46 |
47 | # Release keys
48 | app-release.jks
49 | play-account.p12
50 | play-account.json
51 |
52 | # Google Services (e.g. APIs or Firebase)
53 | google-services.json
54 |
55 | # Freeline
56 | freeline.py
57 | freeline/
58 | freeline_project_description.json
59 |
60 | # Fastlane
61 | fastlane/report.xml
62 | fastlane/Preview.html
63 | fastlane/screenshots
64 | fastlane/test_output
65 | fastlane/readme.md
66 |
67 | **/.DS_Store
68 | *.db
69 |
70 | # Reports
71 | reports/
72 |
73 | prebuilds/fullsdk-linux
74 | prebuilds/fullsdk-darwin
75 |
76 | .cxx
77 | *.klib
78 | .kotlin/
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
50 |
51 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id ("composeapp.android-app-conventions")
3 | alias(libs.plugins.tripletplay)
4 | }
5 |
6 | android {
7 | defaultConfig {
8 | applicationId = config.android.applicationId.get()
9 | setProperty("archivesBaseName", "composeapp")
10 |
11 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" // https://github.com/google/dagger/issues/2033
12 |
13 | vectorDrawables.useSupportLibrary = true
14 | }
15 | buildTypes {
16 | getByName("debug") {
17 | namespace = config.android.applicationId.get() + ".debug"
18 | applicationIdSuffix = ".debug"
19 | }
20 | getByName("release") {
21 | namespace = config.android.applicationId.get()
22 | isShrinkResources = true
23 | isMinifyEnabled = true
24 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
25 | }
26 | }
27 | }
28 |
29 | dependencies {
30 | // Modules
31 | implementation(projects.modules.featureHomeShared)
32 |
33 | implementation(libs.androidx.activity.compose)
34 | implementation(libs.gitlive.firebase.firestore)
35 | implementation(libs.jetbrains.compose.ui)
36 | implementation(libs.jetbrains.compose.material3)
37 | implementation(libs.koin.android)
38 | testImplementation(libs.junit)
39 | androidTestImplementation(libs.junit)
40 | androidTestImplementation(libs.androidx.test.espresso.core)
41 | androidTestImplementation(libs.androidx.compose.ui.test.junit4)
42 | androidTestImplementation(libs.gitlive.firebase.firestore)
43 | debugImplementation(libs.androidx.compose.tooling)
44 | debugImplementation(libs.androidx.compose.ui.test.manifest)
45 |
46 | detektPlugins(libs.detekt)
47 | detektPlugins(libs.detekt.rules.compose)
48 | detektPlugins(libs.detekt.twitter.compose.rules)
49 |
50 | implementation(libs.androidx.compose.tooling)
51 | }
52 |
--------------------------------------------------------------------------------
/app/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
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/jarroyo/composeapp/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.composeapp
2 |
3 | import androidx.test.ext.junit.runners.AndroidJUnit4
4 | import org.junit.Test
5 | import org.junit.runner.RunWith
6 |
7 | /**
8 | * Instrumented test, which will execute on an Android device.
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | @RunWith(AndroidJUnit4::class)
13 | class ExampleInstrumentedTest {
14 | @Test
15 | fun useAppContext() {
16 | // Context of the app under test.
17 | assert(true)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
16 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jarroyo/composeapp/ComposeApp.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.composeapp
2 |
3 | import android.app.Application
4 | import android.content.Context
5 | import com.google.firebase.FirebaseApp
6 | import com.jarroyo.feature.home.shared.di.initKoin
7 | import org.koin.dsl.module
8 |
9 | class ComposeApp : Application() {
10 | override fun onCreate() {
11 | super.onCreate()
12 | FirebaseApp.initializeApp(this)
13 | initKoin(
14 | listOf(
15 | module {
16 | single { this@ComposeApp }
17 | },
18 | ),
19 | )
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jarroyo/composeapp/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.composeapp
2 |
3 | import android.os.Bundle
4 | import androidx.activity.ComponentActivity
5 | import androidx.activity.compose.setContent
6 | import com.jarroyo.composeapp.ui.theme.ComposeAppTheme
7 | import com.jarroyo.feature.home.shared.ui.RootView
8 |
9 | class MainActivity : ComponentActivity() {
10 | override fun onCreate(savedInstanceState: Bundle?) {
11 | super.onCreate(savedInstanceState)
12 | setContent {
13 | ComposeAppTheme {
14 | RootView()
15 | }
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jarroyo/composeapp/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.composeapp.ui.theme
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val PurpleDark = Color(0xFFD0BCFF)
6 | val PurpleGreyDark = Color(0xFFCCC2DC)
7 | val PinkDark = Color(0xFFEFB8C8)
8 |
9 | val PurpleLight = Color(0xFF6650a4)
10 | val PurpleGreyLight = Color(0xFF625b71)
11 | val PinkLight = Color(0xFF7D5260)
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jarroyo/composeapp/ui/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.composeapp.ui.theme
2 |
3 | import android.os.Build
4 | import androidx.compose.foundation.isSystemInDarkTheme
5 | import androidx.compose.material3.MaterialTheme
6 | import androidx.compose.material3.darkColorScheme
7 | import androidx.compose.material3.dynamicDarkColorScheme
8 | import androidx.compose.material3.dynamicLightColorScheme
9 | import androidx.compose.material3.lightColorScheme
10 | import androidx.compose.runtime.Composable
11 | import androidx.compose.ui.platform.LocalContext
12 |
13 | private val DarkColorScheme = darkColorScheme(
14 | primary = PurpleDark,
15 | secondary = PurpleGreyDark,
16 | tertiary = PinkDark,
17 | )
18 |
19 | private val LightColorScheme = lightColorScheme(
20 | primary = PurpleLight,
21 | secondary = PurpleGreyLight,
22 | tertiary = PinkLight,
23 | )
24 |
25 | @Composable
26 | fun ComposeAppTheme(
27 | darkTheme: Boolean = isSystemInDarkTheme(),
28 | dynamicColor: Boolean = true,
29 | content: @Composable () -> Unit,
30 | ) {
31 | val myColorScheme = when {
32 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && darkTheme ->
33 | dynamicDarkColorScheme(LocalContext.current)
34 |
35 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !darkTheme ->
36 | dynamicLightColorScheme(LocalContext.current)
37 |
38 | darkTheme -> DarkColorScheme
39 | else -> LightColorScheme
40 | }
41 |
42 | MaterialTheme(
43 | colorScheme = myColorScheme,
44 | typography = Typography,
45 | content = content,
46 | )
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jarroyo/composeapp/ui/theme/Type.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.composeapp.ui.theme
2 |
3 | import androidx.compose.material3.Typography
4 | import androidx.compose.ui.text.TextStyle
5 | import androidx.compose.ui.text.font.FontFamily
6 | import androidx.compose.ui.text.font.FontWeight
7 | import androidx.compose.ui.unit.sp
8 |
9 | // Set of Material typography styles to start with
10 | val Typography = Typography(
11 | bodyLarge = TextStyle(
12 | fontFamily = FontFamily.Default,
13 | fontWeight = FontWeight.Normal,
14 | fontSize = 16.sp,
15 | lineHeight = 24.sp,
16 | letterSpacing = 0.5.sp,
17 | ),
18 | )
19 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ComposeApp
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
12 |
13 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/release/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/test/java/com/jarroyo/composeapp/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.composeapp
2 |
3 | import junit.framework.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | /**
7 | * Example local unit test, which will execute on the development machine (host).
8 | *
9 | * See [testing documentation](http://d.android.com/tools/testing).
10 | */
11 | class ExampleUnitTest {
12 | @Test
13 | fun addition_isCorrect() {
14 | assertEquals(4, 2 + 2)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/build-conventions/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // Sharing build logic between subprojects
2 | // https://docs.gradle.org/current/samples/sample_convention_plugins.html
3 |
4 | plugins {
5 | `kotlin-dsl`
6 | }
7 |
8 |
9 | dependencies {
10 | implementation (libs.plugin.aboutlibraries)
11 | implementation (libs.plugin.android.gradle)
12 | implementation (libs.plugin.androidcachefix)
13 | implementation (libs.plugin.appversioning)
14 | implementation(libs.plugin.compose.compiler)
15 | implementation (libs.plugin.detekt)
16 | implementation (libs.plugin.easylauncher)
17 | implementation (libs.plugin.firebase.perf)
18 | implementation (libs.plugin.google.services)
19 | implementation (libs.plugin.kotlin)
20 | implementation (libs.plugin.ksp)
21 | implementation (libs.plugin.ruler)
22 | implementation (libs.plugin.spotless)
23 | implementation(libs.plugin.versions)
24 | implementation(libs.plugin.versions.update)
25 | implementation (libs.plugin.violation)
26 | implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))
27 | }
28 |
--------------------------------------------------------------------------------
/build-conventions/build/libs/build-conventions.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/build-conventions/build/libs/build-conventions.jar
--------------------------------------------------------------------------------
/build-conventions/build/pluginDescriptors/composeapp.android-app-conventions.properties:
--------------------------------------------------------------------------------
1 | implementation-class=Composeapp_androidAppConventionsPlugin
2 |
--------------------------------------------------------------------------------
/build-conventions/build/pluginDescriptors/composeapp.android-conventions.properties:
--------------------------------------------------------------------------------
1 | implementation-class=Composeapp_androidConventionsPlugin
2 |
--------------------------------------------------------------------------------
/build-conventions/build/pluginDescriptors/composeapp.buildlog-conventions.properties:
--------------------------------------------------------------------------------
1 | implementation-class=Composeapp_buildlogConventionsPlugin
2 |
--------------------------------------------------------------------------------
/build-conventions/build/pluginDescriptors/composeapp.config-conventions.properties:
--------------------------------------------------------------------------------
1 | implementation-class=Composeapp_configConventionsPlugin
2 |
--------------------------------------------------------------------------------
/build-conventions/build/pluginDescriptors/composeapp.dependencies-conventions.properties:
--------------------------------------------------------------------------------
1 | implementation-class=Composeapp_dependenciesConventionsPlugin
2 |
--------------------------------------------------------------------------------
/build-conventions/build/pluginDescriptors/composeapp.detekt-conventions.properties:
--------------------------------------------------------------------------------
1 | implementation-class=Composeapp_detektConventionsPlugin
2 |
--------------------------------------------------------------------------------
/build-conventions/build/pluginDescriptors/composeapp.merged-manifests-conventions.properties:
--------------------------------------------------------------------------------
1 | implementation-class=Composeapp_mergedManifestsConventionsPlugin
2 |
--------------------------------------------------------------------------------
/build-conventions/build/pluginDescriptors/composeapp.ruler-conventions.properties:
--------------------------------------------------------------------------------
1 | implementation-class=Composeapp_rulerConventionsPlugin
2 |
--------------------------------------------------------------------------------
/build-conventions/build/pluginDescriptors/composeapp.spotless-conventions.properties:
--------------------------------------------------------------------------------
1 | implementation-class=Composeapp_spotlessConventionsPlugin
2 |
--------------------------------------------------------------------------------
/build-conventions/build/pluginDescriptors/composeapp.versions-conventions.properties:
--------------------------------------------------------------------------------
1 | implementation-class=Composeapp_versionsConventionsPlugin
2 |
--------------------------------------------------------------------------------
/build-conventions/build/resources/main/META-INF/gradle-plugins/composeapp.android-app-conventions.properties:
--------------------------------------------------------------------------------
1 | implementation-class=Composeapp_androidAppConventionsPlugin
2 |
--------------------------------------------------------------------------------
/build-conventions/build/resources/main/META-INF/gradle-plugins/composeapp.android-conventions.properties:
--------------------------------------------------------------------------------
1 | implementation-class=Composeapp_androidConventionsPlugin
2 |
--------------------------------------------------------------------------------
/build-conventions/build/resources/main/META-INF/gradle-plugins/composeapp.buildlog-conventions.properties:
--------------------------------------------------------------------------------
1 | implementation-class=Composeapp_buildlogConventionsPlugin
2 |
--------------------------------------------------------------------------------
/build-conventions/build/resources/main/META-INF/gradle-plugins/composeapp.config-conventions.properties:
--------------------------------------------------------------------------------
1 | implementation-class=Composeapp_configConventionsPlugin
2 |
--------------------------------------------------------------------------------
/build-conventions/build/resources/main/META-INF/gradle-plugins/composeapp.dependencies-conventions.properties:
--------------------------------------------------------------------------------
1 | implementation-class=Composeapp_dependenciesConventionsPlugin
2 |
--------------------------------------------------------------------------------
/build-conventions/build/resources/main/META-INF/gradle-plugins/composeapp.detekt-conventions.properties:
--------------------------------------------------------------------------------
1 | implementation-class=Composeapp_detektConventionsPlugin
2 |
--------------------------------------------------------------------------------
/build-conventions/build/resources/main/META-INF/gradle-plugins/composeapp.merged-manifests-conventions.properties:
--------------------------------------------------------------------------------
1 | implementation-class=Composeapp_mergedManifestsConventionsPlugin
2 |
--------------------------------------------------------------------------------
/build-conventions/build/resources/main/META-INF/gradle-plugins/composeapp.ruler-conventions.properties:
--------------------------------------------------------------------------------
1 | implementation-class=Composeapp_rulerConventionsPlugin
2 |
--------------------------------------------------------------------------------
/build-conventions/build/resources/main/META-INF/gradle-plugins/composeapp.spotless-conventions.properties:
--------------------------------------------------------------------------------
1 | implementation-class=Composeapp_spotlessConventionsPlugin
2 |
--------------------------------------------------------------------------------
/build-conventions/build/resources/main/META-INF/gradle-plugins/composeapp.versions-conventions.properties:
--------------------------------------------------------------------------------
1 | implementation-class=Composeapp_versionsConventionsPlugin
2 |
--------------------------------------------------------------------------------
/build-conventions/build/tmp/jar/MANIFEST.MF:
--------------------------------------------------------------------------------
1 | Manifest-Version: 1.0
2 |
3 |
--------------------------------------------------------------------------------
/build-conventions/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | google()
5 | mavenCentral()
6 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
7 | }
8 | }
9 |
10 | // https://docs.gradle.org/7.0/userguide/declaring_dependencies.html#sec:type-safe-project-accessors
11 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
12 |
13 | @Suppress("UnstableApiUsage")
14 | dependencyResolutionManagement {
15 | repositories {
16 | gradlePluginPortal()
17 | google()
18 | mavenCentral()
19 | }
20 | versionCatalogs {
21 | create("libs") {
22 | from(files("../gradle/libs.versions.toml"))
23 | }
24 | }
25 | }
26 |
27 | rootProject.name = "build-conventions"
28 |
--------------------------------------------------------------------------------
/build-conventions/src/main/kotlin/com/jarroyo/composeapp/ext/AndroidConfigExt.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.composeapp.ext
2 |
3 | import org.gradle.api.JavaVersion
4 | import org.gradle.api.plugins.ExtensionAware
5 | import org.gradle.api.provider.Property
6 | import org.gradle.kotlin.dsl.getByType
7 |
8 | interface AndroidConfigExt : ExtensionAware {
9 | val accountType: Property
10 | val applicationId: Property
11 | val compileSdk: Property
12 | val javaVersion: Property
13 | val minSdk: Property
14 | val targetSdk: Property
15 | }
16 |
17 | internal inline val ConfigExt.android: AndroidConfigExt get() = extensions.getByType()
18 |
--------------------------------------------------------------------------------
/build-conventions/src/main/kotlin/com/jarroyo/composeapp/ext/AppConfigExt.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.composeapp.ext
2 |
3 | import org.gradle.api.plugins.ExtensionAware
4 | import org.gradle.api.provider.Property
5 | import org.gradle.kotlin.dsl.getByType
6 |
7 | interface AppConfigExt : ExtensionAware {
8 | val deepLinkSchema: Property
9 | val googleMapsApiKey: Property
10 | val helpUrl: Property
11 | val linkProductInfoUrl: Property
12 | val privacyPolicyUrl: Property
13 | val termsAndConditionsUrl: Property
14 | }
15 |
16 | internal inline val ConfigExt.app: AppConfigExt get() = extensions.getByType()
17 |
--------------------------------------------------------------------------------
/build-conventions/src/main/kotlin/com/jarroyo/composeapp/ext/ConfigExt.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.composeapp.ext
2 |
3 | import org.gradle.api.Project
4 | import org.gradle.api.plugins.ExtensionAware
5 | import org.gradle.kotlin.dsl.getByType
6 |
7 | interface ConfigExt : ExtensionAware
8 |
9 | internal inline val Project.config: ConfigExt get() = extensions.getByType()
10 |
--------------------------------------------------------------------------------
/build-conventions/src/main/kotlin/com/jarroyo/composeapp/ext/ExtraPropertiesExtensionExt.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.composeapp.ext
2 |
3 | import org.gradle.api.plugins.ExtraPropertiesExtension
4 |
5 | @Suppress("UNCHECKED_CAST")
6 | fun ExtraPropertiesExtension.getOrNull(name: String): T? = if (has(name)) get(name) as? T else null
7 |
--------------------------------------------------------------------------------
/build-conventions/src/main/kotlin/com/jarroyo/composeapp/ext/ParamsConfigExt.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.composeapp.ext
2 |
3 | import org.gradle.api.plugins.ExtensionAware
4 | import org.gradle.api.provider.Property
5 | import org.gradle.kotlin.dsl.getByType
6 |
7 | interface ParamsConfigExt : ExtensionAware {
8 | val espressoCleanup: Property
9 | val saveBuildLogToFile: Property
10 | }
11 |
12 | internal inline val ConfigExt.params: ParamsConfigExt get() = extensions.getByType()
13 |
--------------------------------------------------------------------------------
/build-conventions/src/main/kotlin/com/jarroyo/composeapp/gmd/GradleManagedDevices.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.composeapp.gmd
2 |
3 | import com.android.build.api.dsl.CommonExtension
4 | import com.android.build.api.dsl.ManagedVirtualDevice
5 | import org.gradle.kotlin.dsl.create
6 | import org.gradle.kotlin.dsl.get
7 | import org.gradle.kotlin.dsl.invoke
8 |
9 | /**
10 | * Configure project for Gradle managed devices
11 | */
12 | internal fun configureGradleManagedDevices(
13 | commonExtension: CommonExtension<*, *, *, *, *, *>,
14 | ) {
15 | val pixel6Api31 = DeviceConfig("Pixel 6", 31, "aosp")
16 | val pixel6Api28 = DeviceConfig("Pixel 6", 28, "aosp")
17 |
18 | val allDevices = listOf(pixel6Api31, pixel6Api28)
19 |
20 | commonExtension.testOptions {
21 | managedDevices {
22 | devices {
23 | allDevices.forEach { deviceConfig ->
24 | create(deviceConfig.taskName).apply {
25 | device = deviceConfig.device
26 | apiLevel = deviceConfig.apiLevel
27 | systemImageSource = deviceConfig.systemImageSource
28 | }
29 | }
30 | }
31 | }
32 | }
33 | }
34 |
35 | private data class DeviceConfig(
36 | val device: String,
37 | val apiLevel: Int,
38 | val systemImageSource: String,
39 | ) {
40 | val taskName = buildString {
41 | append(device.lowercase().replace(" ", ""))
42 | append("Api")
43 | append(apiLevel.toString())
44 | append(systemImageSource.replace("-", ""))
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/build-conventions/src/main/kotlin/composeapp.android-app-conventions.gradle.kts:
--------------------------------------------------------------------------------
1 | import com.android.build.gradle.internal.tasks.factory.dependsOn
2 | import com.jarroyo.composeapp.gmd.configureGradleManagedDevices
3 | import com.mikepenz.aboutlibraries.plugin.DuplicateMode
4 | import com.mikepenz.aboutlibraries.plugin.DuplicateRule
5 | import com.project.starter.easylauncher.filter.ChromeLikeFilter
6 | import org.gradle.accessors.dm.LibrariesForLibs
7 | import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension
8 |
9 | plugins {
10 | id("org.jetbrains.kotlin.plugin.compose")
11 | id("com.android.application")
12 | id("composeapp.android-conventions")
13 | id("kotlin-parcelize")
14 | id("com.mikepenz.aboutlibraries.plugin")
15 | id("composeapp.merged-manifests-conventions")
16 | id("composeapp.dependencies-conventions")
17 | id("com.starter.easylauncher")
18 | id("composeapp.ruler-conventions")
19 | id("com.google.gms.google-services")
20 | }
21 |
22 | val libs = the()
23 |
24 | android {
25 | buildFeatures {
26 | compose = true
27 | }
28 | compileOptions {
29 | isCoreLibraryDesugaringEnabled = true // https://developer.android.com/studio/write/java8-support#library-desugaring
30 | }
31 | configureGradleManagedDevices(this)
32 | configure {
33 | compilerOptions {
34 | freeCompilerArgs.set(
35 | freeCompilerArgs.get() + listOf(
36 | "-opt-in=androidx.compose.foundation.ExperimentalFoundationApi",
37 | "-opt-in=androidx.compose.material.ExperimentalMaterialApi",
38 | "-opt-in=androidx.compose.material3.ExperimentalMaterial3Api",
39 | "-opt-in=androidx.compose.ui.ExperimentalComposeUiApi",
40 | )
41 | )
42 | }
43 | }
44 | }
45 |
46 | easylauncher {
47 | buildTypes {
48 | create("debug") {
49 | filters(chromeLike(gravity = ChromeLikeFilter.Gravity.TOP, label = "DEBUG", textSizeRatio = 0.20f, labelPadding = 10))
50 | }
51 | }
52 | }
53 |
54 | aboutLibraries {
55 | duplicationMode = DuplicateMode.MERGE
56 | duplicationRule = DuplicateRule.SIMPLE
57 | }
58 |
59 | dependencies {
60 |
61 | coreLibraryDesugaring(libs.desugar)
62 | debugImplementation(libs.leakcanary)
63 | implementation(libs.androidx.core.ktx)
64 | implementation(libs.coroutines.core)
65 | implementation(libs.jetbrains.kotlinx.collections.immutable)
66 | implementation(libs.kotlin.result)
67 | implementation(libs.timber)
68 |
69 | testImplementation(project(":modules:library-test"))
70 | androidTestUtil(libs.androidx.test.orchestrator)
71 | }
72 |
73 | tasks.register("installGitHooks") {
74 | from(file("$rootDir/.githooks"))
75 | into(file("$rootDir/.git/hooks"))
76 | fileMode = "755".toInt(8)
77 | }
78 |
79 | afterEvaluate {
80 | tasks.named("preBuild").dependsOn("installGitHooks")
81 | }
82 |
--------------------------------------------------------------------------------
/build-conventions/src/main/kotlin/composeapp.buildlog-conventions.gradle.kts:
--------------------------------------------------------------------------------
1 | import com.jarroyo.composeapp.ext.config
2 | import com.jarroyo.composeapp.ext.params
3 | import org.gradle.api.internal.GradleInternal
4 | import org.gradle.internal.logging.LoggingOutputInternal
5 | import java.text.SimpleDateFormat
6 | import java.util.Date
7 |
8 | if (config.params.saveBuildLogToFile.get()) {
9 | val datetime = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(Date())
10 | val buildLogDir = "${buildDir}/logs"
11 | mkdir(buildLogDir)
12 | val buildLog = File("${buildLogDir}/buildlog-${datetime}.txt")
13 |
14 | System.setProperty("org.gradle.color.error", "RED")
15 |
16 | val outputListener = StandardOutputListener { output -> buildLog.appendText(output.toString()) }
17 | (gradle as GradleInternal).services.get(LoggingOutputInternal::class.java).addStandardOutputListener(outputListener)
18 | (gradle as GradleInternal).services.get(LoggingOutputInternal::class.java).addStandardErrorListener(outputListener)
19 | }
20 |
--------------------------------------------------------------------------------
/build-conventions/src/main/kotlin/composeapp.config-conventions.gradle.kts:
--------------------------------------------------------------------------------
1 | import com.jarroyo.composeapp.ext.AndroidConfigExt
2 | import com.jarroyo.composeapp.ext.ConfigExt
3 | import com.jarroyo.composeapp.ext.ParamsConfigExt
4 | import com.jarroyo.composeapp.ext.getOrNull
5 |
6 | val config = extensions.create("config").apply {
7 | extensions.create("android").apply {
8 | accountType.convention("com.jarroyo.composeapp.auth")
9 | applicationId.convention("com.jarroyo.composeapp")
10 | compileSdk.convention(35)
11 | javaVersion.convention(JavaVersion.VERSION_17)
12 | minSdk.convention(24)
13 | targetSdk.convention(35)
14 | }
15 |
16 | extensions.create("params").apply {
17 | espressoCleanup.convention((rootProject.extra.getOrNull("espressoCleanup") as String?).toBoolean())
18 | saveBuildLogToFile.convention((rootProject.extra.getOrNull("saveBuildLogToFile") as String?).toBoolean())
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/build-conventions/src/main/kotlin/composeapp.dependencies-conventions.gradle.kts:
--------------------------------------------------------------------------------
1 | import com.android.build.gradle.internal.tasks.factory.dependsOn
2 | import java.util.Locale
3 |
4 | afterEvaluate {
5 | val outputPath = "$projectDir/versions/dependencies"
6 | mkdir(outputPath)
7 | val compileDependencyReportTask = tasks.register("generateRuntimeDependenciesReport") {
8 | description = "Generates a text file containing the Runtime classpath dependencies."
9 | }
10 | project.configurations.filter { it.name.contains("RuntimeClasspath") }.forEach { configuration ->
11 | val configurationTask = tasks.register(
12 | "generate${configuration.name.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.ROOT) else it.toString() }}DependenciesReport",
13 | DependencyReportTask::class.java,
14 | ) {
15 | configurations = setOf(configuration)
16 | outputFile = File("$outputPath/${configuration.name}Dependencies.txt")
17 | }
18 | compileDependencyReportTask.configure { dependsOn(configurationTask) }
19 | }
20 | tasks.named("check").dependsOn(tasks.named("generateRuntimeDependenciesReport"))
21 | }
22 |
--------------------------------------------------------------------------------
/build-conventions/src/main/kotlin/composeapp.detekt-conventions.gradle.kts:
--------------------------------------------------------------------------------
1 | import io.gitlab.arturbosch.detekt.extensions.DetektExtension
2 | import org.gradle.accessors.dm.LibrariesForLibs
3 | import org.gradle.kotlin.dsl.the
4 |
5 | plugins {
6 | id ("io.gitlab.arturbosch.detekt")
7 | }
8 | val libs = the()
9 |
10 | detekt {
11 | toolVersion = libs.versions.detekt.get()
12 | source.setFrom(
13 | DetektExtension.DEFAULT_SRC_DIR_KOTLIN,
14 | DetektExtension.DEFAULT_TEST_SRC_DIR_KOTLIN,
15 | "src/androidTest/kotlin",
16 | "src/androidMain/kotlin",
17 | "src/commonMain/kotlin",
18 | "src/jvmMain/kotlin",
19 | "src/nativeMain/kotlin",
20 | "src/nativeTest/kotlin",
21 | )
22 | parallel = true
23 | autoCorrect = true
24 | }
25 |
26 | dependencies {
27 | detektPlugins (libs.detekt)
28 | detektPlugins (libs.detekt.rules.compose)
29 | detektPlugins (libs.detekt.twitter.compose.rules)
30 | }
31 |
32 | afterEvaluate {
33 | tasks.named("check") {
34 | dependsOn(tasks.named("detektMain"))
35 | dependsOn(tasks.named("detektTest"))
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/build-conventions/src/main/kotlin/composeapp.merged-manifests-conventions.gradle.kts:
--------------------------------------------------------------------------------
1 | tasks {
2 | register("copyMergedManifests") {
3 | dependsOn(tasks.matching { it.name matches "^process.*Manifest$".toRegex() })
4 | mustRunAfter(tasks.matching { it.name matches "^process.*Manifest$".toRegex() })
5 | mkdir("versions/mergedManifests")
6 | from("$buildDir/intermediates/merged_manifests") {
7 | include("**/*.xml")
8 | }
9 | into("versions/mergedManifests")
10 | filter { line -> line.replace("(android:version.*=\".*\")|(android:testOnly=\".*\")".toRegex(), "") }
11 | }
12 |
13 | named("check") {
14 | dependsOn(tasks.named("copyMergedManifests"))
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/build-conventions/src/main/kotlin/composeapp.multiplatform-feature-conventions.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.gradle.accessors.dm.LibrariesForLibs
2 |
3 | plugins {
4 | id ("composeapp.multiplatform-library-conventions")
5 | }
6 | val libs = the()
7 | android {
8 | buildFeatures {
9 | compose = true
10 | }
11 | }
12 | kotlin {
13 | sourceSets {
14 | commonMain.dependencies {
15 | api(project(":modules:library-feature"))
16 |
17 | implementation(project(":modules:library-ui-shared"))
18 | implementation(libs.jetbrains.compose.material3)
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/build-conventions/src/main/kotlin/composeapp.ruler-conventions.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.spotify.ruler")
3 | }
4 |
5 | ruler {
6 | abi.set("arm64-v8a")
7 | locale.set("en")
8 | screenDensity.set(480)
9 | sdkVersion.set(30)
10 | ownershipFile.set(rootProject.file("config/ruler/ownership.yaml"))
11 | defaultOwner.set("others")
12 | }
13 |
--------------------------------------------------------------------------------
/build-conventions/src/main/kotlin/composeapp.spotless-conventions.gradle.kts:
--------------------------------------------------------------------------------
1 | import com.diffplug.gradle.spotless.SpotlessTask
2 | import org.gradle.accessors.dm.LibrariesForLibs
3 |
4 | plugins {
5 | id ("com.diffplug.spotless")
6 | }
7 | val libs = the()
8 |
9 | spotless {
10 | kotlin {
11 | target("**/*.kt")
12 | targetExclude("**/build/**/*.kt", "**/nativeMain/**/*.kt", "**/desktopMain/**/*.kt")
13 | diktat(libs.versions.diktat.get()).configFile("$rootDir/config/diktat/diktat-analysis.yml")
14 | trimTrailingWhitespace()
15 | indentWithSpaces()
16 | endWithNewline()
17 | }
18 |
19 | format ("graphql") {
20 | target("**/*.graphql")
21 | prettier(libs.versions.prettier.get()).configFile("$rootDir/config/prettier/prettierrc-graphql.yml")
22 | }
23 |
24 | format ("yml") {
25 | target ("**/*.yml", "**/*.yaml")
26 | prettier(libs.versions.prettier.get()).configFile("$rootDir/config/prettier/prettierrc-yml.yml")
27 | }
28 |
29 | format ("androidXml") {
30 | target ("**/AndroidManifest.xml", "src/**/*.xml")
31 | targetExclude ("**/mergedManifests/**/AndroidManifest.xml", "**/build/**/*.xml")
32 | indentWithSpaces()
33 | trimTrailingWhitespace()
34 | endWithNewline()
35 | }
36 |
37 | format ("misc") {
38 | // define the files to apply `misc` to
39 | target ("**/*.md", "**/.gitignore")
40 |
41 | // define the steps to apply to those files
42 | indentWithSpaces()
43 | trimTrailingWhitespace()
44 | endWithNewline()
45 | }
46 | }
47 |
48 | tasks {
49 | withType {
50 | mustRunAfter(":app:copyMergedManifests")
51 | }
52 | }
53 |
54 |
--------------------------------------------------------------------------------
/build-conventions/src/main/kotlin/composeapp.versions-conventions.gradle.kts:
--------------------------------------------------------------------------------
1 | import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask
2 | import java.util.Locale
3 |
4 | plugins {
5 | id("com.github.ben-manes.versions")
6 | id("nl.littlerobots.version-catalog-update")
7 | }
8 |
9 | versionCatalogUpdate {
10 | keep {
11 | keepUnusedVersions.set(true)
12 | keepUnusedLibraries.set(true)
13 | keepUnusedPlugins.set(true)
14 | }
15 | }
16 |
17 | val isNonStable: (String) -> Boolean = { version ->
18 | val stableKeyword = setOf("RELEASE", "FINAL", "GA").any { version.uppercase(Locale.ROOT).contains(it) }
19 | val regex = "^[0-9,.v-]+(-r)?$".toRegex()
20 | !stableKeyword && !(version matches regex)
21 | }
22 |
23 | tasks.withType() {
24 | gradleReleaseChannel = "current"
25 | rejectVersionIf {
26 | isNonStable(candidate.version) && !isNonStable(currentVersion)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/build-conventions/src/main/kotlin/composeapp.violation-comments-to-github-conventions.gradle.kts:
--------------------------------------------------------------------------------
1 | import se.bjurr.violations.comments.github.plugin.gradle.ViolationCommentsToGitHubTask
2 |
3 | plugins {
4 | id("se.bjurr.violations.violation-comments-to-github-gradle-plugin")
5 | }
6 |
7 | tasks.register("violationCommentsToGitHub") {
8 | setPullRequestId(System.getProperties()["GITHUB_PULLREQUESTID"] as String?)
9 | setoAuth2Token(System.getProperties()["GITHUB_OAUTH2TOKEN"] as String?)
10 | setGitHubUrl("https://api.github.com/")
11 | setCreateCommentWithAllSingleFileComments(false)
12 | setCreateSingleFileComments(true)
13 | setCommentOnlyChangedContent(true)
14 | setKeepOldComments(false)
15 | setViolations(
16 | listOf(
17 | listOf(
18 | "KOTLINGRADLE",
19 | ".",
20 | ".*/build/logs/buildlog.*\\.txt\$",
21 | "Gradle",
22 | ),
23 | listOf(
24 | "CHECKSTYLE",
25 | ".",
26 | ".*/reports/detekt/.*\\.xml\$",
27 | "Detekt",
28 | ),
29 | listOf(
30 | "ANDROIDLINT",
31 | ".",
32 | ".*/reports/lint-results.*\\.xml\$",
33 | "Android Lint",
34 | ),
35 | listOf(
36 | "JUNIT",
37 | ".",
38 | ".*/build/test-results/test.*/.*\\.xml\$",
39 | "JUnit",
40 | ),
41 | listOf(
42 | "JUNIT",
43 | ".",
44 | ".*/build/sauce/saucectl-report\\.xml\$",
45 | "Espresso",
46 | ),
47 | ),
48 | )
49 | }
50 |
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | plugins {
3 | id("composeapp.config-conventions")
4 | id("composeapp.buildlog-conventions")
5 | id("composeapp.spotless-conventions")
6 | id("composeapp.versions-conventions")
7 | id("composeapp.violation-comments-to-github-conventions")
8 | alias(libs.plugins.multiplatform)
9 | }
10 |
11 | subprojects {
12 | gradle.projectsEvaluated {
13 | tasks.withType {
14 | options.compilerArgs.add("-Xlint:unchecked")
15 | options.compilerArgs.add("-Xlint:deprecation")
16 | }
17 | }
18 | }
19 |
20 | tasks.withType {
21 | description = "Regenerates the Gradle Wrapper files"
22 | distributionType = Wrapper.DistributionType.ALL
23 | gradleVersion = libs.versions.gradle.get()
24 | }
25 |
--------------------------------------------------------------------------------
/config/greclipse/greclipse.properties:
--------------------------------------------------------------------------------
1 | #Whether to use 'space', 'tab' or 'mixed' (both) characters for indentation.
2 | #The default value is 'tab'.
3 | org.eclipse.jdt.core.formatter.tabulation.char=space
4 |
5 | #Number of spaces used for indentation in case 'space' characters
6 | #have been selected. The default value is 4.
7 | org.eclipse.jdt.core.formatter.tabulation.size=4
8 |
9 | #Number of spaces used for indentation in case 'mixed' characters
10 | #have been selected. The default value is 4.
11 | org.eclipse.jdt.core.formatter.indentation.size=4
12 |
13 | #Whether or not indentation characters are inserted into empty lines.
14 | #The default value is 'true'.
15 | org.eclipse.jdt.core.formatter.indent_empty_lines=false
16 |
17 | #Number of spaces used for multiline indentation.
18 | #The default value is 2.
19 | groovy.formatter.multiline.indentation=2
20 |
21 | #Length after which list are considered too long. These will be wrapped.
22 | #The default value is 30.
23 | groovy.formatter.longListLength=30
24 |
25 | #Whether opening braces position shall be the next line.
26 | #The default value is 'same'.
27 | groovy.formatter.braces.start=same
28 |
29 | #Whether closing braces position shall be the next line.
30 | #The default value is 'next'.
31 | groovy.formatter.braces.end=next
32 |
33 | #Remove unnecessary semicolons. The default value is 'false'.
34 | groovy.formatter.remove.unnecessary.semicolons=true
35 |
--------------------------------------------------------------------------------
/config/lint/lint.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/config/prettier/prettierrc-graphql.yml:
--------------------------------------------------------------------------------
1 | # Configuration File for Prettier formatter used when running Spotless.
2 | # More information about config file: https://prettier.io/docs/en/configuration.html
3 | # List of rules which can be configured here: https://prettier.io/docs/en/options.html
4 | tabWidth: 4
5 |
--------------------------------------------------------------------------------
/config/prettier/prettierrc-yml.yml:
--------------------------------------------------------------------------------
1 | # Configuration File for Prettier formatter used when running Spotless.
2 | # More information about config file: https://prettier.io/docs/en/configuration.html
3 | # List of rules which can be configured here: https://prettier.io/docs/en/options.html
4 | tabWidth: 2
5 | singleQuote: true
6 |
--------------------------------------------------------------------------------
/config/ruler/ownership.yaml:
--------------------------------------------------------------------------------
1 | - identifier: :app
2 | owner: :app
3 |
4 | - identifier: :modules:library*
5 | owner: :module:library*
6 |
7 | - identifier: :modules:feature*
8 | owner: :module:feature*
9 |
10 | - identifier: androidx.*
11 | owner: androidx
12 |
13 | - identifier: com.apollographql.*
14 | owner: apollo
15 |
16 | - identifier: com.google.*
17 | owner: google
18 |
19 | - identifier: org.jetbrains.kotlin* # without the . to match also kotlinx
20 | owner: kotlin
21 |
--------------------------------------------------------------------------------
/desktop/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/desktop/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.jetbrains.compose.desktop.application.dsl.TargetFormat
2 |
3 | plugins {
4 | kotlin("multiplatform")
5 | id("org.jetbrains.compose")
6 | id("org.jetbrains.kotlin.plugin.compose")
7 | }
8 | version = "1.0-SNAPSHOT"
9 |
10 | kotlin {
11 | jvm {
12 | withJava()
13 | }
14 | sourceSets {
15 | jvmMain.dependencies {
16 | implementation(projects.modules.featureHomeShared)
17 | implementation(compose.desktop.currentOs)
18 | implementation(libs.gitlive.firebase.java)
19 | implementation(libs.koin.core)
20 | }
21 | }
22 | }
23 |
24 | compose.desktop {
25 | application {
26 | mainClass = "com.jarroyo.composeapp.MainKt"
27 | nativeDistributions {
28 | targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
29 | packageName = "desktop"
30 | packageVersion = "1.0.0"
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/desktop/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
--------------------------------------------------------------------------------
/desktop/src/jvmMain/kotlin/com/jarroyo/composeapp/Main.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.composeapp
2 |
3 | import android.app.Application
4 | import androidx.compose.ui.window.Window
5 | import androidx.compose.ui.window.application
6 | import com.jarroyo.feature.home.shared.ui.RootView
7 | import com.jarroyo.feature.home.shared.di.initKoin
8 | import com.google.firebase.Firebase
9 | import com.google.firebase.FirebaseOptions
10 | import com.google.firebase.FirebasePlatform
11 | import com.google.firebase.initialize
12 |
13 | // Migration to PreCompose 1.6.2 import moe.tlaster.precompose.ProvidePreComposeLocals
14 |
15 | fun main() {
16 | initializeFirebase()
17 | initKoin()
18 | application {
19 | Window(
20 | title = "Compose APP",
21 | onCloseRequest = ::exitApplication,
22 | ) {
23 | RootView()
24 | }
25 | }
26 | }
27 |
28 | fun initializeFirebase() {
29 | FirebasePlatform.initializeFirebasePlatform(
30 | object : FirebasePlatform() {
31 | override fun store(key: String, value: String) { }
32 | override fun retrieve(key: String) = ""
33 | override fun clear(key: String) { }
34 | override fun log(msg: String) { }
35 | },
36 | )
37 |
38 | val options: FirebaseOptions = FirebaseOptions.Builder()
39 | .setProjectId("virtualgym-684f7")
40 | .setApplicationId("1:129903346150:android:bf4d3697156f8e7009d627")
41 | .build()
42 |
43 | Firebase.initialize(Application(), options)
44 | }
45 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Gradle
2 | org.gradle.caching=true
3 | org.gradle.jvmargs=-Xms2G -Xmx6G -XX:+UseParallelGC
4 | org.gradle.parallel=true
5 | org.gradle.warning.mode=all
6 |
7 | # Android
8 | android.builder.sdkDownload=true
9 | android.enableJetifier=false
10 | android.enableResourceOptimizations=true
11 | android.experimental.cacheCompileLibResources=true
12 | android.experimental.enableSourceSetPathsMap=true
13 | android.nonFinalResIds=true
14 | android.nonTransitiveRClass=true
15 | android.useAndroidX=true
16 | kapt.include.compile.classpath=false
17 |
18 | # Kotlin
19 | kotlin.code.style=official
20 | kotlin.incremental.usePreciseJavaTracking=true
21 | kotlin.native.binary.memoryModel=experimental
22 | kotlin.native.cacheKind.iosSimulatorArm64=none
23 | kotlin.mpp.enableCInteropCommonization=true
24 | org.jetbrains.compose.experimental.uikit.enabled=true
25 | kotlin.native.cacheKind=none
26 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Oct 13 15:47:24 CEST 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/iosApp/iosApp.xcodeproj/project.xcworkspace/xcuserdata/javierarroyo.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/iosApp/iosApp.xcodeproj/project.xcworkspace/xcuserdata/javierarroyo.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/iosApp/iosApp.xcodeproj/xcuserdata/javierarroyo.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/iosApp/iosApp.xcodeproj/xcuserdata/javierarroyo.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | iosApp.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/iosApp/iosApp.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/iosApp/iosApp.xcworkspace/xcuserdata/javierarroyo.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/iosApp/iosApp.xcworkspace/xcuserdata/javierarroyo.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/iosApp/iosApp.xcworkspace/xcuserdata/javierarroyo.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Promises (Playground) 1.xcscheme
8 |
9 | orderHint
10 | 1
11 |
12 | Promises (Playground) 2.xcscheme
13 |
14 | orderHint
15 | 2
16 |
17 | Promises (Playground) 3.xcscheme
18 |
19 | orderHint
20 | 3
21 |
22 | Promises (Playground) 4.xcscheme
23 |
24 | orderHint
25 | 4
26 |
27 | Promises (Playground) 5.xcscheme
28 |
29 | orderHint
30 | 5
31 |
32 | Promises (Playground).xcscheme
33 |
34 | orderHint
35 | 0
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/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 | }
12 |
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "scale" : "1x",
11 | "size" : "16x16"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "scale" : "2x",
16 | "size" : "16x16"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "scale" : "1x",
21 | "size" : "32x32"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "scale" : "2x",
26 | "size" : "32x32"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "scale" : "1x",
31 | "size" : "128x128"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "scale" : "2x",
36 | "size" : "128x128"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "scale" : "1x",
41 | "size" : "256x256"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "scale" : "2x",
46 | "size" : "256x256"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "scale" : "1x",
51 | "size" : "512x512"
52 | },
53 | {
54 | "idiom" : "mac",
55 | "scale" : "2x",
56 | "size" : "512x512"
57 | }
58 | ],
59 | "info" : {
60 | "author" : "xcode",
61 | "version" : 1
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/iosApp/iosApp/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // iosApp
4 | //
5 | // Created by Javier Arroyo on 25/2/24.
6 | //
7 |
8 | import SwiftUI
9 | import ComposeApp
10 |
11 | struct ContentView: UIViewControllerRepresentable {
12 | func makeUIViewController(context: Context) -> UIViewController {
13 | MainViewControllerKt.MainViewController()
14 | }
15 |
16 | func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
17 | }
18 |
--------------------------------------------------------------------------------
/iosApp/iosApp/GoogleService-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | API_KEY
6 | AIzaSyCEcoV6soF-T9LMLMmXFXgR6JmbGfp1vf0
7 | GCM_SENDER_ID
8 | 129903346150
9 | PLIST_VERSION
10 | 1
11 | BUNDLE_ID
12 | com.jarroyo.composeapp.debug
13 | PROJECT_ID
14 | virtualgym-684f7
15 | STORAGE_BUCKET
16 | virtualgym-684f7.firebasestorage.app
17 | IS_ADS_ENABLED
18 |
19 | IS_ANALYTICS_ENABLED
20 |
21 | IS_APPINVITE_ENABLED
22 |
23 | IS_GCM_ENABLED
24 |
25 | IS_SIGNIN_ENABLED
26 |
27 | GOOGLE_APP_ID
28 | 1:129903346150:ios:c5248395c469a52e09d627
29 |
30 |
--------------------------------------------------------------------------------
/iosApp/iosApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CADisableMinimumFrameDurationOnPhone
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/iosApp/iosApp/iosApp.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.files.user-selected.read-only
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/iosApp/iosApp/iosAppApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // iosAppApp.swift
3 | // iosApp
4 | //
5 | // Created by Javier Arroyo on 25/2/24.
6 | //
7 |
8 | import SwiftUI
9 | import ComposeApp
10 | import FirebaseCore
11 |
12 | class AppDelegate: NSObject, UIApplicationDelegate {
13 | func application(_ application: UIApplication,
14 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
15 | FirebaseApp.configure()
16 |
17 | return true
18 | }
19 | }
20 |
21 | @main
22 | struct iosAppApp: App {
23 | @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
24 |
25 | init() {
26 | KoinAppModuleKt.doInitKoin(additionalModules: [])
27 | }
28 | var body: some Scene {
29 | WindowGroup {
30 | ContentView()
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/modules/feature-common-api/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/modules/feature-common-api/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("composeapp.multiplatform-library-conventions")
3 | alias(libs.plugins.kotlinx.serialization)
4 | }
5 |
6 | android {
7 | namespace = "com.jarroyo.feature.common.api"
8 | resourcePrefix = "common_api_"
9 | defaultConfig {
10 | consumerProguardFiles("$projectDir/proguard-home-api-consumer-rules.pro")
11 | }
12 | }
13 |
14 | kotlin {
15 | sourceSets {
16 | commonMain.dependencies {
17 | api(libs.jetbrains.kotlinx.serialization)
18 |
19 | implementation(projects.modules.libraryNavigationApi)
20 | implementation(projects.modules.libraryNetworkApi)
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/modules/feature-common-api/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/modules/feature-common-api/consumer-rules.pro
--------------------------------------------------------------------------------
/modules/feature-common-api/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.
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
--------------------------------------------------------------------------------
/modules/feature-common-api/src/commonMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/modules/feature-common-api/src/commonMain/kotlin/com.jarroyo.feature.common.api/interactor/OpenUrlInBrowserInteractor.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.common.api.interactor
2 |
3 | interface OpenUrlInBrowserInteractor {
4 | suspend operator fun invoke(url: String)
5 | }
6 |
--------------------------------------------------------------------------------
/modules/feature-common/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/modules/feature-common/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("composeapp.multiplatform-feature-conventions")
3 | id("org.jetbrains.compose")
4 | id("org.jetbrains.kotlin.plugin.compose")
5 | }
6 |
7 | android {
8 | namespace = "com.jarroyo.feature.common"
9 | sourceSets["main"].apply {
10 | res.srcDirs("src/androidMain/res", "src/commonMain/resources")
11 | }
12 | }
13 |
14 | kotlin {
15 | // REVIEW reason of ld: framework 'FirebaseCore' not found
16 | tasks.matching { it.name == "linkDebugTestIosSimulatorArm64" }.configureEach {
17 | enabled = false
18 | }
19 | tasks.matching { it.name == "linkDebugTestIosX64" }.configureEach {
20 | enabled = false
21 | }
22 | sourceSets {
23 | androidMain.dependencies {
24 | }
25 | commonMain.dependencies {
26 | implementation(compose.ui)
27 | implementation(compose.foundation)
28 | implementation(compose.material)
29 | implementation(compose.runtime)
30 | implementation(libs.jetbrains.kotlin.datetime)
31 | implementation(libs.sqldelight.coroutines)
32 |
33 | implementation(projects.modules.featureCommonApi)
34 | implementation(projects.modules.libraryNavigation)
35 | implementation(projects.modules.libraryNetworkApi)
36 | implementation(projects.modules.libraryNetwork)
37 | }
38 |
39 | desktopMain.dependencies {
40 | }
41 |
42 | iosMain.dependencies {
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/modules/feature-common/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/modules/feature-common/consumer-rules.pro
--------------------------------------------------------------------------------
/modules/feature-common/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.
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
--------------------------------------------------------------------------------
/modules/feature-common/src/androidMain/kotlin/com.jarroyo.feature.common/interactor/OpenUrlInBrowserInteractor.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.common.interactor
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import android.net.Uri
6 | import org.koin.java.KoinJavaComponent.get
7 |
8 | actual fun openUrl(url: String) {
9 | val context: Context = get(clazz = Context::class.java)
10 | val uri = url.let { Uri.parse(it) } ?: return
11 | val intent = Intent().apply {
12 | action = Intent.ACTION_VIEW
13 | data = uri
14 | addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
15 | }
16 | context.startActivity(intent)
17 | }
18 |
--------------------------------------------------------------------------------
/modules/feature-common/src/commonMain/kotlin/com.jarroyo.feature.common/CommonFeature.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.common
2 |
3 | import com.jarroyo.feature.common.api.interactor.OpenUrlInBrowserInteractor
4 | import com.jarroyo.feature.common.interactor.OpenUrlInBrowserInteractorImpl
5 | import com.jarroyo.library.feature.Feature
6 | import org.koin.core.module.Module
7 | import org.koin.dsl.module
8 |
9 | class CommonFeature : Feature() {
10 | override val id = "Common"
11 |
12 | companion object {
13 | val module: Module = module {
14 | factory { OpenUrlInBrowserInteractorImpl() }
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/modules/feature-common/src/commonMain/kotlin/com.jarroyo.feature.common/interactor/OpenUrlInBrowserInteractorImpl.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.common.interactor
2 |
3 | import com.jarroyo.feature.common.api.interactor.OpenUrlInBrowserInteractor
4 | import org.koin.core.component.KoinComponent
5 |
6 | internal class OpenUrlInBrowserInteractorImpl : OpenUrlInBrowserInteractor, KoinComponent {
7 | override suspend operator fun invoke(url: String) {
8 | openUrl(url)
9 | }
10 | }
11 |
12 | expect fun openUrl(url: String)
13 |
--------------------------------------------------------------------------------
/modules/feature-common/src/desktopMain/kotlin/com.jarroyo.feature.common/interactor/OpenUrlInBrowserInteractor.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.common.interactor
2 |
3 | import java.awt.Desktop
4 | import java.net.URI
5 |
6 | actual fun openUrl(url: String) {
7 | val uri = url.let { URI.create(it) } ?: return
8 | Desktop.getDesktop().browse(uri)
9 | }
--------------------------------------------------------------------------------
/modules/feature-common/src/nativeMain/kotlin/com.jarroyo.feature.common/interactor/OpenUrlInBrowserInteractor.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.common.interactor
2 |
3 | import platform.Foundation.NSURL
4 | import platform.UIKit.UIApplication
5 |
6 | actual fun openUrl(url: String) {
7 | val nsUrl = url.let { NSURL.URLWithString(it) } ?: return
8 | UIApplication.sharedApplication.openURL(nsUrl)
9 | }
--------------------------------------------------------------------------------
/modules/feature-electricity-api/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/modules/feature-electricity-api/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("composeapp.multiplatform-library-conventions")
3 | alias(libs.plugins.kotlinx.serialization)
4 | }
5 |
6 | android {
7 | namespace = "com.jarroyo.feature.electricity.api"
8 | resourcePrefix = "electricity_api_"
9 | defaultConfig {
10 | consumerProguardFiles("$projectDir/proguard-home-api-consumer-rules.pro")
11 | }
12 | }
13 |
14 | kotlin {
15 | sourceSets {
16 | commonMain.dependencies {
17 | api(libs.jetbrains.kotlinx.serialization)
18 |
19 | implementation(projects.modules.libraryNavigationApi)
20 | implementation(projects.modules.libraryNetworkApi)
21 | implementation(projects.modules.libraryNetwork)
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/modules/feature-electricity-api/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/modules/feature-electricity-api/consumer-rules.pro
--------------------------------------------------------------------------------
/modules/feature-electricity-api/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.
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
--------------------------------------------------------------------------------
/modules/feature-electricity-api/src/commonMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/modules/feature-electricity-api/src/commonMain/kotlin/com/jarroyo/feature/electricity/api/destination/ElectricityDestination.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.electricity.api.destination
2 |
3 | import com.jarroyo.library.navigation.api.destination.NavigationDestination
4 |
5 | object ElectricityDestination : NavigationDestination() {
6 | override val route: String = "electricity"
7 | }
8 |
--------------------------------------------------------------------------------
/modules/feature-electricity-api/src/commonMain/kotlin/com/jarroyo/feature/electricity/api/interactor/GetElectricityDataInteractor.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.electricity.api.interactor
2 |
3 | import com.github.michaelbull.result.Result
4 | import com.jarroyo.library.network.di.ElectricityData
5 | import kotlinx.datetime.LocalDate
6 |
7 | interface GetElectricityDataInteractor {
8 | suspend operator fun invoke(
9 | startDate: LocalDate,
10 | endDate: LocalDate,
11 | ): Result
12 | }
13 |
--------------------------------------------------------------------------------
/modules/feature-electricity/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/modules/feature-electricity/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("composeapp.multiplatform-feature-conventions")
3 | id("org.jetbrains.compose")
4 | id("org.jetbrains.kotlin.plugin.compose")
5 | }
6 |
7 | android {
8 | namespace = "com.jarroyo.feature.electricity"
9 | sourceSets["main"].apply {
10 | res.srcDirs("src/androidMain/res", "src/commonMain/resources")
11 | }
12 | }
13 |
14 | kotlin {
15 | // REVIEW reason of ld: framework 'FirebaseCore' not found
16 | tasks.matching { it.name == "linkDebugTestIosSimulatorArm64" }.configureEach {
17 | enabled = false
18 | }
19 | tasks.matching { it.name == "linkDebugTestIosX64" }.configureEach {
20 | enabled = false
21 | }
22 | sourceSets {
23 | androidMain.dependencies {
24 | }
25 | commonMain.dependencies {
26 | implementation(compose.ui)
27 | implementation(compose.foundation)
28 | implementation(compose.material)
29 | implementation(compose.runtime)
30 | implementation(libs.composemultiplatformcharts)
31 | implementation(libs.composemultiplatformcharts2)
32 | implementation(libs.jetbrains.kotlin.datetime)
33 | implementation(libs.sqldelight.coroutines)
34 |
35 | implementation(projects.modules.featureElectricityApi)
36 | implementation(projects.modules.libraryNavigation)
37 | implementation(projects.modules.libraryNetworkApi)
38 | implementation(projects.modules.libraryNetwork)
39 | }
40 |
41 | desktopMain.dependencies {
42 | }
43 |
44 | iosMain.dependencies {
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/modules/feature-electricity/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/modules/feature-electricity/consumer-rules.pro
--------------------------------------------------------------------------------
/modules/feature-electricity/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.
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
--------------------------------------------------------------------------------
/modules/feature-electricity/src/androidMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/modules/feature-electricity/src/androidMain/kotlin/com/jarroyo/feature/electricity/di/PlatformModule.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.electricity.di
2 |
3 | import org.koin.dsl.module
4 |
5 | actual fun electricityModule() = module {
6 | }
7 |
--------------------------------------------------------------------------------
/modules/feature-electricity/src/commonMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/modules/feature-electricity/src/commonMain/kotlin/com/jarroyo/feature/electricity/ElectricityFeature.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.electricity
2 |
3 | import androidx.compose.runtime.Composable
4 | import com.jarroyo.feature.electricity.api.destination.ElectricityDestination
5 | import com.jarroyo.feature.electricity.api.interactor.GetElectricityDataInteractor
6 | import com.jarroyo.feature.electricity.di.electricityModule
7 | import com.jarroyo.feature.electricity.ui.ElectricityViewModel
8 | import com.jarroyo.feature.electricity.interactor.GetElectricityDataInteractorImpl
9 | import com.jarroyo.feature.electricity.ui.ElectricityScreen
10 | import com.jarroyo.library.feature.Feature
11 | import com.jarroyo.library.navigation.api.destination.NavigationDestination
12 | import org.koin.core.module.Module
13 | import org.koin.core.module.dsl.factoryOf
14 | import org.koin.dsl.module
15 |
16 | class ElectricityFeature : Feature() {
17 | override val id = "Electricity"
18 |
19 | override val composableDestinations: Map Unit> = mapOf(
20 | ElectricityDestination to { ElectricityScreen() },
21 | )
22 |
23 | override val navigationSuiteEntry: NavigationSuiteEntry = NavigationSuiteEntry(
24 | priority = 0,
25 | route = ElectricityDestination.route,
26 | label = id,
27 | )
28 |
29 | companion object {
30 | val module: List = module {
31 | factory { GetElectricityDataInteractorImpl(get()) }
32 | factoryOf(::ElectricityViewModel)
33 | } + electricityModule()
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/modules/feature-electricity/src/commonMain/kotlin/com/jarroyo/feature/electricity/di/PlatformModule.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.electricity.di
2 |
3 | import org.koin.core.module.Module
4 |
5 | expect fun electricityModule(): Module
6 |
--------------------------------------------------------------------------------
/modules/feature-electricity/src/commonMain/kotlin/com/jarroyo/feature/electricity/interactor/GetElectricityDataInteractorImpl.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.electricity.interactor
2 |
3 | import com.github.michaelbull.result.Err
4 | import com.github.michaelbull.result.Ok
5 | import com.github.michaelbull.result.Result
6 | import com.jarroyo.feature.electricity.api.interactor.GetElectricityDataInteractor
7 | import com.jarroyo.library.network.di.ElectricityApi
8 | import com.jarroyo.library.network.di.ElectricityData
9 | import kotlinx.datetime.LocalDate
10 |
11 | internal class GetElectricityDataInteractorImpl(
12 | private val electricityApi: ElectricityApi,
13 | ) : GetElectricityDataInteractor {
14 | override suspend operator fun invoke(
15 | startDate: LocalDate,
16 | endDate: LocalDate,
17 | ): Result = try {
18 | Ok(electricityApi.fetchElectricityData(startDate, endDate))
19 | } catch (e: Exception) {
20 | Err(e)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/modules/feature-electricity/src/commonMain/kotlin/com/jarroyo/feature/electricity/ui/ElectricityContract.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.electricity.ui
2 |
3 | import com.jarroyo.library.network.di.ElectricityData
4 | import com.jarroyo.library.ui.shared.ViewEffect
5 | import com.jarroyo.library.ui.shared.ViewEvent
6 | import com.jarroyo.library.ui.shared.ViewState
7 |
8 | object ElectricityContract {
9 | data class State(
10 | val electricityData: ElectricityData? = null,
11 | val loading: Boolean = false,
12 | ) : ViewState
13 |
14 | sealed class Event : ViewEvent {
15 | data object OnUpButtonClicked: Event()
16 | data object OnSwipeToRefresh: Event()
17 | }
18 |
19 | sealed class Effect : ViewEffect {
20 | data class ShowSnackbar(val message: String) : Effect()
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/modules/feature-electricity/src/commonMain/kotlin/com/jarroyo/feature/electricity/ui/ElectricityViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.electricity.ui
2 |
3 | import androidx.lifecycle.viewModelScope
4 | import com.jarroyo.feature.electricity.api.interactor.GetElectricityDataInteractor
5 | import com.jarroyo.feature.electricity.ui.ElectricityContract.Effect
6 | import com.jarroyo.feature.electricity.ui.ElectricityContract.Event
7 | import com.jarroyo.feature.electricity.ui.ElectricityContract.State
8 | import com.jarroyo.library.navigation.api.navigator.AppNavigator
9 | import com.jarroyo.library.ui.shared.BaseViewModel
10 | import com.kizitonwose.calendar.core.now
11 | import kotlinx.coroutines.launch
12 | import kotlinx.datetime.LocalDate
13 |
14 | class ElectricityViewModel(
15 | private val appNavigator: AppNavigator,
16 | private val getElectricityDataInteractor: GetElectricityDataInteractor,
17 | ) : BaseViewModel() {
18 | init {
19 | refreshData()
20 | }
21 | override fun provideInitialState() = State()
22 |
23 | override fun handleEvent(event: Event) {
24 | when(event) {
25 | is Event.OnSwipeToRefresh -> refreshData()
26 | is Event.OnUpButtonClicked -> appNavigator.navigateUp()
27 | }
28 | }
29 |
30 | private fun refreshData() {
31 | viewModelScope.launch {
32 | updateState { copy(loading = true) }
33 | val result = getElectricityDataInteractor(startDate = LocalDate.now(), endDate = LocalDate.now())
34 | if (result.isOk) {
35 | updateState { copy(electricityData = result.value) }
36 | } else {
37 | sendEffect { Effect.ShowSnackbar(result.error.message.orEmpty()) }
38 | }
39 | updateState { copy(loading = false) }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/modules/feature-electricity/src/desktopMain/kotlin/com/jarroyo/feature/electricity/di/PlatformModule.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.electricity.di
2 |
3 | import org.koin.dsl.module
4 | import io.ktor.client.*
5 | import io.ktor.client.engine.java.*
6 |
7 |
8 | actual fun electricityModule() = module {
9 | single { Java.create() }
10 | }
11 |
--------------------------------------------------------------------------------
/modules/feature-electricity/src/nativeMain/kotlin/com/jarroyo/feature/electricity/di/PlatformModule.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.electricity.di
2 |
3 | import org.koin.dsl.module
4 |
5 | actual fun electricityModule() = module {
6 | }
7 |
--------------------------------------------------------------------------------
/modules/feature-home-api/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/modules/feature-home-api/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("composeapp.multiplatform-library-conventions")
3 | alias(libs.plugins.kotlinx.serialization)
4 | }
5 |
6 | android {
7 | namespace = "com.jarroyo.feature.home.api"
8 | resourcePrefix = "home_api_"
9 | defaultConfig {
10 | consumerProguardFiles("$projectDir/proguard-home-api-consumer-rules.pro")
11 | }
12 | }
13 |
14 | kotlin {
15 | sourceSets {
16 | commonMain.dependencies {
17 | api(libs.jetbrains.kotlinx.serialization)
18 |
19 | implementation(projects.modules.libraryNavigationApi)
20 | implementation(projects.modules.libraryNetworkApi)
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/modules/feature-home-api/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/modules/feature-home-api/consumer-rules.pro
--------------------------------------------------------------------------------
/modules/feature-home-api/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.
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
--------------------------------------------------------------------------------
/modules/feature-home-api/src/commonMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/modules/feature-home-shared/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/modules/feature-home-shared/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("composeapp.multiplatform-feature-conventions")
3 | id("org.jetbrains.compose")
4 | id("org.jetbrains.kotlin.plugin.compose")
5 | }
6 |
7 | android {
8 | namespace = "com.jarroyo.feature.home.shared"
9 | sourceSets["main"].apply {
10 | res.srcDirs("src/androidMain/res", "src/commonMain/resources")
11 | }
12 | }
13 |
14 | kotlin {
15 | targets.filterIsInstance()
16 | .forEach {
17 | it.binaries.filterIsInstance()
18 | .forEach { lib ->
19 | lib.isStatic = true
20 | lib.linkerOpts.add("-lsqlite3")
21 | }
22 | }
23 | // REVIEW reason of ld: framework 'FirebaseCore' not found
24 | tasks.matching { it.name == "linkDebugTestIosSimulatorArm64" }.configureEach {
25 | enabled = false
26 | }
27 | tasks.matching { it.name == "linkDebugTestIosX64" }.configureEach {
28 | enabled = false
29 | }
30 | sourceSets {
31 | androidMain.dependencies {
32 | }
33 | commonMain.dependencies {
34 | implementation(compose.ui)
35 | implementation(compose.foundation)
36 | implementation(compose.material)
37 | implementation(compose.runtime)
38 | implementation(libs.jetbrains.kotlin.datetime)
39 |
40 | implementation(projects.modules.featureCommon)
41 | implementation(projects.modules.featureElectricity)
42 | implementation(projects.modules.featureHomeApi)
43 | implementation(projects.modules.featureLaunches)
44 | implementation(projects.modules.featureLaunchesApi)
45 | implementation(projects.modules.featureSchedules)
46 | implementation(projects.modules.featureSchedulesApi)
47 | implementation(projects.modules.libraryNavigation)
48 | implementation(projects.modules.libraryNetworkApi)
49 | implementation(projects.modules.libraryNetwork)
50 | }
51 |
52 | desktopMain.dependencies {
53 | }
54 |
55 | iosMain.dependencies {
56 | }
57 | }
58 | }
--------------------------------------------------------------------------------
/modules/feature-home-shared/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
--------------------------------------------------------------------------------
/modules/feature-home-shared/src/androidMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/modules/feature-home-shared/src/commonMain/kotlin/com/jarroyo/feature/home/shared/di/FeaturesModule.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.home.shared.di
2 |
3 | import com.jarroyo.feature.common.CommonFeature
4 | import com.jarroyo.feature.electricity.ElectricityFeature
5 | import com.jarroyo.feature.home.shared.ui.HomeFeature
6 | import com.jarroyo.feature.launches.LaunchesFeature
7 | import com.jarroyo.feature.schedules.SchedulesFeature
8 | import com.jarroyo.library.feature.Feature
9 | import org.koin.dsl.module
10 |
11 | val featuresModule = module {
12 | single { CommonFeature() }
13 | single { ElectricityFeature() }
14 | single { HomeFeature() }
15 | single { LaunchesFeature() }
16 | single { SchedulesFeature() }
17 |
18 | single> {
19 | listOf(
20 | get(),
21 | get(),
22 | get(),
23 | get(),
24 | get(),
25 | )
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/modules/feature-home-shared/src/commonMain/kotlin/com/jarroyo/feature/home/shared/di/KoinAppModule.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.home.shared.di
2 |
3 | import com.jarroyo.feature.common.CommonFeature
4 | import com.jarroyo.feature.electricity.ElectricityFeature
5 | import com.jarroyo.feature.launches.LaunchesFeature
6 | import com.jarroyo.feature.schedules.SchedulesFeature
7 | import com.jarroyo.library.navigation.di.navigationModule
8 | import com.jarroyo.library.network.di.networkModule
9 | import org.koin.core.context.startKoin
10 | import org.koin.core.module.Module
11 |
12 | fun initKoin(additionalModules: List = emptyList()) =
13 | startKoin {
14 | modules(
15 | additionalModules +
16 | featuresModule +
17 | navigationModule +
18 | networkModule +
19 | CommonFeature.module +
20 | ElectricityFeature.module +
21 | LaunchesFeature.module +
22 | SchedulesFeature.module,
23 | )
24 | }
25 |
--------------------------------------------------------------------------------
/modules/feature-home-shared/src/commonMain/kotlin/com/jarroyo/feature/home/shared/ext/LocalDateTimeExt.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.home.shared.ext
2 |
3 | import kotlinx.datetime.LocalDateTime
4 | import kotlinx.datetime.format
5 | import kotlinx.datetime.format.char
6 |
7 | fun LocalDateTime.format(): String = format(LocalDateTime.Format {
8 | dayOfMonth()
9 | char('/')
10 | monthNumber()
11 | char('/')
12 | year()
13 | char(' ')
14 | hour()
15 | char(':')
16 | minute()
17 | char(':')
18 | second()
19 | })
20 |
--------------------------------------------------------------------------------
/modules/feature-home-shared/src/commonMain/kotlin/com/jarroyo/feature/home/shared/ui/HomeFeature.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.home.shared.ui
2 |
3 | import com.jarroyo.library.feature.Feature
4 |
5 | class HomeFeature : Feature() {
6 | override val id = "Home"
7 | }
8 |
--------------------------------------------------------------------------------
/modules/feature-home-shared/src/commonMain/kotlin/com/jarroyo/feature/home/shared/ui/MainNavigationBar.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.home.shared.ui
2 |
3 | import androidx.compose.material3.MaterialTheme
4 | import androidx.compose.material3.NavigationBar
5 | import androidx.compose.material3.NavigationBarDefaults
6 | import androidx.compose.material3.NavigationBarItemColors
7 | import androidx.compose.material3.NavigationBarItemDefaults
8 | import androidx.compose.material3.contentColorFor
9 | import androidx.compose.runtime.Composable
10 | import androidx.compose.runtime.getValue
11 | import androidx.compose.ui.graphics.Color
12 | import androidx.compose.ui.unit.Dp
13 | import androidx.navigation.NavHostController
14 | import androidx.navigation.compose.currentBackStackEntryAsState
15 | import com.jarroyo.feature.schedules.navigationsuite.ScheduleListNavigationBarItem
16 | import com.jarroyo.library.feature.Feature
17 |
18 | @Composable
19 | fun MainNavigationBar(
20 | navController: NavHostController,
21 | mainNavigationBarEntries: Map,
22 | containerColor: Color = NavigationBarDefaults.containerColor,
23 | contentColor: Color = MaterialTheme.colorScheme.contentColorFor(containerColor),
24 | tonalElevation: Dp = NavigationBarDefaults.Elevation,
25 | itemColors: NavigationBarItemColors = NavigationBarItemDefaults.colors(),
26 | ) {
27 | val navBackStackEntry by navController.currentBackStackEntryAsState()
28 | val currentRoute = navBackStackEntry?.run { destination.route }
29 | val hideBottomNav = mainNavigationBarEntries[currentRoute]?.hideBottomNav != false
30 |
31 | if (!hideBottomNav) {
32 | NavigationBar(
33 | containerColor = containerColor,
34 | contentColor = contentColor,
35 | tonalElevation = tonalElevation,
36 | ) {
37 | mainNavigationBarEntries.values.forEach { entry ->
38 | ScheduleListNavigationBarItem(
39 | selected = currentRoute == entry.route,
40 | onClick = {
41 | navController.navigate(entry.route) {
42 | if (!entry.hideBottomNav) {
43 | restoreState = true
44 | popUpTo(0) {
45 | inclusive = true
46 | saveState = true
47 | }
48 | launchSingleTop = true
49 | }
50 | }
51 | },
52 | label = entry.label,
53 | )
54 | }
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/modules/feature-home-shared/src/commonMain/resources/drawable/ui_ic_arrow_back.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/modules/feature-home-shared/src/nativeMain/kotlin/MainViewController.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.nativeMain.kotlin
2 |
3 | import androidx.compose.ui.window.ComposeUIViewController
4 | import com.jarroyo.feature.home.shared.ui.RootView
5 |
6 | fun MainViewController() = ComposeUIViewController { RootView() }
7 |
--------------------------------------------------------------------------------
/modules/feature-launches-api/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/modules/feature-launches-api/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("composeapp.multiplatform-library-conventions")
3 | alias(libs.plugins.kotlinx.serialization)
4 | }
5 |
6 | android {
7 | namespace = "com.jarroyo.feature.launches.api"
8 | resourcePrefix = "launches_api_"
9 | defaultConfig {
10 | consumerProguardFiles("$projectDir/proguard-home-api-consumer-rules.pro")
11 | }
12 | }
13 |
14 | kotlin {
15 | sourceSets {
16 | commonMain.dependencies {
17 | api(libs.jetbrains.kotlinx.serialization)
18 |
19 | implementation(projects.modules.libraryNavigationApi)
20 | implementation(projects.modules.libraryNetworkApi)
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/modules/feature-launches-api/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/modules/feature-launches-api/consumer-rules.pro
--------------------------------------------------------------------------------
/modules/feature-launches-api/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.
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
--------------------------------------------------------------------------------
/modules/feature-launches-api/src/commonMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/modules/feature-launches-api/src/commonMain/kotlin/com/jarroyo/feature/launches/api/destination/LaunchDestination.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.launches.api.destination
2 |
3 | import androidx.lifecycle.SavedStateHandle
4 | import androidx.navigation.NamedNavArgument
5 | import androidx.navigation.NavType
6 | import androidx.navigation.navArgument
7 | import com.jarroyo.library.navigation.api.destination.NavigationDestination
8 | import kotlinx.serialization.Serializable
9 |
10 | object LaunchDestination : NavigationDestination() {
11 | private const val ID_PARAM = "id"
12 | override val route: String = "launch/{$ID_PARAM}"
13 |
14 | override val arguments: List = listOf(
15 | navArgument(ID_PARAM) {
16 | type = NavType.StringType
17 | nullable = true
18 | defaultValue = null
19 | },
20 | )
21 |
22 | fun get(id: String): String = route.replace("{$ID_PARAM}", id)
23 |
24 | @Serializable
25 | data class Result(
26 | override val id: Long = uniqueId,
27 | val type: String,
28 | val name: String,
29 | ) : NavigationDestination.Result()
30 |
31 | object Arguments {
32 | fun getId(savedStateHandle: SavedStateHandle): String? = savedStateHandle[ID_PARAM]
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/modules/feature-launches-api/src/commonMain/kotlin/com/jarroyo/feature/launches/api/destination/LaunchListDestination.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.launches.api.destination
2 |
3 | import com.jarroyo.library.navigation.api.destination.NavigationDestination
4 |
5 | object LaunchListDestination: NavigationDestination() {
6 | override val route: String = "launchList"
7 | }
8 |
--------------------------------------------------------------------------------
/modules/feature-launches-api/src/commonMain/kotlin/com/jarroyo/feature/launches/api/interactor/AddFavoriteInteractor.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.launches.api.interactor
2 |
3 | import com.github.michaelbull.result.Result
4 | import com.jarroyo.composeapp.library.network.api.graphql.fragment.LaunchFragment
5 |
6 | interface AddFavoriteInteractor {
7 | suspend operator fun invoke(
8 | launch: LaunchFragment,
9 | ): Result
10 | }
11 |
--------------------------------------------------------------------------------
/modules/feature-launches-api/src/commonMain/kotlin/com/jarroyo/feature/launches/api/interactor/GetFavoritesInteractor.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.launches.api.interactor
2 |
3 | import com.github.michaelbull.result.Result
4 |
5 | interface GetFavoritesInteractor {
6 | suspend operator fun invoke(): Result, Exception>
7 | }
8 |
--------------------------------------------------------------------------------
/modules/feature-launches-api/src/commonMain/kotlin/com/jarroyo/feature/launches/api/interactor/GetLaunchDetailInteractor.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.launches.api.interactor
2 |
3 | import com.apollographql.apollo.cache.normalized.FetchPolicy
4 | import com.github.michaelbull.result.Result
5 | import com.jarroyo.composeapp.library.network.api.graphql.fragment.LaunchFragment
6 |
7 | interface GetLaunchDetailInteractor {
8 | suspend operator fun invoke(
9 | id: String,
10 | fetchPolicy: FetchPolicy = FetchPolicy.CacheFirst,
11 | ): Result
12 | }
13 |
--------------------------------------------------------------------------------
/modules/feature-launches-api/src/commonMain/kotlin/com/jarroyo/feature/launches/api/interactor/GetLaunchesInteractor.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.launches.api.interactor
2 |
3 | import com.apollographql.apollo.cache.normalized.FetchPolicy
4 | import com.github.michaelbull.result.Result
5 | import com.jarroyo.composeapp.library.network.api.graphql.fragment.LaunchFragment
6 |
7 | interface GetLaunchesInteractor {
8 | suspend operator fun invoke(
9 | page: Int = 1,
10 | pageSize: Int = 20,
11 | fetchPolicy: FetchPolicy = FetchPolicy.NetworkFirst,
12 | ): Result?, Exception>
13 | }
14 |
--------------------------------------------------------------------------------
/modules/feature-launches-api/src/commonMain/kotlin/com/jarroyo/feature/launches/api/interactor/RemoveFavoriteInteractor.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.launches.api.interactor
2 |
3 | import com.github.michaelbull.result.Result
4 |
5 | interface RemoveFavoriteInteractor {
6 | suspend operator fun invoke(
7 | id: String,
8 | ): Result
9 | }
10 |
--------------------------------------------------------------------------------
/modules/feature-launches/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/modules/feature-launches/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("composeapp.multiplatform-feature-conventions")
3 | id("org.jetbrains.compose")
4 | id("org.jetbrains.kotlin.plugin.compose")
5 | alias(libs.plugins.sqldelight)
6 | }
7 |
8 | android {
9 | namespace = "com.jarroyo.launches.schedules"
10 | sourceSets["main"].apply {
11 | res.srcDirs("src/androidMain/res", "src/commonMain/resources")
12 | }
13 | }
14 |
15 | sqldelight {
16 | databases {
17 | create("Database") {
18 | packageName.set("com.jarroyo.feature.launches.sqldelight")
19 | }
20 | }
21 | linkSqlite.set(true)
22 | }
23 |
24 | kotlin {
25 | // REVIEW reason of ld: framework 'FirebaseCore' not found
26 | tasks.matching { it.name == "linkDebugTestIosSimulatorArm64" }.configureEach {
27 | enabled = false
28 | }
29 | tasks.matching { it.name == "linkDebugTestIosX64" }.configureEach {
30 | enabled = false
31 | }
32 | sourceSets {
33 | androidMain.dependencies {
34 | implementation(libs.sqldelight.androidDriver)
35 | }
36 | commonMain.dependencies {
37 | implementation(compose.ui)
38 | implementation(compose.foundation)
39 | implementation(compose.material)
40 | implementation(compose.runtime)
41 | implementation(libs.jetbrains.kotlin.datetime)
42 | implementation(libs.sqldelight.coroutines)
43 |
44 | implementation(projects.modules.featureCommonApi)
45 | implementation(projects.modules.featureLaunchesApi)
46 | implementation(projects.modules.libraryNavigation)
47 | implementation(projects.modules.libraryNetworkApi)
48 | implementation(projects.modules.libraryNetwork)
49 | }
50 |
51 | desktopMain.dependencies {
52 | implementation(libs.sqldelight.jvmDriver)
53 | }
54 |
55 | iosMain.dependencies {
56 | implementation(libs.sqldelight.nativeDriver)
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/modules/feature-launches/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/modules/feature-launches/consumer-rules.pro
--------------------------------------------------------------------------------
/modules/feature-launches/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.
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
--------------------------------------------------------------------------------
/modules/feature-launches/src/androidMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/modules/feature-launches/src/androidMain/kotlin/com/jarroyo/feature/launches/di/PlatformModule.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.launches.di
2 |
3 | import app.cash.sqldelight.driver.android.AndroidSqliteDriver
4 | import com.jarroyo.feature.launches.sqldelight.Database
5 | import com.jarroyo.feature.launches.sqldelight.DatabaseWrapper
6 | import io.ktor.client.engine.android.Android
7 | import org.koin.dsl.module
8 |
9 | actual fun launchesModule() = module {
10 | single {
11 | val driver = AndroidSqliteDriver(Database.Schema, get(), "Rockets.db")
12 | DatabaseWrapper(Database(driver))
13 | }
14 | single { Android.create() }
15 | }
16 |
--------------------------------------------------------------------------------
/modules/feature-launches/src/commonMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/modules/feature-launches/src/commonMain/kotlin/com/jarroyo/feature/launches/LaunchesFeature.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.launches
2 |
3 | import androidx.compose.runtime.Composable
4 | import com.jarroyo.feature.launches.api.interactor.AddFavoriteInteractor
5 | import com.jarroyo.feature.launches.api.interactor.GetLaunchDetailInteractor
6 | import com.jarroyo.feature.launches.api.interactor.RemoveFavoriteInteractor
7 | import com.jarroyo.feature.launches.api.destination.LaunchDestination
8 | import com.jarroyo.feature.launches.api.destination.LaunchListDestination
9 | import com.jarroyo.feature.launches.api.interactor.GetFavoritesInteractor
10 | import com.jarroyo.feature.launches.api.interactor.GetLaunchesInteractor
11 | import com.jarroyo.feature.launches.di.launchesModule
12 | import com.jarroyo.feature.launches.interactor.AddFavoriteInteractorImpl
13 | import com.jarroyo.feature.launches.interactor.GetFavoritesInteractorImpl
14 | import com.jarroyo.feature.launches.interactor.GetLaunchDetailInteractorImpl
15 | import com.jarroyo.feature.launches.interactor.GetLaunchesInteractorImpl
16 | import com.jarroyo.feature.launches.interactor.RemoveFavoriteInteractorImpl
17 | import com.jarroyo.feature.launches.ui.launchdetail.LaunchDetailScreen
18 | import com.jarroyo.feature.launches.ui.launchlist.LaunchListScreen
19 | import com.jarroyo.feature.launches.ui.launchlist.LaunchListViewModel
20 | import com.jarroyo.feature.launches.ui.launchdetail.LaunchDetailViewModel
21 | import com.jarroyo.library.feature.Feature
22 | import com.jarroyo.library.navigation.api.destination.NavigationDestination
23 | import org.koin.core.module.Module
24 | import org.koin.core.module.dsl.factoryOf
25 | import org.koin.dsl.module
26 |
27 | class LaunchesFeature : Feature() {
28 | override val id = "Launches"
29 |
30 | override val composableDestinations: Map Unit> = mapOf(
31 | LaunchDestination to { LaunchDetailScreen() },
32 | LaunchListDestination to { LaunchListScreen() },
33 | )
34 |
35 | override val navigationSuiteEntry: NavigationSuiteEntry = NavigationSuiteEntry(
36 | priority = 0,
37 | route = LaunchListDestination.route,
38 | label = "Launches",
39 | )
40 |
41 | companion object {
42 | val module: List = module {
43 | factory { AddFavoriteInteractorImpl(get()) }
44 | factory { GetFavoritesInteractorImpl(get()) }
45 | factory { GetLaunchesInteractorImpl(get()) }
46 | factory { GetLaunchDetailInteractorImpl(get()) }
47 | factory { RemoveFavoriteInteractorImpl(get()) }
48 |
49 | factoryOf(::LaunchDetailViewModel)
50 | factoryOf(::LaunchListViewModel)
51 | } + launchesModule()
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/modules/feature-launches/src/commonMain/kotlin/com/jarroyo/feature/launches/di/PlatformModule.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.launches.di
2 |
3 | import org.koin.core.module.Module
4 |
5 | expect fun launchesModule(): Module
6 |
--------------------------------------------------------------------------------
/modules/feature-launches/src/commonMain/kotlin/com/jarroyo/feature/launches/interactor/AddFavoriteInteractorImpl.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.launches.interactor
2 |
3 | import com.github.michaelbull.result.Err
4 | import com.github.michaelbull.result.Ok
5 | import com.github.michaelbull.result.Result
6 | import com.jarroyo.composeapp.library.network.api.graphql.fragment.LaunchFragment
7 | import com.jarroyo.feature.launches.sqldelight.DatabaseWrapper
8 | import com.jarroyo.feature.launches.sqldelight.dao.FavoriteRocketsDao
9 | import com.jarroyo.feature.launches.FavoriteRockets
10 | import com.jarroyo.feature.launches.api.interactor.AddFavoriteInteractor
11 | import org.koin.core.component.KoinComponent
12 |
13 | internal class AddFavoriteInteractorImpl(
14 | private val databaseWrapper: DatabaseWrapper,
15 | ) : AddFavoriteInteractor, KoinComponent {
16 | override suspend operator fun invoke(
17 | launch: LaunchFragment,
18 | ): Result = try {
19 | FavoriteRocketsDao(checkNotNull(databaseWrapper.instance)).insert(
20 | FavoriteRockets(
21 | checkNotNull(launch.id), launch.mission_name,
22 | ),
23 | )
24 | Ok(true)
25 | } catch (e: Exception) {
26 | Err(e)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/modules/feature-launches/src/commonMain/kotlin/com/jarroyo/feature/launches/interactor/GetFavoritesInteractorImpl.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.launches.interactor
2 |
3 | import com.github.michaelbull.result.Err
4 | import com.github.michaelbull.result.Ok
5 | import com.github.michaelbull.result.Result
6 | import com.jarroyo.feature.launches.api.interactor.GetFavoritesInteractor
7 | import com.jarroyo.feature.launches.sqldelight.DatabaseWrapper
8 | import com.jarroyo.feature.launches.sqldelight.dao.FavoriteRocketsDao
9 |
10 | internal class GetFavoritesInteractorImpl(
11 | private val databaseWrapper: DatabaseWrapper,
12 | ) : GetFavoritesInteractor {
13 | override suspend operator fun invoke(): Result, Exception> = try {
14 | Ok(FavoriteRocketsDao(checkNotNull(databaseWrapper.instance)).select().map { it.id })
15 | } catch (e: Exception) {
16 | Err(e)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/modules/feature-launches/src/commonMain/kotlin/com/jarroyo/feature/launches/interactor/GetLaunchDetailInteractorImpl.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.launches.interactor
2 |
3 | import com.apollographql.apollo.ApolloClient
4 | import com.apollographql.apollo.cache.normalized.FetchPolicy
5 | import com.apollographql.apollo.cache.normalized.fetchPolicy
6 | import com.github.michaelbull.result.Ok
7 | import com.github.michaelbull.result.Result
8 | import com.jarroyo.composeapp.library.network.api.graphql.LaunchDetailQuery
9 | import com.jarroyo.composeapp.library.network.api.graphql.fragment.LaunchFragment
10 | import com.jarroyo.feature.launches.api.interactor.GetLaunchDetailInteractor
11 | import com.jarroyo.library.network.api.ext.executeAndHandleErrors
12 | import org.koin.core.component.KoinComponent
13 |
14 | internal class GetLaunchDetailInteractorImpl(
15 | private val apolloClient: ApolloClient,
16 | ) : GetLaunchDetailInteractor, KoinComponent {
17 | override suspend operator fun invoke(
18 | id: String,
19 | fetchPolicy: FetchPolicy,
20 | ): Result = apolloClient
21 | .query(LaunchDetailQuery(id))
22 | .fetchPolicy(fetchPolicy)
23 | .executeAndHandleErrors { response ->
24 | Ok(response.data?.launch?.launchFragment)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/modules/feature-launches/src/commonMain/kotlin/com/jarroyo/feature/launches/interactor/GetLaunchesInteractorImpl.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.launches.interactor
2 |
3 | import com.apollographql.apollo.ApolloClient
4 | import com.apollographql.apollo.cache.normalized.FetchPolicy
5 | import com.apollographql.apollo.cache.normalized.fetchPolicy
6 | import com.github.michaelbull.result.Ok
7 | import com.github.michaelbull.result.Result
8 | import com.jarroyo.composeapp.library.network.api.graphql.LaunchesQuery
9 | import com.jarroyo.composeapp.library.network.api.graphql.fragment.LaunchFragment
10 | import com.jarroyo.feature.launches.api.interactor.GetLaunchesInteractor
11 | import com.jarroyo.library.network.api.ext.executeAndHandleErrors
12 |
13 | internal class GetLaunchesInteractorImpl(
14 | private val apolloClient: ApolloClient,
15 | ) : GetLaunchesInteractor {
16 | override suspend operator fun invoke(
17 | page: Int,
18 | pageSize: Int,
19 | fetchPolicy: FetchPolicy,
20 | ): Result?, Exception> = apolloClient
21 | .query(LaunchesQuery())
22 | .fetchPolicy(fetchPolicy)
23 | .executeAndHandleErrors { response ->
24 | Ok(response.data?.launches?.filterNotNull()?.map { it.launchFragment })
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/modules/feature-launches/src/commonMain/kotlin/com/jarroyo/feature/launches/interactor/RemoveFavoriteInteractorImpl.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.launches.interactor
2 |
3 | import com.github.michaelbull.result.Err
4 | import com.github.michaelbull.result.Ok
5 | import com.github.michaelbull.result.Result
6 | import com.jarroyo.feature.launches.api.interactor.RemoveFavoriteInteractor
7 | import com.jarroyo.feature.launches.sqldelight.DatabaseWrapper
8 | import com.jarroyo.feature.launches.sqldelight.dao.FavoriteRocketsDao
9 |
10 | internal class RemoveFavoriteInteractorImpl(
11 | private val databaseWrapper: DatabaseWrapper,
12 | ) : RemoveFavoriteInteractor {
13 | override suspend operator fun invoke(
14 | id: String,
15 | ): Result = try {
16 | FavoriteRocketsDao(checkNotNull(databaseWrapper.instance)).removeItem(id)
17 | Ok(true)
18 | } catch (e: Exception) {
19 | Err(e)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/modules/feature-launches/src/commonMain/kotlin/com/jarroyo/feature/launches/sqldelight/DatabaseWrapper.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.launches.sqldelight
2 |
3 | data class DatabaseWrapper(val instance: Database?)
4 |
--------------------------------------------------------------------------------
/modules/feature-launches/src/commonMain/kotlin/com/jarroyo/feature/launches/sqldelight/dao/FavoriteRocketsDao.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.launches.sqldelight.dao
2 |
3 | import co.touchlab.kermit.Logger
4 | import com.jarroyo.feature.launches.FavoriteRockets
5 | import com.jarroyo.feature.launches.sqldelight.Database
6 |
7 | class FavoriteRocketsDao(database: Database) {
8 | private val db = database.favoriteRocketsQueries
9 |
10 | internal fun insert(item: FavoriteRockets) {
11 | Logger.d("Insert: $item")
12 | db.insertFavorite(item.id, item.title)
13 | }
14 |
15 | internal fun removeItem(id: String) {
16 | Logger.d("RemoveItem: $id")
17 | db.removeFavorite(id)
18 | }
19 |
20 | internal fun deleteAll() {
21 | db.removeAllFavoritesRockets()
22 | }
23 |
24 | internal fun select(): List = db.selectFavorites().executeAsList()
25 | }
26 |
--------------------------------------------------------------------------------
/modules/feature-launches/src/commonMain/kotlin/com/jarroyo/feature/launches/ui/launchdetail/LaunchDetailContract.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.launches.ui.launchdetail
2 |
3 | import androidx.compose.runtime.Immutable
4 | import com.jarroyo.composeapp.library.network.api.graphql.fragment.LaunchFragment
5 | import com.jarroyo.feature.launches.api.destination.LaunchDestination
6 | import com.jarroyo.library.ui.shared.ViewEffect
7 | import com.jarroyo.library.ui.shared.ViewEvent
8 | import com.jarroyo.library.ui.shared.ViewState
9 |
10 | @Immutable
11 | object LaunchDetailContract {
12 | data class State(
13 | val favorite: Boolean? = null,
14 | val launch: LaunchFragment? = null,
15 | val loading: Boolean = false,
16 | ) : ViewState
17 |
18 | sealed class Event : ViewEvent {
19 | data class OnOpenUrl(val url: String): Event()
20 | data class OnViewAttached(val id: String): Event()
21 | data object OnAddFavoritesButtonClicked: Event()
22 | data object OnUpButtonClicked: Event()
23 | }
24 |
25 | sealed class Effect : ViewEffect {
26 | data class SetResultAndNavigate(
27 | val result: LaunchDestination.Result,
28 | val navigate: () -> Unit,
29 | ) : Effect()
30 | data class ShowSnackbar(val message: String) : Effect()
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/modules/feature-launches/src/commonMain/kotlin/com/jarroyo/feature/launches/ui/launchlist/LaunchListContract.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.launches.ui.launchlist
2 |
3 | import com.jarroyo.composeapp.library.network.api.graphql.fragment.LaunchFragment
4 | import com.jarroyo.library.ui.shared.ViewEffect
5 | import com.jarroyo.library.ui.shared.ViewEvent
6 | import com.jarroyo.library.ui.shared.ViewState
7 | import kotlinx.datetime.LocalDateTime
8 |
9 | object LaunchListContract {
10 | data class State(
11 | val currentLocalDateTime: LocalDateTime? = null,
12 | val favoritesList: List? = null,
13 | val loading: Boolean = false,
14 | val rocketList: List? = null,
15 | ) : ViewState
16 |
17 | sealed class Event : ViewEvent {
18 | data class OnItemClicked(val id: String) : Event()
19 | data class OnLaunchUpdated(val type: String, val name: String): Event()
20 | data object FavoritesUpdated: Event()
21 | data object OnSwipeToRefresh: Event()
22 | }
23 |
24 | sealed class Effect : ViewEffect {
25 | data class ShowSnackbar(val message: String) : Effect()
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/modules/feature-launches/src/commonMain/sqldelight/com/jarroyo/feature/launches/FavoriteRockets.sq:
--------------------------------------------------------------------------------
1 | CREATE TABLE IF NOT EXISTS FavoriteRockets (
2 | id TEXT NOT NULL PRIMARY KEY,
3 | title TEXT
4 | );
5 |
6 | insertFavorite:
7 | INSERT INTO FavoriteRockets(id, title)
8 | VALUES(?, ?);
9 |
10 | removeFavorite:
11 | DELETE FROM FavoriteRockets WHERE id = ?;
12 |
13 | removeAllFavoritesRockets:
14 | DELETE FROM FavoriteRockets;
15 |
16 | selectFavorites:
17 | SELECT * FROM FavoriteRockets;
--------------------------------------------------------------------------------
/modules/feature-launches/src/desktopMain/kotlin/com/jarroyo/feature/launches/di/PlatformModule.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.launches.di
2 |
3 | import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver
4 | import com.jarroyo.feature.launches.sqldelight.Database
5 | import com.jarroyo.feature.launches.sqldelight.DatabaseWrapper
6 | import org.koin.dsl.module
7 |
8 | actual fun launchesModule() = module {
9 | single {
10 | val url = "jdbc:sqlite:./database/Rockets.db"
11 | val driver = JdbcSqliteDriver(url).also { Database.Schema.create(it) }
12 | DatabaseWrapper(Database(driver))
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/modules/feature-launches/src/nativeMain/kotlin/com/jarroyo/feature/launches/di/PlatformModule.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.launches.di
2 |
3 | import app.cash.sqldelight.driver.native.NativeSqliteDriver
4 | import com.jarroyo.feature.launches.sqldelight.Database
5 | import com.jarroyo.feature.launches.sqldelight.DatabaseWrapper
6 | import io.ktor.client.engine.darwin.Darwin
7 | import org.koin.dsl.module
8 |
9 | actual fun launchesModule() = module {
10 | single {
11 | val driver = NativeSqliteDriver(Database.Schema, "Rockets.db")
12 | DatabaseWrapper(Database(driver))
13 | }
14 | single { Darwin.create() }
15 | }
16 |
--------------------------------------------------------------------------------
/modules/feature-schedules-api/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/modules/feature-schedules-api/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("composeapp.multiplatform-library-conventions")
3 | alias(libs.plugins.kotlinx.serialization)
4 | }
5 |
6 | android {
7 | namespace = "com.jarroyo.feature.schedules.api"
8 | resourcePrefix = "schedules_api_"
9 | defaultConfig {
10 | consumerProguardFiles("$projectDir/proguard-home-api-consumer-rules.pro")
11 | }
12 | }
13 |
14 | kotlin {
15 | sourceSets {
16 | commonMain.dependencies {
17 | api(libs.jetbrains.kotlinx.serialization)
18 |
19 | implementation(projects.modules.libraryNavigationApi)
20 | implementation(projects.modules.libraryNetworkApi)
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/modules/feature-schedules-api/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/modules/feature-schedules-api/consumer-rules.pro
--------------------------------------------------------------------------------
/modules/feature-schedules-api/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.
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
--------------------------------------------------------------------------------
/modules/feature-schedules-api/src/commonMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/modules/feature-schedules-api/src/commonMain/kotlin/com/jarroyo/feature/schedules/api/destination/ScheduleDetailDetail.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.schedules.api.destination
2 |
3 | import androidx.lifecycle.SavedStateHandle
4 | import androidx.navigation.NamedNavArgument
5 | import androidx.navigation.NavType
6 | import androidx.navigation.navArgument
7 | import com.jarroyo.library.navigation.api.destination.NavigationDestination
8 | import kotlinx.serialization.Serializable
9 |
10 | object ScheduleDetailDestination: NavigationDestination() {
11 | private const val ID_PARAM = "id"
12 | override val route: String = "scheduleDetail/{$ID_PARAM}"
13 |
14 | override val arguments: List = listOf(
15 | navArgument(ID_PARAM) {
16 | type = NavType.StringType
17 | nullable = true
18 | defaultValue = null
19 | },
20 | )
21 |
22 | fun get(id: String): String = route.replace("{$ID_PARAM}", id)
23 |
24 | @Serializable
25 | data class Result(
26 | override val id: Long = uniqueId,
27 | val operationType: OperationType,
28 | ) : NavigationDestination.Result() {
29 | enum class OperationType {
30 | CREATE,
31 | REMOVE,
32 | UPDATE,
33 | }
34 | }
35 |
36 | object Arguments {
37 | fun getId(savedStateHandle: SavedStateHandle): String? = savedStateHandle[ID_PARAM]
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/modules/feature-schedules-api/src/commonMain/kotlin/com/jarroyo/feature/schedules/api/destination/ScheduleListDestination.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.schedules.api.destination
2 |
3 | import com.jarroyo.library.navigation.api.destination.NavigationDestination
4 |
5 | object ScheduleListDestination: NavigationDestination() {
6 | override val route: String = "schedules"
7 | }
8 |
--------------------------------------------------------------------------------
/modules/feature-schedules-api/src/commonMain/kotlin/com/jarroyo/feature/schedules/api/destination/UserSelectorDestination.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.schedules.api.destination
2 |
3 | import androidx.lifecycle.SavedStateHandle
4 | import androidx.navigation.NamedNavArgument
5 | import androidx.navigation.NavType
6 | import androidx.navigation.navArgument
7 | import com.jarroyo.feature.schedules.api.model.User
8 | import com.jarroyo.library.navigation.api.destination.NavigationDestination
9 | import kotlinx.serialization.Serializable
10 |
11 | object UserSelectorDestination: NavigationDestination() {
12 | private const val USER_LIST_PARAM = "USER_LIST_PARAM"
13 | override val route: String = "userSelector/{$USER_LIST_PARAM}"
14 |
15 | override val arguments: List = listOf(
16 | navArgument(USER_LIST_PARAM) {
17 | type = NavType.StringType
18 | nullable = true
19 | defaultValue = null
20 | },
21 | )
22 |
23 | fun get(userList: String): String = route.replace("{$USER_LIST_PARAM}", userList)
24 |
25 | @Serializable
26 | data class Result(
27 | override val id: Long = uniqueId,
28 | val userList: List,
29 | ) : NavigationDestination.Result()
30 |
31 | object Arguments {
32 | fun getUserList(savedStateHandle: SavedStateHandle): String? = savedStateHandle[USER_LIST_PARAM]
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/modules/feature-schedules-api/src/commonMain/kotlin/com/jarroyo/feature/schedules/api/ext/UserExt.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.schedules.api.ext
2 |
3 | import com.jarroyo.feature.schedules.api.model.User
4 |
5 | fun User.getFullName() = "$name $lastname"
6 |
--------------------------------------------------------------------------------
/modules/feature-schedules-api/src/commonMain/kotlin/com/jarroyo/feature/schedules/api/interactor/AddSchedulesInteractor.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.schedules.api.interactor
2 |
3 | import com.github.michaelbull.result.Result
4 | import com.jarroyo.feature.schedules.api.model.Schedule
5 |
6 | interface AddScheduleInteractor {
7 | suspend operator fun invoke(
8 | schedule: Schedule,
9 | ): Result
10 | }
11 |
--------------------------------------------------------------------------------
/modules/feature-schedules-api/src/commonMain/kotlin/com/jarroyo/feature/schedules/api/interactor/GetScheduleInteractor.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.schedules.api.interactor
2 |
3 | import com.github.michaelbull.result.Result
4 | import com.jarroyo.feature.schedules.api.model.Schedule
5 |
6 | interface GetScheduleInteractor {
7 | suspend operator fun invoke(
8 | id: String,
9 | ): Result
10 | }
11 |
--------------------------------------------------------------------------------
/modules/feature-schedules-api/src/commonMain/kotlin/com/jarroyo/feature/schedules/api/interactor/GetSchedulesInteractor.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.schedules.api.interactor
2 |
3 | import com.github.michaelbull.result.Result
4 | import com.jarroyo.feature.schedules.api.model.Schedule
5 |
6 | interface GetSchedulesInteractor {
7 | suspend operator fun invoke(
8 | ): Result?, Exception>
9 | }
10 |
--------------------------------------------------------------------------------
/modules/feature-schedules-api/src/commonMain/kotlin/com/jarroyo/feature/schedules/api/interactor/GetUserInteractor.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.schedules.api.interactor
2 |
3 | import com.github.michaelbull.result.Result
4 | import com.jarroyo.feature.schedules.api.model.User
5 |
6 | interface GetUserInteractor {
7 | suspend operator fun invoke(
8 | id: String,
9 | ): Result
10 | }
11 |
--------------------------------------------------------------------------------
/modules/feature-schedules-api/src/commonMain/kotlin/com/jarroyo/feature/schedules/api/interactor/GetUserListInteractor.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.schedules.api.interactor
2 |
3 | import com.github.michaelbull.result.Result
4 | import com.jarroyo.feature.schedules.api.model.User
5 |
6 | interface GetUserListInteractor {
7 | suspend operator fun invoke(
8 | ): Result?, Exception>
9 | }
10 |
--------------------------------------------------------------------------------
/modules/feature-schedules-api/src/commonMain/kotlin/com/jarroyo/feature/schedules/api/interactor/RemoveSchedulesInteractor.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.schedules.api.interactor
2 |
3 | import com.github.michaelbull.result.Result
4 |
5 | interface RemoveScheduleInteractor {
6 | suspend operator fun invoke(
7 | id: String,
8 | ): Result
9 | }
10 |
--------------------------------------------------------------------------------
/modules/feature-schedules-api/src/commonMain/kotlin/com/jarroyo/feature/schedules/api/model/Schedule.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.schedules.api.model
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class Schedule(
7 | val id: String,
8 | val time: String,
9 | val slots: Int,
10 | val users: List,
11 | )
12 |
--------------------------------------------------------------------------------
/modules/feature-schedules-api/src/commonMain/kotlin/com/jarroyo/feature/schedules/api/model/User.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.schedules.api.model
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class User(
7 | val id: String,
8 | val lastname: String,
9 | val name: String,
10 | )
11 |
--------------------------------------------------------------------------------
/modules/feature-schedules/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/modules/feature-schedules/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("composeapp.multiplatform-feature-conventions")
3 | id("org.jetbrains.compose")
4 | id("org.jetbrains.kotlin.plugin.compose")
5 | }
6 |
7 | android {
8 | namespace = "com.jarroyo.feature.schedules"
9 | sourceSets["main"].apply {
10 | res.srcDirs("src/androidMain/res", "src/commonMain/resources")
11 | }
12 | }
13 |
14 | kotlin {
15 | // REVIEW reason of ld: framework 'FirebaseCore' not found
16 | tasks.matching { it.name == "linkDebugTestIosSimulatorArm64" }.configureEach {
17 | enabled = false
18 | }
19 | tasks.matching { it.name == "linkDebugTestIosX64" }.configureEach {
20 | enabled = false
21 | }
22 | sourceSets {
23 | androidMain.dependencies {
24 | }
25 | commonMain.dependencies {
26 | implementation(compose.ui)
27 | implementation(compose.foundation)
28 | implementation(compose.material)
29 | implementation(compose.runtime)
30 | implementation(libs.jetbrains.kotlin.datetime)
31 | implementation(libs.sqldelight.coroutines)
32 |
33 | implementation(projects.modules.featureSchedulesApi)
34 | implementation(projects.modules.libraryNavigation)
35 | implementation(projects.modules.libraryNetworkApi)
36 | implementation(projects.modules.libraryNetwork)
37 | }
38 |
39 | desktopMain.dependencies {
40 |
41 | }
42 |
43 | iosMain.dependencies {
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/modules/feature-schedules/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/modules/feature-schedules/consumer-rules.pro
--------------------------------------------------------------------------------
/modules/feature-schedules/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.
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
--------------------------------------------------------------------------------
/modules/feature-schedules/src/commonMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/modules/feature-schedules/src/commonMain/kotlin/com/jarroyo/feature/schedules/interactor/AddScheduleInteractorImpl.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.schedules.interactor
2 |
3 | import com.github.michaelbull.result.Err
4 | import com.github.michaelbull.result.Ok
5 | import com.github.michaelbull.result.Result
6 | import com.jarroyo.feature.schedules.api.interactor.AddScheduleInteractor
7 | import com.jarroyo.feature.schedules.api.model.Schedule
8 | import dev.gitlive.firebase.firestore.FirebaseFirestore
9 | import dev.gitlive.firebase.firestore.FirebaseFirestoreException
10 |
11 | internal class AddScheduleInteractorImpl(
12 | private val firestore: FirebaseFirestore,
13 | ) : AddScheduleInteractor {
14 | override suspend fun invoke(schedule: Schedule): Result {
15 | try {
16 | firestore
17 | .collection("Schedules")
18 | .document(schedule.id)
19 | .set(schedule)
20 | return Ok(true)
21 | } catch (e: FirebaseFirestoreException) {
22 | return Err(e)
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/modules/feature-schedules/src/commonMain/kotlin/com/jarroyo/feature/schedules/interactor/GetScheduleInteractorImpl.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.schedules.interactor
2 |
3 | import com.github.michaelbull.result.Err
4 | import com.github.michaelbull.result.Ok
5 | import com.github.michaelbull.result.Result
6 | import com.jarroyo.feature.schedules.api.interactor.GetScheduleInteractor
7 | import com.jarroyo.feature.schedules.api.model.Schedule
8 | import dev.gitlive.firebase.firestore.FirebaseFirestore
9 | import dev.gitlive.firebase.firestore.FirebaseFirestoreException
10 |
11 | internal class GetScheduleInteractorImpl(
12 | private val firestore: FirebaseFirestore,
13 | ): GetScheduleInteractor {
14 | override suspend operator fun invoke(
15 | id: String,
16 | ): Result {
17 | try {
18 | val query = firestore.collection("Schedules").where {
19 | "id" equalTo id
20 | }
21 |
22 | val schedules = query.get().documents.map {
23 | it.data(Schedule.serializer())
24 | }
25 | return Ok(schedules.firstOrNull())
26 | } catch (e: FirebaseFirestoreException) {
27 | return Err(e)
28 | } catch (e: IllegalArgumentException) {
29 | return Err(e)
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/modules/feature-schedules/src/commonMain/kotlin/com/jarroyo/feature/schedules/interactor/GetSchedulesInteractorImpl.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.schedules.interactor
2 |
3 | import com.github.michaelbull.result.Err
4 | import com.github.michaelbull.result.Ok
5 | import com.github.michaelbull.result.Result
6 | import com.jarroyo.feature.schedules.api.interactor.GetSchedulesInteractor
7 | import com.jarroyo.feature.schedules.api.model.Schedule
8 | import dev.gitlive.firebase.firestore.FirebaseFirestore
9 | import dev.gitlive.firebase.firestore.FirebaseFirestoreException
10 |
11 | internal class GetSchedulesInteractorImpl(
12 | private val firestore: FirebaseFirestore,
13 | ): GetSchedulesInteractor {
14 | override suspend operator fun invoke(): Result?, Exception> {
15 | try {
16 | val scheduleList =
17 | firestore.collection("Schedules").get()
18 | .documents.map { document ->
19 | document.data(Schedule.serializer())
20 | }
21 | return Ok(scheduleList)
22 | } catch (e: FirebaseFirestoreException) {
23 | return Err(e)
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/modules/feature-schedules/src/commonMain/kotlin/com/jarroyo/feature/schedules/interactor/GetUserInteractorImpl.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.schedules.interactor
2 |
3 | import com.github.michaelbull.result.Err
4 | import com.github.michaelbull.result.Ok
5 | import com.github.michaelbull.result.Result
6 | import com.jarroyo.feature.schedules.api.interactor.GetUserInteractor
7 | import com.jarroyo.feature.schedules.api.model.User
8 | import dev.gitlive.firebase.firestore.FirebaseFirestore
9 | import dev.gitlive.firebase.firestore.FirebaseFirestoreException
10 |
11 | internal class GetUserInteractorImpl(
12 | private val firestore: FirebaseFirestore,
13 | ): GetUserInteractor {
14 | override suspend operator fun invoke(
15 | id: String,
16 | ): Result {
17 | try {
18 | val query = firestore.collection("Users").where {
19 | "id" equalTo id
20 | }
21 |
22 | val users = query.get().documents.map {
23 | it.data(User.serializer())
24 | }
25 | return Ok(users.firstOrNull())
26 | } catch (e: FirebaseFirestoreException) {
27 | return Err(e)
28 | } catch (e: IllegalArgumentException) {
29 | return Err(e)
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/modules/feature-schedules/src/commonMain/kotlin/com/jarroyo/feature/schedules/interactor/GetUserListInteractorImpl.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.schedules.interactor
2 |
3 | import com.github.michaelbull.result.Err
4 | import com.github.michaelbull.result.Ok
5 | import com.github.michaelbull.result.Result
6 | import com.jarroyo.feature.schedules.api.interactor.GetUserListInteractor
7 | import com.jarroyo.feature.schedules.api.model.User
8 | import dev.gitlive.firebase.firestore.FirebaseFirestore
9 | import dev.gitlive.firebase.firestore.FirebaseFirestoreException
10 |
11 | internal class GetUserListInteractorImpl(
12 | private val firestore: FirebaseFirestore,
13 | ): GetUserListInteractor {
14 | override suspend operator fun invoke(): Result?, Exception> {
15 | try {
16 | val list =
17 | firestore.collection("Users").get()
18 | .documents.map { document ->
19 | document.data(User.serializer())
20 | }
21 | return Ok(list)
22 | } catch (e: FirebaseFirestoreException) {
23 | return Err(e)
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/modules/feature-schedules/src/commonMain/kotlin/com/jarroyo/feature/schedules/interactor/RemoveScheduleInteractorImpl.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.schedules.interactor
2 |
3 | import com.github.michaelbull.result.Err
4 | import com.github.michaelbull.result.Ok
5 | import com.github.michaelbull.result.Result
6 | import com.jarroyo.feature.schedules.api.interactor.RemoveScheduleInteractor
7 | import dev.gitlive.firebase.firestore.FirebaseFirestore
8 | import dev.gitlive.firebase.firestore.FirebaseFirestoreException
9 |
10 | internal class RemoveScheduleInteractorImpl(
11 | private val firestore: FirebaseFirestore,
12 | ) : RemoveScheduleInteractor {
13 | override suspend fun invoke(id: String): Result {
14 | try {
15 | firestore
16 | .collection("Schedules")
17 | .document(id)
18 | .delete()
19 | return Ok(true)
20 | } catch (e: FirebaseFirestoreException) {
21 | return Err(e)
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/modules/feature-schedules/src/commonMain/kotlin/com/jarroyo/feature/schedules/navigationsuite/ScheduleListNavigationSuiteItem.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.schedules.navigationsuite
2 |
3 | import androidx.compose.foundation.layout.RowScope
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.ui.Modifier
6 | import androidx.compose.ui.graphics.ImageBitmap
7 | import androidx.compose.ui.graphics.painter.BitmapPainter
8 | import com.jarroyo.library.ui.shared.component.MainNavigationBarItem
9 |
10 | @Composable
11 | fun RowScope.ScheduleListNavigationBarItem(
12 | selected: Boolean,
13 | onClick: () -> Unit,
14 | label: String,
15 | modifier: Modifier = Modifier,
16 | ) {
17 | val painter = BitmapPainter(ImageBitmap(20, 20))
18 | MainNavigationBarItem(
19 | selected = selected,
20 | onClick = onClick,
21 | icon = painter, // painterResource(resource = painter),
22 | modifier = modifier,
23 | label = label,
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/modules/feature-schedules/src/commonMain/kotlin/com/jarroyo/feature/schedules/ui/list/ScheduleListContract.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.schedules.ui.list
2 |
3 | import androidx.compose.runtime.Immutable
4 | import com.jarroyo.feature.schedules.api.destination.ScheduleDetailDestination
5 | import com.jarroyo.feature.schedules.api.model.Schedule
6 | import com.jarroyo.library.ui.shared.ViewEffect
7 | import com.jarroyo.library.ui.shared.ViewEvent
8 | import com.jarroyo.library.ui.shared.ViewState
9 |
10 | @Immutable
11 | object ScheduleListContract {
12 | data class State(
13 | val loading: Boolean = false,
14 | val scheduleList: List? = null,
15 | ) : ViewState
16 |
17 | sealed class Event : ViewEvent {
18 | data class OnItemClicked(val id: String) : Event()
19 | data class OnScheduleUpdated(val operationType: ScheduleDetailDestination.Result.OperationType): Event()
20 | data object OnAddScheduleButtonClicked: Event()
21 | data object OnSwipeToRefresh: Event()
22 | }
23 |
24 | sealed class Effect : ViewEffect {
25 | data class ShowSnackbar(val message: String) : Effect()
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/modules/feature-schedules/src/commonMain/kotlin/com/jarroyo/feature/schedules/ui/list/ScheduleListItem.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.schedules.ui.list
2 |
3 | import androidx.compose.foundation.layout.Arrangement
4 | import androidx.compose.foundation.layout.Column
5 | import androidx.compose.foundation.layout.fillMaxWidth
6 | import androidx.compose.foundation.layout.padding
7 | import androidx.compose.material.Card
8 | import androidx.compose.material.ExperimentalMaterialApi
9 | import androidx.compose.material.Text
10 | import androidx.compose.runtime.Composable
11 | import androidx.compose.ui.Modifier
12 | import com.jarroyo.feature.schedules.api.model.Schedule
13 | import com.jarroyo.feature.schedules.ui.list.ScheduleListContract.Event
14 | import com.jarroyo.library.ui.shared.component.placeholder
15 | import com.jarroyo.library.ui.shared.theme.Spacing
16 |
17 | @OptIn(ExperimentalMaterialApi::class)
18 | @Composable
19 | fun ScheduleListItem(
20 | item: Schedule,
21 | sendEvent: (event: Event) -> Unit,
22 | modifier: Modifier = Modifier,
23 | placeholder: Boolean = false,
24 | ) {
25 | Card(
26 | onClick = { sendEvent(Event.OnItemClicked(checkNotNull(item.id))) },
27 | modifier = Modifier.then(modifier),
28 | enabled = !placeholder,
29 | elevation = Spacing.quarter,
30 | ) {
31 | Column(
32 | modifier = Modifier
33 | .fillMaxWidth()
34 | .padding(Spacing.x02),
35 | verticalArrangement = Arrangement.spacedBy(Spacing.quarter),
36 | ) {
37 | Text(
38 | "- id: ${item.id}",
39 | modifier = Modifier.placeholder(placeholder),
40 | )
41 | Text(
42 | "- Time: ${item.time}",
43 | modifier = Modifier.placeholder(placeholder),
44 | )
45 | Text(
46 | "- Slots: ${item.slots}",
47 | modifier = Modifier.placeholder(placeholder),
48 | )
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/modules/feature-schedules/src/commonMain/kotlin/com/jarroyo/feature/schedules/ui/list/ScheduleListViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.schedules.ui.list
2 |
3 | import androidx.lifecycle.viewModelScope
4 | import com.jarroyo.feature.schedules.api.destination.ScheduleDetailDestination
5 | import com.jarroyo.feature.schedules.api.destination.ScheduleDetailDestination.Result
6 | import com.jarroyo.feature.schedules.api.interactor.GetSchedulesInteractor
7 | import com.jarroyo.feature.schedules.ui.list.ScheduleListContract.Effect
8 | import com.jarroyo.feature.schedules.ui.list.ScheduleListContract.Event
9 | import com.jarroyo.feature.schedules.ui.list.ScheduleListContract.State
10 | import com.jarroyo.library.navigation.api.navigator.AppNavigator
11 | import com.jarroyo.library.ui.shared.BaseViewModel
12 | import kotlinx.coroutines.launch
13 | import org.koin.android.annotation.KoinViewModel
14 |
15 | @KoinViewModel
16 | class ScheduleListViewModel(
17 | private val appNavigator: AppNavigator,
18 | private val getSchedulesInteractor: GetSchedulesInteractor,
19 | ) : BaseViewModel() {
20 | init {
21 | refreshData()
22 | }
23 |
24 | override fun provideInitialState() = State()
25 |
26 | override fun handleEvent(event: Event) {
27 | when (event) {
28 | is Event.OnAddScheduleButtonClicked -> appNavigator.navigate(ScheduleDetailDestination.get(""))
29 | is Event.OnItemClicked -> appNavigator.navigate(ScheduleDetailDestination.get(event.id))
30 | is Event.OnScheduleUpdated -> handleOnScheduleUpdated(event.operationType)
31 | is Event.OnSwipeToRefresh -> handleOnSwipeToRefresh()
32 | }
33 | }
34 |
35 | private fun handleOnScheduleUpdated(operationType: Result.OperationType) {
36 | refreshData()
37 | val message = when(operationType) {
38 | Result.OperationType.CREATE -> "Created"
39 | Result.OperationType.REMOVE -> "Removed"
40 | Result.OperationType.UPDATE -> "Updated"
41 | }
42 | sendEffect { Effect.ShowSnackbar(message) }
43 | }
44 |
45 | private fun handleOnSwipeToRefresh() {
46 | refreshData()
47 | sendEffect { Effect.ShowSnackbar("Refreshing data...") }
48 | }
49 |
50 | private fun refreshData() {
51 | viewModelScope.launch {
52 | updateState { copy(loading = true) }
53 | refreshSchedules()
54 | updateState { copy(loading = false) }
55 | }
56 | }
57 | private suspend fun refreshSchedules() {
58 | val result = getSchedulesInteractor()
59 | if (result.isOk) {
60 | updateState { copy(scheduleList = result.value) }
61 | } else {
62 | sendEffect { Effect.ShowSnackbar(result.error.message.orEmpty()) }
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/modules/feature-schedules/src/commonMain/kotlin/com/jarroyo/feature/schedules/ui/scheduledetail/ScheduleDetailContract.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.schedules.ui.scheduledetail
2 |
3 | import androidx.compose.runtime.Immutable
4 | import com.jarroyo.feature.schedules.api.destination.ScheduleDetailDestination
5 | import com.jarroyo.feature.schedules.api.model.User
6 | import com.jarroyo.library.ui.shared.ViewEffect
7 | import com.jarroyo.library.ui.shared.ViewEvent
8 | import com.jarroyo.library.ui.shared.ViewState
9 |
10 | @Immutable
11 | object ScheduleDetailContract {
12 | data class State(
13 | val editAllowed: Boolean = false,
14 | val id: String? = null,
15 | val loading: Boolean = false,
16 | val saveButtonEnabled: Boolean = false,
17 | val slots: String? = null,
18 | val time: String? = null,
19 | val userList: List? = null,
20 | ) : ViewState
21 |
22 | sealed class Event : ViewEvent {
23 | data class OnIdValueChanged(val value: String): Event()
24 | data class OnTimeValueChanged(val value: String): Event()
25 | data class OnSlotsValueChanged(val value: String): Event()
26 | data class OnUserSelectedUpdate(val userList: List): Event()
27 | data object OnAddScheduleButtonClicked: Event()
28 | data object OnRemoveButtonClicked: Event()
29 | data object OnUserItemClicked: Event()
30 | data object OnUpButtonClicked: Event()
31 | }
32 |
33 | sealed class Effect : ViewEffect {
34 | data class SetResultAndNavigate(
35 | val result: ScheduleDetailDestination.Result,
36 | val navigate: () -> Unit,
37 | ) : Effect()
38 | data class ShowSnackbar(val message: String) : Effect()
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/modules/feature-schedules/src/commonMain/kotlin/com/jarroyo/feature/schedules/ui/userselector/UserSelectorContract.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.schedules.ui.userselector
2 |
3 | import androidx.compose.runtime.Immutable
4 | import com.jarroyo.feature.schedules.api.destination.UserSelectorDestination
5 | import com.jarroyo.feature.schedules.api.model.User
6 | import com.jarroyo.library.ui.shared.ViewEffect
7 | import com.jarroyo.library.ui.shared.ViewEvent
8 | import com.jarroyo.library.ui.shared.ViewState
9 |
10 | @Immutable
11 | object UserSelectorContract {
12 | data class State(
13 | val loading: Boolean = false,
14 | val multipleSelection: Boolean = false,
15 | val saveButtonEnabled: Boolean = false,
16 | val users: List? = null,
17 | val usersSelectedMap: Map = mapOf(),
18 | ) : ViewState
19 |
20 | sealed class Event : ViewEvent {
21 | data class OnItemClicked(val user: User, val selected: Boolean) : Event()
22 | data object OnSaveButtonClicked : Event()
23 | data object OnUpButtonClicked : Event()
24 | }
25 |
26 | sealed class Effect : ViewEffect {
27 | data class SetResultAndNavigate(val result: UserSelectorDestination.Result, val navigate: () -> Unit) : Effect()
28 | data class ShowErrorSnackbar(
29 | val message: String,
30 | ) : Effect()
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/modules/feature-schedules/src/commonMain/kotlin/com/jarroyo/feature/schedules/ui/userselector/UserSelectorItem.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.feature.schedules.ui.userselector
2 |
3 | import androidx.compose.foundation.layout.Arrangement
4 | import androidx.compose.foundation.layout.Row
5 | import androidx.compose.material.Text
6 | import androidx.compose.material3.Checkbox
7 | import androidx.compose.material3.RadioButton
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.ui.Alignment
10 | import androidx.compose.ui.Modifier
11 | import androidx.compose.ui.text.style.TextOverflow
12 | import com.jarroyo.feature.schedules.api.ext.getFullName
13 | import com.jarroyo.feature.schedules.api.model.User
14 | import com.jarroyo.library.ui.shared.component.SettingsMenuRow
15 | import com.jarroyo.library.ui.shared.theme.Spacing
16 |
17 | @Composable
18 | fun UserSelectorItem(
19 | user: User,
20 | state: UserSelectorContract.State,
21 | modifier: Modifier = Modifier,
22 | onClick: (selected: Boolean) -> Unit = {},
23 | placeholder: Boolean = false,
24 | ) {
25 | val isCurrentlySelected = state.usersSelectedMap[user.id] != null
26 | SettingsMenuRow(
27 | title = {
28 | Row(
29 | horizontalArrangement = Arrangement.spacedBy(Spacing.x02),
30 | verticalAlignment = Alignment.CenterVertically,
31 | ) {
32 | Text(
33 | text = user.getFullName(),
34 | overflow = TextOverflow.Ellipsis,
35 | maxLines = 2,
36 | // placeholder = placeholder,
37 | )
38 | }
39 | },
40 | modifier = modifier,
41 | icon = {
42 | if (state.multipleSelection) {
43 | Checkbox(
44 | checked = isCurrentlySelected,
45 | onCheckedChange = { onClick(it) },
46 | )
47 | } else {
48 | RadioButton(
49 | selected = isCurrentlySelected,
50 | onClick = { onClick(!isCurrentlySelected) },
51 | )
52 | }
53 | },
54 | enabled = !placeholder,
55 | onClick = { onClick(!isCurrentlySelected) },
56 | )
57 | }
58 |
--------------------------------------------------------------------------------
/modules/library-feature/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/modules/library-feature/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("composeapp.multiplatform-library-conventions")
3 | }
4 | android {
5 | namespace = "com.jarroyo.library.feature"
6 | resourcePrefix = "feature_"
7 | defaultConfig {
8 | consumerProguardFiles("$projectDir/proguard-feature-consumer-rules.pro")
9 | }
10 | }
11 |
12 | kotlin {
13 | sourceSets {
14 | androidMain.dependencies {
15 | }
16 | commonMain.dependencies {
17 | api(projects.modules.libraryNavigationApi)
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/modules/library-feature/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/modules/library-feature/consumer-rules.pro
--------------------------------------------------------------------------------
/modules/library-feature/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.
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
--------------------------------------------------------------------------------
/modules/library-feature/src/commonMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/modules/library-feature/src/commonMain/kotlin/com/jarroyo/library/feature/Feature.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.library.feature
2 |
3 | import androidx.compose.runtime.Composable
4 | import com.jarroyo.library.navigation.api.destination.NavigationDestination
5 |
6 | abstract class Feature {
7 | abstract val id: String
8 | open val bottomSheetDestinations: Map Unit> = emptyMap()
9 | open val composableDestinations: Map Unit> = emptyMap()
10 | open val debugComposable: @Composable (() -> Unit)? = null
11 | open val dialogDestinations: Map Unit> = emptyMap()
12 | open val featureLifecycle: FeatureLifecycle = FeatureLifecycle()
13 | open val navigationSuiteEntry: NavigationSuiteEntry? = null
14 |
15 | data class NavigationSuiteEntry(
16 | val priority: Int,
17 | val route: String,
18 | val label: String,
19 | val enabled: Boolean = true,
20 | val hideBottomNav: Boolean = false,
21 | )
22 | }
23 |
--------------------------------------------------------------------------------
/modules/library-feature/src/commonMain/kotlin/com/jarroyo/library/feature/FeatureLifecycle.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.library.feature
2 |
3 | data class FeatureLifecycle(
4 | val onLogin: suspend () -> Unit = {},
5 | val onLogout: suspend () -> Unit = {},
6 | )
7 |
--------------------------------------------------------------------------------
/modules/library-feature/src/commonMain/kotlin/com/jarroyo/library/feature/FeatureManager.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.library.feature
2 |
3 | class FeatureManager {
4 | private val _featureMap = mutableMapOf()
5 | private val features: List get() = _featureMap.values.toList()
6 | val featureMap : Map = _featureMap
7 |
8 | inline fun getFeature(): T {
9 | val key = T::class.simpleName ?: error("Feature class must have a simple name")
10 | return featureMap[key] as? T ?: error("No feature registered with key $key")
11 | }
12 |
13 | fun register(features: List) {
14 | features.forEach { feature ->
15 | val key = feature::class.simpleName ?: error("Feature class must have a simple name")
16 | _featureMap.put(key, feature)?.let { error("Duplicate features ${feature.id} - ${it.id}") }
17 | }
18 | }
19 |
20 | suspend fun onUserLogin() {
21 | features.forEach { it.featureLifecycle.onLogin() }
22 | }
23 |
24 | suspend fun onUserLogout() {
25 | features.reversed().forEach { it.featureLifecycle.onLogout() }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/modules/library-navigation-api/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/modules/library-navigation-api/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("composeapp.multiplatform-library-conventions")
3 | }
4 |
5 | android {
6 | namespace = "com.jarroyo.library.navigation.api"
7 | resourcePrefix = "navigation_api_"
8 | defaultConfig {
9 | consumerProguardFiles("$projectDir/proguard-navigation-api-consumer-rules.pro")
10 | }
11 | }
12 |
13 | kotlin {
14 | sourceSets {
15 | commonMain.dependencies {
16 | implementation(libs.coroutines.core)
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/modules/library-navigation-api/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
--------------------------------------------------------------------------------
/modules/library-navigation-api/src/commonMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/modules/library-navigation-api/src/commonMain/kotlin/com/jarroyo/library/navigation/api/destination/NavigationDestination.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.library.navigation.api.destination
2 |
3 | import androidx.navigation.NamedNavArgument
4 | import kotlin.random.Random
5 |
6 | abstract class NavigationDestination {
7 | abstract val route: String
8 |
9 | open val arguments: List
10 | get() = emptyList()
11 |
12 | abstract class Result {
13 | abstract val id: Long
14 | var consumed: Boolean = false
15 |
16 | companion object {
17 | val uniqueId
18 | get() = Random.nextLong()
19 | }
20 | }
21 |
22 | companion object {
23 | @Suppress("LateinitUsage") lateinit var DEEP_LINK_SCHEME: String
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/modules/library-navigation-api/src/commonMain/kotlin/com/jarroyo/library/navigation/api/navigator/AppNavigator.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.library.navigation.api.navigator
2 |
3 | import androidx.navigation.NavOptionsBuilder
4 | import kotlinx.coroutines.flow.Flow
5 |
6 | interface AppNavigator {
7 | val destinations: Flow
8 | val homeDestination: String
9 |
10 | /**
11 | * Attempts to navigate up in the navigation hierarchy. Suitable for when the user presses the
12 | * "Up" button marked with a left (or start)-facing arrow in the upper left (or starting)
13 | * corner of the app UI.
14 | *
15 | * The intended behavior of Up differs from Back when the user did not reach the current
16 | * destination from the application's own task. e.g. if the user is viewing a document or link
17 | * in the current app in an activity hosted on another app's task where the user clicked the
18 | * link. In this case the current activity (determined by the context used to create this
19 | * NavController) will be finished and the user will be taken to an appropriate destination
20 | * in this app on its own task.
21 | *
22 | * @return true if the navigation request was successfully delivered to the View, false otherwise
23 | */
24 | fun navigateUp(): Boolean
25 |
26 | /**
27 | * Attempts to pop the navigation controller's back stack. Analogous to when the user presses the system Back button.
28 | *
29 | * @return true if the navigation request was successfully delivered to the View, false otherwise
30 | */
31 | fun navigateBack(): Boolean
32 | fun navigateHome(): Boolean
33 | fun navigateToLogin(builder: NavOptionsBuilder.() -> Unit): Boolean
34 | fun navigate(route: String, builder: NavOptionsBuilder.() -> Unit = { launchSingleTop = true }): Boolean
35 | }
36 |
--------------------------------------------------------------------------------
/modules/library-navigation-api/src/commonMain/kotlin/com/jarroyo/library/navigation/api/navigator/NavigatorEvent.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.library.navigation.api.navigator
2 |
3 | import androidx.navigation.NavOptionsBuilder
4 |
5 | sealed class NavigatorEvent {
6 | data class Directions(val destination: String, val builder: NavOptionsBuilder.() -> Unit) : NavigatorEvent()
7 | data object NavigateBack : NavigatorEvent()
8 | data object NavigateUp : NavigatorEvent()
9 | }
10 |
--------------------------------------------------------------------------------
/modules/library-navigation/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/modules/library-navigation/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("composeapp.multiplatform-library-conventions")
3 | }
4 |
5 | android {
6 | namespace = "com.jarroyo.library.navigation"
7 | resourcePrefix = "navigation_"
8 | defaultConfig {
9 | consumerProguardFiles("$projectDir/proguard-navigation-consumer-rules.pro")
10 | }
11 | }
12 |
13 | kotlin {
14 | sourceSets {
15 | commonMain.dependencies {
16 | api(projects.modules.libraryNavigationApi)
17 | implementation(libs.koin.core)
18 | implementation(projects.modules.featureLaunchesApi)
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/modules/library-navigation/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
--------------------------------------------------------------------------------
/modules/library-navigation/src/commonMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/modules/library-navigation/src/commonMain/kotlin/com/jarroyo/library/navigation/di/KoinNavigationModule.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.library.navigation.di
2 |
3 | import com.jarroyo.library.navigation.api.navigator.AppNavigator
4 | import com.jarroyo.library.navigation.navigator.AppNavigatorImpl
5 | import org.koin.core.component.get
6 | import org.koin.dsl.module
7 |
8 | val navigationModule = module {
9 | single { AppNavigatorImpl() }
10 | }
11 |
--------------------------------------------------------------------------------
/modules/library-network-api/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/modules/library-network-api/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import io.gitlab.arturbosch.detekt.Detekt
2 |
3 | plugins {
4 | id("composeapp.multiplatform-library-conventions")
5 | alias(libs.plugins.apollo)
6 | alias(libs.plugins.kotlinx.serialization)
7 | }
8 |
9 | android {
10 | namespace = "com.jarroyo.composeapp.library.network.api"
11 | resourcePrefix = "network_api_"
12 | defaultConfig {
13 | consumerProguardFiles("$projectDir/proguard-network-api-consumer-rules.pro")
14 | }
15 | }
16 |
17 | apollo {
18 | service("spacex") {
19 | packageName.set("com.jarroyo.composeapp.library.network.api.graphql")
20 | generateApolloMetadata.set(true)
21 | decapitalizeFields.set(true)
22 | generateDataBuilders.set(true)
23 | mapScalar(
24 | "Date",
25 | "kotlinx.datetime.Instant",
26 | "com.apollographql.apollo.adapter.KotlinxInstantAdapter"
27 | )
28 | introspection {
29 | endpointUrl.set("https://spacex-production.up.railway.app/")
30 | schemaFile.set(file("src/commonMain/graphql/schema.graphqls"))
31 | }
32 | }
33 | }
34 |
35 | kotlin {
36 | sourceSets {
37 | commonMain.dependencies {
38 | api(libs.apollo)
39 | api(libs.apollo.adapters)
40 | api(libs.apollo.cache)
41 | implementation(libs.apollo.cache.sqlite)
42 | implementation(libs.jetbrains.kotlinx.serialization)
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/modules/library-network-api/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/modules/library-network-api/consumer-rules.pro
--------------------------------------------------------------------------------
/modules/library-network-api/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
--------------------------------------------------------------------------------
/modules/library-network-api/src/commonMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/modules/library-network-api/src/commonMain/graphql/LaunchFragment.graphql:
--------------------------------------------------------------------------------
1 | fragment launchFragment on Launch {
2 | id
3 | details
4 | launch_date_local
5 | mission_name
6 | rocket {
7 | rocket {
8 | ...rocketFragment
9 | }
10 | }
11 | links {
12 | article_link
13 | flickr_images
14 | mission_patch_small
15 | mission_patch
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/modules/library-network-api/src/commonMain/graphql/QueryLaunchDetail.graphql:
--------------------------------------------------------------------------------
1 | query LaunchDetail($id: ID!) {
2 | launch(id: $id) {
3 | ...launchFragment
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/modules/library-network-api/src/commonMain/graphql/QueryLaunches.graphql:
--------------------------------------------------------------------------------
1 | query LaunchesQuery {
2 | launches {
3 | ...launchFragment
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/modules/library-network-api/src/commonMain/graphql/RocketFragment.graphql:
--------------------------------------------------------------------------------
1 | fragment rocketFragment on Rocket {
2 | id
3 | name
4 | company
5 | active
6 | wikipedia
7 | }
8 |
--------------------------------------------------------------------------------
/modules/library-network-api/src/commonMain/graphql/ShipFragment.graphql:
--------------------------------------------------------------------------------
1 | fragment shipFragment on Ship {
2 | id
3 | name
4 | image
5 | active
6 | home_port
7 | }
8 |
--------------------------------------------------------------------------------
/modules/library-network-api/src/commonMain/kotlin/com/jarroyo/library/network/api/ext/ApolloCallExt.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.library.network.api.ext
2 |
3 | import com.apollographql.apollo.ApolloCall
4 | import com.apollographql.apollo.api.ApolloResponse
5 | import com.apollographql.apollo.api.Operation
6 | import com.apollographql.apollo.exception.CacheMissException
7 | import com.github.michaelbull.result.Err
8 | import com.github.michaelbull.result.Result
9 | import kotlinx.coroutines.flow.filterNot
10 | import kotlinx.coroutines.flow.toList
11 |
12 | suspend fun ApolloCall.executeAndHandleErrors(
13 | successBlock: suspend (ApolloResponse) -> Result,
14 | ): Result {
15 | val responses = toFlow()
16 | .filterNot { it.exception is CacheMissException }
17 | .toList()
18 |
19 | val lastResponse = responses.last()
20 |
21 | if (isSuccessResponse(lastResponse)) {
22 | return successBlock(lastResponse)
23 | }
24 |
25 | return Err(Exception(lastResponse.exception))
26 | }
27 |
28 | private fun isSuccessResponse(response: ApolloResponse): Boolean =
29 | !response.hasErrors() && response.exception == null ||
30 | /* response.executionContext[HttpInfo]?.statusCode == HttpURLConnection.HTTP_PARTIAL &&*/ response.data != null
31 |
--------------------------------------------------------------------------------
/modules/library-network/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/modules/library-network/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("composeapp.multiplatform-library-conventions")
3 | alias(libs.plugins.kotlinx.serialization)
4 | }
5 |
6 | android {
7 | namespace = "com.jarroyo.library.network"
8 | resourcePrefix = "network_"
9 | defaultConfig {
10 | consumerProguardFiles("$projectDir/proguard-network-consumer-rules.pro")
11 | }
12 | sourceSets {
13 | val main by getting
14 | main.manifest.srcFile("src/androidMain/AndroidManifest.xml")
15 | }
16 | }
17 |
18 | kotlin {
19 | sourceSets {
20 | commonMain.dependencies {
21 | api(projects.modules.libraryNetworkApi)
22 | implementation(libs.koin.core)
23 | implementation(libs.ktor.client.content.negotiation)
24 | implementation(libs.ktor.client.core)
25 | implementation(libs.ktor.client.cio)
26 | implementation(libs.ktor.serialization.kotlinx.json)
27 | }
28 |
29 | iosMain.dependencies {
30 | implementation(libs.ktor.client.darwin)
31 | }
32 |
33 | desktopMain.dependencies {
34 | implementation(libs.ktor.client.java)
35 | }
36 |
37 | }
38 | }
--------------------------------------------------------------------------------
/modules/library-network/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
--------------------------------------------------------------------------------
/modules/library-network/src/androidMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/modules/library-network/src/commonMain/kotlin/com/jarroyo/library/network/di/ElectricityApi.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.library.network.di
2 |
3 | import io.ktor.client.HttpClient
4 | import io.ktor.client.call.body
5 | import io.ktor.client.request.get
6 | import kotlinx.datetime.LocalDate
7 | import kotlinx.serialization.Serializable
8 | import org.koin.core.component.KoinComponent
9 |
10 | class ElectricityApi(
11 | private val client: HttpClient,
12 | private var baseUrl: String = "https://apidatos.ree.es/es/datos/mercados/precios-mercados-tiempo-real",
13 | ) : KoinComponent {
14 | suspend fun fetchElectricityData(
15 | startDate: LocalDate,
16 | endDate: LocalDate,
17 | ): ElectricityData {
18 | val url = "$baseUrl?start_date=${startDate}T00:00&end_date=${endDate}T23:59&time_trunc=hour&geo_trunc=electric_system&geo_limit=peninsular&geo_ids=8741"
19 | return client.get(url).body()
20 | }
21 | }
22 |
23 | @Serializable
24 | data class ElectricityData(
25 | var included: List,
26 | )
27 |
28 | @Serializable
29 | data class Included(
30 | var type: String,
31 | var id: String,
32 | var groupId: String?,
33 | var attributes: Attributes,
34 | )
35 |
36 | @Serializable
37 | data class Attributes(
38 | var title: String,
39 | var description: String?,
40 | var color: String,
41 | var type: String?,
42 | var magnitude: String,
43 | var composite: Boolean,
44 | var values: List,
45 | )
46 |
47 | @Serializable
48 | data class Values(
49 | var value: Double,
50 | var percentage: Int,
51 | var datetime: String,
52 | )
53 |
--------------------------------------------------------------------------------
/modules/library-network/src/commonMain/kotlin/com/jarroyo/library/network/di/KoinNetworkModule.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.library.network.di
2 |
3 | import com.apollographql.apollo.ApolloClient
4 | import com.apollographql.apollo.cache.normalized.api.MemoryCacheFactory
5 | import com.apollographql.apollo.cache.normalized.api.NormalizedCacheFactory
6 | import com.apollographql.apollo.cache.normalized.normalizedCache
7 | import dev.gitlive.firebase.Firebase
8 | import dev.gitlive.firebase.firestore.FirebaseFirestore
9 | import dev.gitlive.firebase.firestore.firestore
10 | import org.koin.dsl.module
11 | import io.ktor.client.HttpClient
12 | import io.ktor.client.engine.HttpClientEngine
13 | import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
14 | import io.ktor.serialization.kotlinx.json.json
15 | import kotlinx.serialization.json.Json
16 | import org.koin.core.module.dsl.singleOf
17 |
18 | val networkModule = module {
19 | single {
20 | MemoryCacheFactory(
21 | maxSizeBytes = MEMORY_CACHE_SIZE_IN_BYTES,
22 | expireAfterMillis = CACHE_EXPIRES_IN_MILLIS,
23 | )
24 | }
25 | single {
26 | val chainedCacheFactory: NormalizedCacheFactory = get()
27 | ApolloClient.Builder()
28 | .serverUrl("https://spacex-production.up.railway.app/")
29 | .apply { normalizedCache(chainedCacheFactory) }
30 | .build()
31 | }
32 |
33 | single {
34 | Firebase.firestore
35 | }
36 | singleOf(::createJson)
37 | single { createHttpClient(get(), get()) }
38 |
39 | factory { ElectricityApi(get()) }
40 | }
41 |
42 | fun createJson() = Json { isLenient = true
43 | ignoreUnknownKeys = true }
44 |
45 | fun createHttpClient(httpClientEngine: HttpClientEngine, json: Json) =
46 | HttpClient(httpClientEngine) {
47 | install(ContentNegotiation) {
48 | json(json)
49 | }
50 | }
51 |
52 | private const val MEMORY_CACHE_SIZE_IN_BYTES = 10 * 1024 * 1024
53 | private const val CACHE_EXPIRES_IN_MILLIS = 60*60*1000L
54 |
--------------------------------------------------------------------------------
/modules/library-test/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget
2 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
3 |
4 | plugins {
5 | kotlin("multiplatform")
6 | id("com.android.library")
7 | id("composeapp.config-conventions")
8 | }
9 |
10 | android {
11 | namespace = "com.jarroyo.library.test"
12 | resourcePrefix = "test_"
13 | compileSdk = libs.versions.androidCompileSdk.get().toInt()
14 | defaultConfig {
15 | minSdk = config.android.minSdk.get()
16 | consumerProguardFiles("$projectDir/proguard-test-consumer-rules.pro")
17 | }
18 | packaging {
19 | resources {
20 | // Use this block to exclude conflicting files that breaks your APK assemble task
21 | excludes.add("META-INF/LICENSE-notice.md")
22 | excludes.add("META-INF/LICENSE.md")
23 | excludes.add("META-INF/NOTICE.md")
24 | }
25 | }
26 | compileOptions {
27 | sourceCompatibility = config.android.javaVersion.get()
28 | targetCompatibility = config.android.javaVersion.get()
29 | }
30 | }
31 |
32 | kotlin {
33 | androidTarget()
34 | // jvm("desktop")
35 | sourceSets {
36 | androidMain.dependencies {
37 | api(libs.androidx.test.core)
38 | api(libs.androidx.test.runner)
39 | }
40 | commonMain.dependencies {
41 | api(libs.apollo.testing.support)
42 | api(libs.coroutines.test)
43 | api(libs.junit)
44 | api(libs.mockk)
45 | }
46 |
47 | commonTest.dependencies {
48 | api(libs.apollo.testing.support)
49 | api(libs.coroutines.test)
50 | api(libs.jetbrains.kotlin.test)
51 | api(libs.junit)
52 | api(libs.mockk)
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/modules/library-test/proguard-test-consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/modules/library-test/proguard-test-consumer-rules.pro
--------------------------------------------------------------------------------
/modules/library-ui-shared/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/modules/library-ui-shared/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("composeapp.multiplatform-library-conventions")
3 | id("org.jetbrains.compose")
4 | id("org.jetbrains.kotlin.plugin.compose")
5 | }
6 |
7 | android {
8 | namespace = "com.jarroyo.library.ui.shared"
9 | }
10 |
11 | kotlin {
12 | sourceSets {
13 | sourceSets {
14 | commonMain {
15 | resources.srcDirs("src/commonMain/resources")
16 | }
17 | }
18 |
19 | commonMain.dependencies {
20 | api(compose.materialIconsExtended)
21 | implementation(libs.jetbrains.kotlinx.serialization)
22 | implementation(projects.modules.libraryNavigationApi)
23 |
24 | implementation(compose.components.resources)
25 | implementation(compose.foundation)
26 | implementation(compose.material)
27 | implementation(libs.jetbrains.compose.material3)
28 | implementation(libs.jetbrains.compose.ui)
29 | implementation(libs.jetbrains.compose.ui.graphics)
30 | implementation(libs.jetbrains.compose.ui.resources)
31 | implementation(libs.compose.placeholder)
32 | implementation(libs.coroutines.core)
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/modules/library-ui-shared/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
--------------------------------------------------------------------------------
/modules/library-ui-shared/src/androidMain/kotlin/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/modules/library-ui-shared/src/commonMain/kotlin/com/jarroyo/library/ui/shared/BaseViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.library.ui.shared
2 |
3 | import androidx.compose.runtime.MutableState
4 | import androidx.compose.runtime.State
5 | import androidx.compose.runtime.mutableStateOf
6 | import androidx.lifecycle.ViewModel
7 | import androidx.lifecycle.viewModelScope
8 | import kotlinx.coroutines.channels.Channel
9 | import kotlinx.coroutines.flow.MutableSharedFlow
10 | import kotlinx.coroutines.flow.launchIn
11 | import kotlinx.coroutines.flow.onEach
12 | import kotlinx.coroutines.flow.receiveAsFlow
13 | import kotlinx.coroutines.launch
14 |
15 | abstract class BaseViewModel: ViewModel() {
16 | // State (current state of views)
17 | // Everything is lazy in order to be able to use SavedStateHandle as initial value
18 | private val initialState: UiState by lazy { provideInitialState() }
19 | private val _viewState: MutableState by lazy { mutableStateOf(initialState) }
20 | val viewState: State by lazy { _viewState }
21 |
22 | // Event (user actions)
23 | private val _event: MutableSharedFlow = MutableSharedFlow()
24 |
25 | // Effect (side effects like error messages which we want to show only once)
26 | private val _effect: Channel = Channel()
27 | val effect = _effect.receiveAsFlow()
28 |
29 | init {
30 | _event.onEach {
31 | handleEvent(it)
32 | }.launchIn(viewModelScope)
33 | }
34 |
35 | abstract fun provideInitialState(): UiState
36 |
37 | open fun onLoadingChanged(loading: Boolean) {}
38 |
39 | protected fun updateState(reducer: UiState.() -> UiState) {
40 | val newState = viewState.value.reducer()
41 | _viewState.value = newState
42 | }
43 |
44 | fun onUiEvent(event: UiEvent) {
45 | viewModelScope.launch { _event.emit(event) }
46 | }
47 |
48 | abstract fun handleEvent(event: UiEvent)
49 |
50 | protected fun sendEffect(effectBuilder: () -> UiEffect) {
51 | viewModelScope.launch { _effect.send(effectBuilder()) }
52 | }
53 |
54 | protected suspend fun load(block: suspend () -> Unit) {
55 | block()
56 | }
57 | }
58 |
59 | interface ViewState
60 |
61 | interface ViewEvent
62 |
63 | interface ViewEffect
64 |
--------------------------------------------------------------------------------
/modules/library-ui-shared/src/commonMain/kotlin/com/jarroyo/library/ui/shared/component/BottomButtonBar.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.library.ui.shared.component
2 |
3 | import androidx.compose.foundation.layout.Row
4 | import androidx.compose.foundation.layout.RowScope
5 | import androidx.compose.foundation.layout.fillMaxWidth
6 | import androidx.compose.foundation.layout.padding
7 | import androidx.compose.material3.Surface
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.ui.Alignment
10 | import androidx.compose.ui.Modifier
11 | import androidx.compose.ui.unit.Dp
12 | import androidx.compose.ui.unit.dp
13 | import com.jarroyo.library.ui.shared.theme.Spacing
14 |
15 | @Composable
16 | fun BottomButtonBar(
17 | modifier: Modifier = Modifier,
18 | tonalElevation: Dp = 8.dp,
19 | shadowElevation: Dp = 0.dp,
20 | content: @Composable RowScope.() -> Unit,
21 | ) {
22 | Surface(
23 | modifier = modifier,
24 | tonalElevation = tonalElevation,
25 | shadowElevation = shadowElevation,
26 | ) {
27 | Row(
28 | modifier = Modifier
29 | .fillMaxWidth()
30 | .padding(horizontal = Spacing.x02, vertical = Spacing.x01),
31 | verticalAlignment = Alignment.CenterVertically,
32 | content = content,
33 | )
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/modules/library-ui-shared/src/commonMain/kotlin/com/jarroyo/library/ui/shared/component/NavHostController.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.library.ui.shared.component
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.LaunchedEffect
5 | import androidx.compose.runtime.ProvidableCompositionLocal
6 | import androidx.compose.runtime.compositionLocalOf
7 | import androidx.navigation.NavHostController
8 | import com.jarroyo.library.navigation.api.destination.NavigationDestination
9 | import kotlinx.serialization.json.Json
10 |
11 | val LocalNavHostController: ProvidableCompositionLocal =
12 | compositionLocalOf { error("No NavHostController provided") }
13 |
14 | inline fun NavHostController.setResult(key: String, value: T): Boolean {
15 | val json = Json.encodeToString(value)
16 | return previousBackStackEntry?.run { savedStateHandle[key] = json } != null
17 | }
18 |
19 | @Composable
20 | inline fun NavHostController.observeResult(
21 | key: String,
22 | crossinline onResult: (T) -> Unit,
23 | ): Boolean =
24 | currentBackStackEntry?.run {
25 | LaunchedEffect(Unit) {
26 | savedStateHandle.getStateFlow(key, null).collect { result ->
27 | result?.let {
28 | val response = Json.decodeFromString(result)
29 | savedStateHandle[key] = null
30 | onResult(response)
31 | }
32 | }
33 | }
34 | } != null
35 |
--------------------------------------------------------------------------------
/modules/library-ui-shared/src/commonMain/kotlin/com/jarroyo/library/ui/shared/component/NavigationBarItemWithBadge.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.library.ui.shared.component
2 |
3 | import androidx.compose.foundation.layout.RowScope
4 | import androidx.compose.material3.NavigationBarItem
5 | import androidx.compose.material3.NavigationBarItemColors
6 | import androidx.compose.material3.NavigationBarItemDefaults
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.material.Text
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.graphics.painter.Painter
11 | import androidx.compose.ui.text.style.TextOverflow
12 |
13 | @Composable
14 | fun RowScope.NavigationBarItemWithBadge(
15 | selected: Boolean,
16 | onClick: () -> Unit,
17 | icon: Painter,
18 | modifier: Modifier = Modifier,
19 | label: String? = null,
20 | badgeCount: Int? = null,
21 | colors: NavigationBarItemColors = NavigationBarItemDefaults.colors(),
22 | ) {
23 | NavigationBarItem(
24 | selected = selected,
25 | onClick = onClick,
26 | icon = {
27 | // BadgedBox(
28 | // badge = {
29 | // badgeCount?.let { count ->
30 | // Badge(
31 | // modifier = Modifier.padding(top = Spacing.x01),
32 | // ) {
33 | // Text(count.toString())
34 | // }
35 | // }
36 | // },
37 | // ) {
38 | // Icon(
39 | // painter = icon,
40 | // contentDescription = label,
41 | // )
42 | // }
43 | },
44 | modifier = modifier,
45 | label = label?.let {
46 | {
47 | Text(
48 | text = label,
49 | overflow = TextOverflow.Clip,
50 | maxLines = 1,
51 | )
52 | }
53 | },
54 | colors = colors,
55 | )
56 | }
57 |
58 | @Composable
59 | fun RowScope.MainNavigationBarItem(
60 | selected: Boolean,
61 | onClick: () -> Unit,
62 | icon: Painter,
63 | modifier: Modifier = Modifier,
64 | label: String? = null,
65 | badgeCount: Int? = null,
66 | colors: NavigationBarItemColors = NavigationBarItemDefaults.colors(),
67 | ) {
68 | NavigationBarItemWithBadge(
69 | selected = selected,
70 | onClick = onClick,
71 | icon = icon,
72 | modifier = modifier,
73 | label = label,
74 | badgeCount = badgeCount,
75 | colors = colors,
76 | )
77 | }
78 |
--------------------------------------------------------------------------------
/modules/library-ui-shared/src/commonMain/kotlin/com/jarroyo/library/ui/shared/component/Placeholder.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.library.ui.shared.component
2 |
3 | import androidx.compose.ui.Modifier
4 | import androidx.compose.ui.graphics.Color
5 | import com.eygraber.compose.placeholder.placeholder
6 |
7 | fun Modifier.placeholder(visible: Boolean) = Modifier.placeholder(
8 | visible = visible,
9 | color = Color.Gray,
10 | )
11 |
--------------------------------------------------------------------------------
/modules/library-ui-shared/src/commonMain/kotlin/com/jarroyo/library/ui/shared/component/ProvidableCompositionLocal.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.library.ui.shared.component
2 |
3 | import androidx.compose.foundation.layout.PaddingValues
4 | import androidx.compose.material3.SnackbarHostState
5 | import androidx.compose.runtime.MutableState
6 | import androidx.compose.runtime.ProvidableCompositionLocal
7 | import androidx.compose.runtime.compositionLocalOf
8 |
9 | val LocalMainScaffoldPadding: ProvidableCompositionLocal> = compositionLocalOf { error("No PaddingValues provided") }
10 | val LocalSnackbarHostState: ProvidableCompositionLocal = compositionLocalOf { error("No SnackbarHostState provided") }
11 |
--------------------------------------------------------------------------------
/modules/library-ui-shared/src/commonMain/kotlin/com/jarroyo/library/ui/shared/theme/Spacing.kt:
--------------------------------------------------------------------------------
1 | package com.jarroyo.library.ui.shared.theme
2 |
3 | import androidx.compose.ui.unit.dp
4 |
5 | object Spacing {
6 | val quarter = 2.dp
7 | val half = 4.dp
8 | val x01 = 8.dp
9 | val x02 = 16.dp
10 | val x03 = 24.dp
11 | val x04 = 32.dp
12 | val x05 = 40.dp
13 | val x06 = 48.dp
14 | val x07 = 56.dp
15 | val x08 = 64.dp
16 | val x12 = 96.dp
17 | val x16 = 128.dp
18 | }
19 |
--------------------------------------------------------------------------------
/screenshots/android/detail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/screenshots/android/detail.png
--------------------------------------------------------------------------------
/screenshots/android/graph.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/screenshots/android/graph.png
--------------------------------------------------------------------------------
/screenshots/android/home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/screenshots/android/home.png
--------------------------------------------------------------------------------
/screenshots/android/loading.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/screenshots/android/loading.png
--------------------------------------------------------------------------------
/screenshots/compose_multiplatform_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/screenshots/compose_multiplatform_logo.png
--------------------------------------------------------------------------------
/screenshots/desktop/detail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/screenshots/desktop/detail.png
--------------------------------------------------------------------------------
/screenshots/desktop/graph.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/screenshots/desktop/graph.png
--------------------------------------------------------------------------------
/screenshots/desktop/home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/screenshots/desktop/home.png
--------------------------------------------------------------------------------
/screenshots/desktop/loading.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/screenshots/desktop/loading.png
--------------------------------------------------------------------------------
/screenshots/ios/buildPhases.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/screenshots/ios/buildPhases.png
--------------------------------------------------------------------------------
/screenshots/ios/detail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/screenshots/ios/detail.png
--------------------------------------------------------------------------------
/screenshots/ios/graph.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/screenshots/ios/graph.png
--------------------------------------------------------------------------------
/screenshots/ios/home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/screenshots/ios/home.png
--------------------------------------------------------------------------------
/screenshots/ios/loading.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/screenshots/ios/loading.png
--------------------------------------------------------------------------------
/screenshots/summary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jarroyoesp/ComposeMultiplatformApp/080da979498605ff700ce185d29475e7bef384c8/screenshots/summary.png
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | google()
5 | mavenCentral()
6 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
7 | }
8 | }
9 | // https://docs.gradle.org/7.0/userguide/declaring_dependencies.html#sec:type-safe-project-accessors
10 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
11 |
12 | @Suppress("UnstableApiUsage")
13 | dependencyResolutionManagement {
14 | repositories {
15 | gradlePluginPortal()
16 | google()
17 | mavenCentral()
18 | maven("https://jogamp.org/deployment/maven")
19 | }
20 | }
21 | rootProject.name = "ComposeApp"
22 | includeBuild("build-conventions")
23 | include(
24 | // Apps
25 | ":app",
26 | ":desktop",
27 | ":iosapp",
28 |
29 | // Modules
30 | ":modules:feature-common",
31 | ":modules:feature-common-api",
32 | ":modules:feature-electricity",
33 | ":modules:feature-electricity-api",
34 | ":modules:feature-home-api",
35 | ":modules:feature-home-shared",
36 | ":modules:feature-launches",
37 | ":modules:feature-launches-api",
38 | ":modules:feature-schedules",
39 | ":modules:feature-schedules-api",
40 | ":modules:library-feature",
41 | ":modules:library-navigation",
42 | ":modules:library-navigation-api",
43 | ":modules:library-network",
44 | ":modules:library-network-api",
45 | ":modules:library-test",
46 | ":modules:library-ui-shared",
47 | )
48 |
--------------------------------------------------------------------------------