diff --git a/.github/config/configuration.json b/.github/config/configuration.json new file mode 100644 index 0000000..8729ce9 --- /dev/null +++ b/.github/config/configuration.json @@ -0,0 +1,29 @@ +{ + "categories": [ + { + "title": "## 🚀 Features", + "labels": [ + "feature" + ] + }, + { + "title": "## 🐛 Fixes", + "labels": [ + "fix" + ] + }, + { + "title": "## 🧪 Tests", + "labels": [ + "test" + ] + }, + { + "title": "## 💬 Other", + "labels": [ + "other", + "dependencies" + ] + } + ] +} \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index db12478..1a87522 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,27 +2,35 @@ name: Build on: push: - branches: [ develop ] + branches: [ develop, main ] pull_request: branches: [ main, develop ] +concurrency: + group: build-${{ github.ref }} + cancel-in-progress: true + jobs: build: name: 🔨 Build runs-on: ubuntu-latest steps: - - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 + + - name: Validate Gradle Wrapper + uses: gradle/wrapper-validation-action@v3 - name: Set up JDK 17 - uses: actions/setup-java@v1 + uses: actions/setup-java@v4 with: - java-version: 17 + java-version: '17' + distribution: 'adopt' + cache: gradle - name: Make gradle executable run: chmod +x ./gradlew - - name: Build with gradle - run: ./gradlew build --stacktrace \ No newline at end of file + - name: Build app + run: ./gradlew assemble --stacktrace \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..937f9c5 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,144 @@ +name: Release + +on: + push: + tags: + - '*' + +defaults: + run: + shell: bash + +jobs: + release_desktop: + name: Release Desktop App + strategy: + fail-fast: false + matrix: + os: [ macos-latest, ubuntu-latest, windows-latest ] + runs-on: ${{ matrix.os }} + if: startsWith(github.ref, 'refs/tags/') + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Validate Gradle Wrapper + uses: gradle/wrapper-validation-action@v3 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'adopt' + cache: gradle + + - name: Make gradle executable + run: chmod +x ./gradlew + + - name: Checkout Gradle Build Cache + uses: actions/cache@v2 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + !~/.gradle/wrapper/dists/**/gradle*.zip + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} + restore-keys: | + gradle-${{ runner.os }}- + + - name: Build app + run: ./gradlew :desktop:assemble --stacktrace + + - name: Build Release App + run: | + ./gradlew packageUberJarForCurrentOS + ./gradlew package + + - name: Archive Artifacts + uses: actions/upload-artifact@v2 + with: + name: distributable-${{ matrix.os }} + if-no-files-found: ignore + path: | + desktop/build/**/*.deb + desktop/build/**/*.msi + desktop/build/**/*.dmg + desktop/build/compose/jars/*.jar + + - name: Release + uses: softprops/action-gh-release@91409e712cf565ce9eff10c87a8d1b11b81757ae + with: + prerelease: ${{ contains(github.event.inputs.version, '-rc') || contains(github.event.inputs.version, '-b') || contains(github.event.inputs.version, '-a') }} + files: | + desktop/build/**/*.deb + desktop/build/**/*.msi + desktop/build/**/*.dmg + desktop/build/compose/jars/*.jar + env: + GITHUB_TOKEN: ${{ secrets.MY_GITHUB_TOKEN }} + + release_android: + name: Release Android App + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/') + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Validate Gradle Wrapper + uses: gradle/wrapper-validation-action@v3 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'adopt' + cache: gradle + + - name: Make gradle executable + run: chmod +x ./gradlew + + - name: Build app + run: ./gradlew :android:assembleRelease --stacktrace + + - name: Archive Artifacts + uses: actions/upload-artifact@v2 + with: + name: android-app + if-no-files-found: ignore + path: android/build/outputs/apk/release/*.apk + + - name: Release + uses: softprops/action-gh-release@91409e712cf565ce9eff10c87a8d1b11b81757ae + with: + prerelease: ${{ contains(github.event.inputs.version, '-rc') || contains(github.event.inputs.version, '-b') || contains(github.event.inputs.version, '-a') }} + files: | + android/build/outputs/apk/release/*.apk + env: + GITHUB_TOKEN: ${{ secrets.MY_GITHUB_TOKEN }} + + changelog: + name: Changelog + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/') + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Build Changelog + id: github_release + uses: mikepenz/release-changelog-builder-action@v1 + with: + configuration: ".github/config/configuration.json" + commitMode: true + ignorePreReleases: ${{ !contains(github.ref, '-') }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Release + uses: softprops/action-gh-release@91409e712cf565ce9eff10c87a8d1b11b81757ae + with: + body: ${{steps.github_release.outputs.changelog}} + prerelease: ${{ contains(github.event.inputs.version, '-rc') || contains(github.event.inputs.version, '-b') || contains(github.event.inputs.version, '-a') }} + env: + GITHUB_TOKEN: ${{ secrets.MY_GITHUB_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index bf68fba..28887f3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,103 @@ -*.iml +# Gradle .gradle +build/ + +captures + /local.properties -.idea + +# IntelliJ .idea folder +.idea/workspace.xml +.idea/misc.xml +.idea/libraries +.idea/caches +.idea/navEditor.xml +.idea/tasks.xml +.idea/modules.xml +.idea/compiler.xml +.idea/jarRepositories.xml +.idea/deploymentTargetDropDown.xml +.idea/androidTestResultsUserPreferences.xml +.idea/appInsightsSettings.xml +.idea/artifacts +gradle.xml +*.iml +.fleet + +# General .DS_Store -/build -*/build -/captures .externalNativeBuild -.cxx -local.properties \ No newline at end of file + +# Do not commit plain-text release keys +app-release.jks +play-account.p12 +play-account.json + +# Do not commit firebase config +google-services.json + +# VS Code config +org.eclipse.buildship.core.prefs +.classpath +.project +bin/ + +# Docs +site/ +.cache/ + +# Ignore baseline profile files in samples +sample/**/generated + +########################################################################################## +# Imported from https://github.com/github/gitignore/blob/main/Swift.gitignore +########################################################################################## + +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## User settings +xcuserdata/ + +## Obj-C/Swift specific +*.hmap + +## App packaging +*.ipa +*.dSYM.zip +*.dSYM + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +# Packages/ +# Package.pins +# Package.resolved +# *.xcodeproj +# +# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata +# hence it is not needed unless you have added a package configuration file to your project +# .swiftpm + +.build/ + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. +# Instead, use fastlane to re-generate the screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +report.xml +Preview.html +screenshots/**/*.png +test_output + +*.env* + +.kotlin \ No newline at end of file diff --git a/.scripts/pre-commit b/.scripts/pre-commit index 7516da5..bab6c63 100644 --- a/.scripts/pre-commit +++ b/.scripts/pre-commit @@ -32,12 +32,6 @@ else exit 1 fi -./gradlew app:ktlintCheck --daemon -STATUS=$? - -# return 1 exit code if running checks -[ $STATUS -ne 0 ] && exit 1 -exit 0 # Branch branch="$(git rev-parse --abbrev-ref HEAD)" diff --git a/README.md b/README.md index af155d8..15873a3 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ -

MealTime

- # FocusBloom FocusBloom is a Kotlin Multiplatform app that helps users enhance their productivity and time management skills through focused work intervals and short breaks. @@ -12,7 +10,6 @@ FocusBloom is a Kotlin Multiplatform app that helps users enhance their producti ![](https://img.shields.io/badge/Android-black.svg?style=for-the-badge&logo=android) | ![](https://img.shields.io/badge/iOS-black.svg?style=for-the-badge&logo=apple) | ![](https://img.shields.io/badge/Desktop-black.svg?style=for-the-badge&logo=windows) | ![](https://img.shields.io/badge/Web-black.svg?style=for-the-badge&logo=google-chrome) :----: | :----: | :----: | :----: ✅ | ✅ | ✅ | Planned -Get it on Google Play ## Screenshots ### Android @@ -29,6 +26,18 @@ FocusBloom is a Kotlin Multiplatform app that helps users enhance their producti ## Architecture The app is shared between Android, iOS and Desktop. The shared code is written in Kotlin and the UI is built with Compose Multiplatform. Shared code, written in Kotlin, is compiled to JVM bytecode for Android and Desktop with Kotlin/JVM and to native binaries for iOS with Kotlin/Native. ### Modules + +```mermaid +%%{ + init: { + 'theme': 'neutral' + } +}%% + +graph LR + :desktop --> :shared + :android --> :shared +``` - shared: - contains all the shared code between the platforms - android: @@ -51,8 +60,6 @@ The app is shared between Android, iOS and Desktop. The shared code is written i - [Compose Components Resources](https://mvnrepository.com/artifact/org.jetbrains.compose.components/components-resources) - Resources For Compose Multiplatform. - [Material3 Window Size Multiplatform](https://github.com/chrisbanes/material3-windowsizeclass-multiplatform) - About Material 3 Window Size Class for Compose Multiplatform. - [Spotless](https://github.com/diffplug/spotless) - A code formatter that helps keep the codebase clean. -- [Detekt](https://github.com/detekt/detekt) - Static code analysis for Kotlin. -- [Ktlint](https://github.com/pinterest/ktlint) - A static code analysis tool and formatter for Kotlin. - [Github Actions](https://docs.github.com/en/actions) - A CI/CD tool that helps automate workflows. - [Renovate](https://docs.renovatebot.com/) - An open-source software tool designed to help automate the process of updating dependencies in software projects. @@ -85,4 +92,4 @@ Copyright 2023 JoelKanyi See the License for the specific language governing permissions and limitations under the License. ``` - + \ No newline at end of file diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 85d056f..56347ce 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -17,6 +17,7 @@ plugins { alias(libs.plugins.android.application) alias(libs.plugins.android.kotlin) alias(libs.plugins.compose.multiplatform) + alias(libs.plugins.compose.compiler) } android { @@ -37,7 +38,13 @@ android { isDebuggable = true } getByName("release") { - isMinifyEnabled = false + isMinifyEnabled = true + isShrinkResources = true + + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro", + ) } } @@ -54,14 +61,10 @@ android { buildFeatures { compose = true } - - composeOptions { - kotlinCompilerExtensionVersion = "1.5.2" - } } dependencies { - implementation(project(":shared")) + implementation(projects.shared) implementation(libs.compose.activity) implementation(libs.koin.android) coreLibraryDesugaring(libs.core.library.desugaring) diff --git a/android/src/main/java/com/joelkanyi/focusbloom/android/BloomApp.kt b/android/src/main/java/com/joelkanyi/focusbloom/android/BloomApp.kt index e46adbc..228dbaf 100644 --- a/android/src/main/java/com/joelkanyi/focusbloom/android/BloomApp.kt +++ b/android/src/main/java/com/joelkanyi/focusbloom/android/BloomApp.kt @@ -18,7 +18,7 @@ package com.joelkanyi.focusbloom.android import android.app.Application import com.joelkanyi.focusbloom.android.di.androidModule import com.joelkanyi.focusbloom.di.KoinInit -import io.github.aakira.napier.BuildConfig +import com.russhwolf.settings.BuildConfig import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidLogger import org.koin.core.logger.Level @@ -32,8 +32,8 @@ class BloomApp : Application() { androidContext(androidContext = this@BloomApp) modules( listOf( - androidModule - ) + androidModule, + ), ) } } diff --git a/android/src/main/java/com/joelkanyi/focusbloom/android/MainActivity.kt b/android/src/main/java/com/joelkanyi/focusbloom/android/MainActivity.kt index f9fba09..9f4bf8d 100644 --- a/android/src/main/java/com/joelkanyi/focusbloom/android/MainActivity.kt +++ b/android/src/main/java/com/joelkanyi/focusbloom/android/MainActivity.kt @@ -40,8 +40,8 @@ class MainActivity : ComponentActivity() { mutableStateOf( ContextCompat.checkSelfPermission( this, - android.Manifest.permission.POST_NOTIFICATIONS - ) == PackageManager.PERMISSION_GRANTED + android.Manifest.permission.POST_NOTIFICATIONS, + ) == PackageManager.PERMISSION_GRANTED, ) } else { mutableStateOf(true) @@ -52,7 +52,7 @@ class MainActivity : ComponentActivity() { contract = ActivityResultContracts.RequestPermission(), onResult = { granted -> hasCamPermission = granted - } + }, ) LaunchedEffect(key1 = true, block = { diff --git a/build.gradle.kts b/build.gradle.kts index e65d207..1ff05a6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,9 +7,13 @@ plugins { alias(libs.plugins.nativeCocoapod) apply false alias(libs.plugins.compose.multiplatform) alias(libs.plugins.spotless) - alias(libs.plugins.ktlint) - alias(libs.plugins.detekt) - alias(libs.plugins.gradleVersionUpdates) + id("dev.iurysouza.modulegraph") version "0.8.1" + alias(libs.plugins.compose.compiler) apply false +} + +moduleGraphConfig { + readmePath.set("./README.md") + heading = "### Module Graph" } allprojects { @@ -19,44 +23,16 @@ allprojects { maven(url = "https://jitpack.io") maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") } - - apply(plugin = "org.jlleitschuh.gradle.ktlint") - ktlint { - debug.set(true) - verbose.set(true) - android.set(false) - outputToConsole.set(true) - outputColorName.set("RED") - filter { - enableExperimentalRules.set(true) - exclude { projectDir.toURI().relativize(it.file.toURI()).path.contains("/generated/") } - include("**/kotlin/**") - } - } } subprojects { - apply(plugin = "io.gitlab.arturbosch.detekt") - detekt { - parallel = true - config = files("${project.rootDir}/config/detekt/detekt.yml") - } - - tasks.withType { - checkForGradleUpdate = true - outputFormatter = "html" - outputDir = "build/reports/dependencyUpdates" - reportfileName = "report" - } - apply(plugin = "com.diffplug.spotless") spotless { kotlin { target("**/*.kt") - ktlint().userData(mapOf("disabled_rules" to "filename")) licenseHeaderFile( rootProject.file("${project.rootDir}/spotless/copyright.kt"), - "^(package|object|import|interface)" + "^(package|object|import|interface)", ) trimTrailingWhitespace() endWithNewline() @@ -74,11 +50,3 @@ subprojects { } } } - -task("addPreCommitGitHookOnBuild") { - println("⚈ ⚈ ⚈ Running Add Pre Commit Git Hook Script on Build ⚈ ⚈ ⚈") - exec { - commandLine("cp", "./.scripts/pre-commit", "./.git/hooks") - } - println("✅ Added Pre Commit Git Hook Script.") -} diff --git a/desktop/build.gradle.kts b/desktop/build.gradle.kts index 6072b87..84217e6 100644 --- a/desktop/build.gradle.kts +++ b/desktop/build.gradle.kts @@ -13,28 +13,60 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import org.jetbrains.compose.desktop.application.dsl.TargetFormat + plugins { alias(libs.plugins.jvm) alias(libs.plugins.compose.multiplatform) + alias(libs.plugins.compose.compiler) } dependencies { - implementation(project(":shared")) + implementation(projects.shared) implementation(compose.desktop.currentOs) } +group = "com.joelkanyi" +version = "1.0.0" + compose.desktop { application { mainClass = "DesktopAppKt" nativeDistributions { - targetFormats( - org.jetbrains.compose.desktop.application.dsl.TargetFormat.Dmg, - org.jetbrains.compose.desktop.application.dsl.TargetFormat.Msi, - org.jetbrains.compose.desktop.application.dsl.TargetFormat.Deb, - org.jetbrains.compose.desktop.application.dsl.TargetFormat.Rpm + targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) + + packageName = "FocusBloom" + packageVersion = project.version as String + description = + "An app that helps users enhance their productivity and time management skills through focused work intervals and short breaks." + copyright = "© 2023 Joel Kanyi" + vendor = "Joel Kanyi" + + // .gradlew suggestRuntimeModules + modules( + "java.instrument", + "java.management", + "java.prefs", + "java.sql", + "jdk.unsupported" ) - packageName = "focusbloom" - packageName = "1.0.0" + + val iconsRoot = project.file("src/main/resources/drawables") + + linux { + iconFile.set(iconsRoot.resolve("launcher_icons/linuxos.png")) + } + + windows { + iconFile.set(iconsRoot.resolve("launcher_icons/windowsos.ico")) + upgradeUuid = "31575EDF-D0D5-4CEF-A4D2-7562083D6D88" + menuGroup = packageName + perUserInstall = true + } + + macOS { + iconFile.set(iconsRoot.resolve("launcher_icons/macos.icns")) + } } } } diff --git a/desktop/src/main/kotlin/DesktopApp.kt b/desktop/src/main/kotlin/DesktopApp.kt index 1a07857..eae6e3a 100644 --- a/desktop/src/main/kotlin/DesktopApp.kt +++ b/desktop/src/main/kotlin/DesktopApp.kt @@ -31,7 +31,7 @@ lateinit var koin: Koin fun main() { koin = KoinInit().init() koin.loadModules( - listOf() + listOf(), ) return application { @@ -41,8 +41,8 @@ fun main() { state = rememberWindowState( position = WindowPosition.Aligned(Alignment.Center), width = 1200.dp, - height = 700.dp - ) + height = 700.dp, + ), ) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { FocusBloomApp() diff --git a/desktop/src/main/resources/drawables/launcher_icons/linuxos.png b/desktop/src/main/resources/drawables/launcher_icons/linuxos.png new file mode 100644 index 0000000..48b7447 Binary files /dev/null and b/desktop/src/main/resources/drawables/launcher_icons/linuxos.png differ diff --git a/desktop/src/main/resources/drawables/launcher_icons/macos.icns b/desktop/src/main/resources/drawables/launcher_icons/macos.icns new file mode 100644 index 0000000..4ca6232 Binary files /dev/null and b/desktop/src/main/resources/drawables/launcher_icons/macos.icns differ diff --git a/desktop/src/main/resources/drawables/launcher_icons/windowsos.ico b/desktop/src/main/resources/drawables/launcher_icons/windowsos.ico new file mode 100644 index 0000000..0b5d718 Binary files /dev/null and b/desktop/src/main/resources/drawables/launcher_icons/windowsos.ico differ diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a78a0ca..1df20f0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,64 +1,43 @@ [versions] -componentsResources = "1.5.3" -core = "1.12.0" -koalaplotCore = "0.4.0-dev1" -korau = "4.0.6" -kotlin = "1.9.10" -gradle = "8.1.2" -kotlinxCoroutinesSwing = "1.7.3" -detekt = "1.19.0" -gradleVersionUpdate = "0.46.0" -koinCore = "3.5.0" -koinAndroid = "3.5.0" -kotlinxSerializationJson = "1.6.0" -kotlinxDateTime = "0.4.1" -material3WindowSizeClassMultiplatform = "0.3.1" -napier = "2.6.1" -sqlDelight = "2.0.0" -multiplatformSettings = "1.1.0" -compose = "1.5.10-beta01" -core-library-desugaring = "2.0.3" -toast4j = "0.2.0" -voyager = "1.0.0-rc07" -compose-activity = "1.8.0" -koin-compose = "1.1.0" -spotless = "5.17.1" -ktlint = "11.6.1" -accompanist-systemUIController = "0.30.1" -annotationJvm = "1.7.0" -[plugins] -detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } -compose-multiplatform = { id = "org.jetbrains.compose", version.ref = "compose" } -gradleVersionUpdates = { id = "com.github.ben-manes.versions", version.ref = "gradleVersionUpdate" } -multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } -nativeCocoapod = { id = "org.jetbrains.kotlin.native.cocoapods", version.ref = "kotlin" } -android-kotlin = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } -android-library = { id = "com.android.library", version.ref = "gradle" } -android-application = { id = "com.android.application", version.ref = "gradle" } -jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -kotlinX-serialization-plugin = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } -sqlDelight-plugin = { id = "app.cash.sqldelight", version.ref = "sqlDelight" } -spotless = { id = "com.diffplug.spotless", version.ref = "spotless" } -ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" } +androidDriver = "2.0.2" +kotlin = "2.0.0" +compose = "1.6.10" +koinCore = "3.5.6" +koinAndroid = "3.5.6" +gradle = "8.4.0" +sqlDelight = " 2.0.2" +voyager = "1.0.0" +multiplatformSettings = "1.1.1" +koin-compose = "1.1.5" +core = "1.13.1" +jna = "5.14.0" +koalaplotCore = "0.5.4" +kotlinxCoroutinesSwing = "1.8.0" +kotlinxSerializationJson = "1.6.3" +kotlinxDateTime = "0.5.0" +material3WindowSizeClassMultiplatform = "0.5.0" +napier = "2.7.1" +core-library-desugaring = "2.0.4" +toast4j = "0.2.0" +compose-activity = "1.9.0" +spotless = "6.25.0" +accompanist-systemUIController = "0.34.0" [libraries] -components-resources = { module = "org.jetbrains.compose.components:components-resources", version.ref = "componentsResources" } +android-driver = { module = "app.cash.sqldelight:android-driver", version.ref = "androidDriver" } core = { module = "androidx.core:core", version.ref = "core" } +coroutines-extensions = { module = "app.cash.sqldelight:coroutines-extensions", version.ref = "androidDriver" } +jna = { module = "net.java.dev.jna:jna", version.ref = "jna" } koalaplot-core = { module = "io.github.koalaplot:koalaplot-core", version.ref = "koalaplotCore" } koin-core = { module = "io.insert-koin:koin-core", version.ref = "koinCore" } koin-compose = { module = "io.insert-koin:koin-compose", version.ref = "koin-compose"} -korau = { module = "com.soywiz.korlibs.korau:korau", version.ref = "korau" } kotlinx-coroutines-swing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "kotlinxCoroutinesSwing" } kotlinX-serializationJson = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "kotlinxSerializationJson" } material3-window-size-multiplatform = { module = "dev.chrisbanes.material3:material3-window-size-class-multiplatform", version.ref = "material3WindowSizeClassMultiplatform" } -primitive-adapters = { module = "app.cash.sqldelight:primitive-adapters", version.ref = "sqlDelight" } +native-driver = { module = "app.cash.sqldelight:native-driver", version.ref = "androidDriver" } +primitive-adapters = { module = "app.cash.sqldelight:primitive-adapters", version.ref = "androidDriver" } sqlDelight-runtime = { module = "app.cash.sqldelight:runtime", version.ref = "sqlDelight" } -sqlDelight-coroutine = { module = "app.cash.sqldelight:coroutines-extensions", version.ref = "sqlDelight" } -sqlDelight-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqlDelight" } -sqlDelight-native = { module = "app.cash.sqldelight:native-driver", version.ref = "sqlDelight" } -sqlDelight-JVM = { module = "app.cash.sqldelight:sqlite-driver", version.ref = "sqlDelight" } -sqlDelight-driver-sqljs = { module = "app.cash.sqldelight:sqljs-driver", version = "2.0.0-alpha05" } napier = { module = "io.github.aakira:napier", version.ref = "napier" } kotlinX-dateTime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinxDateTime" } multiplatformSettings-noArg = { module = "com.russhwolf:multiplatform-settings-no-arg", version.ref = "multiplatformSettings" } @@ -66,14 +45,30 @@ multiplatformSettings-coroutines = { module = "com.russhwolf:multiplatform-setti compose-activity = { module = "androidx.activity:activity-compose", version.ref = "compose-activity" } koin-android = { module = "io.insert-koin:koin-android", version.ref = "koinAndroid" } +sqlite-driver = { module = "app.cash.sqldelight:sqlite-driver", version.ref = "androidDriver" } toast4j = { module = "de.mobanisto:toast4j", version.ref = "toast4j" } + +# Voyager voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" } voyager-bottomSheetNavigator = { module = "cafe.adriel.voyager:voyager-bottom-sheet-navigator", version.ref = "voyager" } voyager-tabNavigator = { module = "cafe.adriel.voyager:voyager-tab-navigator", version.ref = "voyager" } voyager-transitions = { module = "cafe.adriel.voyager:voyager-transitions", version.ref = "voyager" } +voyager-koin = { module = "cafe.adriel.voyager:voyager-koin", version.ref = "voyager" } + core-library-desugaring = { module = "com.android.tools:desugar_jdk_libs", version.ref = "core-library-desugaring" } accompanist-systemUIController = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanist-systemUIController" } -lifecycle-common = { group = "androidx.lifecycle", name = "lifecycle-common", version = "2.6.2" } -annotation-jvm = { group = "androidx.annotation", name = "annotation-jvm", version.ref = "annotationJvm" } -stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" } \ No newline at end of file +stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" } + +[plugins] +compose-multiplatform = { id = "org.jetbrains.compose", version.ref = "compose" } +multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } +compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } +nativeCocoapod = { id = "org.jetbrains.kotlin.native.cocoapods", version.ref = "kotlin" } +android-kotlin = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +android-library = { id = "com.android.library", version.ref = "gradle" } +android-application = { id = "com.android.application", version.ref = "gradle" } +jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } +kotlinX-serialization-plugin = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } +sqlDelight-plugin = { id = "app.cash.sqldelight", version.ref = "sqlDelight" } +spotless = { id = "com.diffplug.spotless", version.ref = "spotless" } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e708b1c..d64cd49 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 5f9cae7..b82aa23 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ -#Fri Aug 18 22:40:35 EAT 2023 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 4f906e0..1aa94a4 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,67 +17,99 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +119,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,88 +130,120 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 107acd3..25da30d 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,13 +41,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -56,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/ios/Podfile.lock b/ios/Podfile.lock index d90cf18..8352fa8 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -9,8 +9,8 @@ EXTERNAL SOURCES: :path: "../shared" SPEC CHECKSUMS: - shared: 3e0befb0fecedf50640d928d7650c497b1d237ab + shared: c3b82c05e553e3953cbbc618b8894285fa4cf610 PODFILE CHECKSUM: 83af500b7f00be241df85ae41eabbcbb267800e4 -COCOAPODS: 1.12.1 +COCOAPODS: 1.15.2 diff --git a/ios/Pods/Local Podspecs/shared.podspec.json b/ios/Pods/Local Podspecs/shared.podspec.json index 9e70a8c..0e6a5b8 100644 --- a/ios/Pods/Local Podspecs/shared.podspec.json +++ b/ios/Pods/Local Podspecs/shared.podspec.json @@ -13,6 +13,9 @@ "platforms": { "ios": "14.1" }, + "xcconfig": { + "ENABLE_USER_SCRIPT_SANDBOXING": "NO" + }, "pod_target_xcconfig": { "KOTLIN_PROJECT_PATH": ":shared", "PRODUCT_MODULE_NAME": "shared" @@ -26,6 +29,6 @@ } ], "resources": [ - "build/compose/ios/shared/compose-resources" + "build/compose/cocoapods/compose-resources" ] } diff --git a/ios/Pods/Manifest.lock b/ios/Pods/Manifest.lock index d90cf18..8352fa8 100644 --- a/ios/Pods/Manifest.lock +++ b/ios/Pods/Manifest.lock @@ -9,8 +9,8 @@ EXTERNAL SOURCES: :path: "../shared" SPEC CHECKSUMS: - shared: 3e0befb0fecedf50640d928d7650c497b1d237ab + shared: c3b82c05e553e3953cbbc618b8894285fa4cf610 PODFILE CHECKSUM: 83af500b7f00be241df85ae41eabbcbb267800e4 -COCOAPODS: 1.12.1 +COCOAPODS: 1.15.2 diff --git a/ios/Pods/Pods.xcodeproj/project.pbxproj b/ios/Pods/Pods.xcodeproj/project.pbxproj index b0610c3..9395803 100644 --- a/ios/Pods/Pods.xcodeproj/project.pbxproj +++ b/ios/Pods/Pods.xcodeproj/project.pbxproj @@ -22,12 +22,12 @@ /* Begin PBXBuildFile section */ 648F16425FEF89525AE0325F5A984B86 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 73010CC983E3809BECEE5348DA1BB8C6 /* Foundation.framework */; }; - 8749C8E8DC500B064FA0BC7A78C38A2A /* Pods-iosApp-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = E6DB2A5F5DADA1DDE45F36B1A2D6AC16 /* Pods-iosApp-dummy.m */; }; - 8801CBFD38B946597BD07145B2EEFC9F /* Pods-iosApp-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 015E0D7EA7331961AB63E5AFECA86BB5 /* Pods-iosApp-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8749C8E8DC500B064FA0BC7A78C38A2A /* Pods-iosApp-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 54DC7F9924F99FEE67A9AD3BEE09A10C /* Pods-iosApp-dummy.m */; }; + 8801CBFD38B946597BD07145B2EEFC9F /* Pods-iosApp-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = F87A626222A8D9B26448A9FEB04FD0DF /* Pods-iosApp-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 3B3DD97234976EAE23DEF848B521F3BD /* PBXContainerItemProxy */ = { + C1E4F2F8101E860063100BCC8448BC3B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; @@ -37,25 +37,23 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 015E0D7EA7331961AB63E5AFECA86BB5 /* Pods-iosApp-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-iosApp-umbrella.h"; sourceTree = ""; }; - 11C9B998CE0869936AE6BE69270DAAC9 /* Pods-iosApp.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-iosApp.modulemap"; sourceTree = ""; }; - 15C62982CC8B1FD56214604CE982F646 /* compose-resources */ = {isa = PBXFileReference; includeInIndex = 1; name = "compose-resources"; path = "build/compose/ios/shared/compose-resources"; sourceTree = ""; }; - 215D65FA831B0E4F8AC0B3826A846FDA /* shared.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = shared.debug.xcconfig; sourceTree = ""; }; - 244FC2DAA936A0B07ACEFDC74E2D54B8 /* Pods-iosApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-iosApp.release.xcconfig"; sourceTree = ""; }; - 45CF8D032A049A7012F30257A8F121AF /* shared-copy-dsyms.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "shared-copy-dsyms.sh"; sourceTree = ""; }; - 55ABB06C8A1800962A74E007E7733796 /* Pods-iosApp-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-iosApp-frameworks.sh"; sourceTree = ""; }; - 5F931FED0EB9A618FB9EFF402884E1AC /* shared.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; path = shared.podspec; sourceTree = ""; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; - 670597F16E31933FDD530ED5140D4EEF /* shared.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = shared.framework; path = build/cocoapods/framework/shared.framework; sourceTree = ""; }; - 6F3B5FED07117E377CFAC3D49B490F17 /* Pods-iosApp-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-iosApp-resources.sh"; sourceTree = ""; }; + 0187F135D5F61750CD24C3E04B6E8B64 /* Pods-iosApp-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-iosApp-frameworks.sh"; sourceTree = ""; }; + 1192783415C3DAB1D0759742CCB1D491 /* shared.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; path = shared.podspec; sourceTree = ""; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 1E3AA4BB27FB47FC8730ED18E97BE3F7 /* shared-copy-dsyms.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "shared-copy-dsyms.sh"; sourceTree = ""; }; + 20D1F143BB7A4CD522117C279A4C293E /* shared.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = shared.release.xcconfig; sourceTree = ""; }; + 3F64BADB16C311E9D09DAD0116F684F4 /* Pods-iosApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-iosApp.debug.xcconfig"; sourceTree = ""; }; + 54DC7F9924F99FEE67A9AD3BEE09A10C /* Pods-iosApp-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-iosApp-dummy.m"; sourceTree = ""; }; + 5AC974169B09D4545F741E9A2147610E /* Pods-iosApp-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-iosApp-Info.plist"; sourceTree = ""; }; 73010CC983E3809BECEE5348DA1BB8C6 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; - 76A263267985B0185D85E24D40FCEE9A /* Pods-iosApp-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-iosApp-Info.plist"; sourceTree = ""; }; - 76D28488EA8CF5C697DFF07967A9960E /* Pods-iosApp-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-iosApp-acknowledgements.plist"; sourceTree = ""; }; + 8B524AABE529FB8A2D151B05321336E0 /* Pods-iosApp.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-iosApp.modulemap"; sourceTree = ""; }; + 8C79858733E6BECC4D9CD03C15051A72 /* shared.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = shared.framework; path = build/cocoapods/framework/shared.framework; sourceTree = ""; }; + 9BB0B8B698D4C35D4F9A07EF70910170 /* Pods-iosApp-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-iosApp-acknowledgements.plist"; sourceTree = ""; }; 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 9F1FFC8B10F4D7D4F37606E8959B3F29 /* Pods-iosApp-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-iosApp-acknowledgements.markdown"; sourceTree = ""; }; + 9FECCB5AFF87D121F15BA01E683C7932 /* Pods-iosApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-iosApp.release.xcconfig"; sourceTree = ""; }; B097DD7534E741D5C41838011D755842 /* Pods-iosApp */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = "Pods-iosApp"; path = Pods_iosApp.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - B7C6BC0A7177E1D2CCCA8D9834206E64 /* shared.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = shared.release.xcconfig; sourceTree = ""; }; - E462E23B3674BF94EAB1504D506F2803 /* Pods-iosApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-iosApp.debug.xcconfig"; sourceTree = ""; }; - E4C923318724794E3CC670804C2D6A6B /* Pods-iosApp-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-iosApp-acknowledgements.markdown"; sourceTree = ""; }; - E6DB2A5F5DADA1DDE45F36B1A2D6AC16 /* Pods-iosApp-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-iosApp-dummy.m"; sourceTree = ""; }; + F87A626222A8D9B26448A9FEB04FD0DF /* Pods-iosApp-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-iosApp-umbrella.h"; sourceTree = ""; }; + FD7FD28F3CCDF64520DA04A7CA0DDDC7 /* shared.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = shared.debug.xcconfig; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -70,110 +68,108 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 0BDC589A39A26B9CF449BC4DB7EAC506 /* shared */ = { + 036E461C32C1F48BA43823A2282B0000 /* Support Files */ = { isa = PBXGroup; children = ( - 15C62982CC8B1FD56214604CE982F646 /* compose-resources */, - DFA1DB9AC4A7CFB0C413CC312D0FE2B7 /* Frameworks */, - 2D40657AD4B86DBF0DA60AE3E13CDCB8 /* Pod */, - DAC212B1D8E90084ABE25A3B3FFC6E00 /* Support Files */, + 1E3AA4BB27FB47FC8730ED18E97BE3F7 /* shared-copy-dsyms.sh */, + FD7FD28F3CCDF64520DA04A7CA0DDDC7 /* shared.debug.xcconfig */, + 20D1F143BB7A4CD522117C279A4C293E /* shared.release.xcconfig */, ); - name = shared; - path = ../../shared; + name = "Support Files"; + path = "../ios/Pods/Target Support Files/shared"; sourceTree = ""; }; - 1F86AA6785DF34AFD5A71790761717DE /* Products */ = { + 11C970DEAE48C6D0282DFE54684F53F1 /* Targets Support Files */ = { isa = PBXGroup; children = ( - B097DD7534E741D5C41838011D755842 /* Pods-iosApp */, + 52A898E555C2DBD7D25A7E44754C3FC6 /* Pods-iosApp */, ); - name = Products; + name = "Targets Support Files"; sourceTree = ""; }; - 2D40657AD4B86DBF0DA60AE3E13CDCB8 /* Pod */ = { + 1F86AA6785DF34AFD5A71790761717DE /* Products */ = { isa = PBXGroup; children = ( - 5F931FED0EB9A618FB9EFF402884E1AC /* shared.podspec */, + B097DD7534E741D5C41838011D755842 /* Pods-iosApp */, ); - name = Pod; + name = Products; sourceTree = ""; }; - 578452D2E740E91742655AC8F1636D1F /* iOS */ = { + 2D696F51575A4504A458FD9155B8919F /* Pod */ = { isa = PBXGroup; children = ( - 73010CC983E3809BECEE5348DA1BB8C6 /* Foundation.framework */, + 1192783415C3DAB1D0759742CCB1D491 /* shared.podspec */, ); - name = iOS; + name = Pod; sourceTree = ""; }; - 58AAD176B64323B9974E5B70EC8B12DC /* Development Pods */ = { + 313FE5FE915A4A924C55AAC02A910D61 /* Development Pods */ = { isa = PBXGroup; children = ( - 0BDC589A39A26B9CF449BC4DB7EAC506 /* shared */, + CD6C10345B56CBB7EE822B2D6ED267E2 /* shared */, ); name = "Development Pods"; sourceTree = ""; }; - BA6B7BC2729F657E9D3682E55CA6E980 /* Pods-iosApp */ = { + 52A898E555C2DBD7D25A7E44754C3FC6 /* Pods-iosApp */ = { isa = PBXGroup; children = ( - 11C9B998CE0869936AE6BE69270DAAC9 /* Pods-iosApp.modulemap */, - E4C923318724794E3CC670804C2D6A6B /* Pods-iosApp-acknowledgements.markdown */, - 76D28488EA8CF5C697DFF07967A9960E /* Pods-iosApp-acknowledgements.plist */, - E6DB2A5F5DADA1DDE45F36B1A2D6AC16 /* Pods-iosApp-dummy.m */, - 55ABB06C8A1800962A74E007E7733796 /* Pods-iosApp-frameworks.sh */, - 76A263267985B0185D85E24D40FCEE9A /* Pods-iosApp-Info.plist */, - 6F3B5FED07117E377CFAC3D49B490F17 /* Pods-iosApp-resources.sh */, - 015E0D7EA7331961AB63E5AFECA86BB5 /* Pods-iosApp-umbrella.h */, - E462E23B3674BF94EAB1504D506F2803 /* Pods-iosApp.debug.xcconfig */, - 244FC2DAA936A0B07ACEFDC74E2D54B8 /* Pods-iosApp.release.xcconfig */, + 8B524AABE529FB8A2D151B05321336E0 /* Pods-iosApp.modulemap */, + 9F1FFC8B10F4D7D4F37606E8959B3F29 /* Pods-iosApp-acknowledgements.markdown */, + 9BB0B8B698D4C35D4F9A07EF70910170 /* Pods-iosApp-acknowledgements.plist */, + 54DC7F9924F99FEE67A9AD3BEE09A10C /* Pods-iosApp-dummy.m */, + 0187F135D5F61750CD24C3E04B6E8B64 /* Pods-iosApp-frameworks.sh */, + 5AC974169B09D4545F741E9A2147610E /* Pods-iosApp-Info.plist */, + F87A626222A8D9B26448A9FEB04FD0DF /* Pods-iosApp-umbrella.h */, + 3F64BADB16C311E9D09DAD0116F684F4 /* Pods-iosApp.debug.xcconfig */, + 9FECCB5AFF87D121F15BA01E683C7932 /* Pods-iosApp.release.xcconfig */, ); name = "Pods-iosApp"; path = "Target Support Files/Pods-iosApp"; sourceTree = ""; }; - CF1408CF629C7361332E53B88F7BD30C = { + 578452D2E740E91742655AC8F1636D1F /* iOS */ = { isa = PBXGroup; children = ( - 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */, - 58AAD176B64323B9974E5B70EC8B12DC /* Development Pods */, - D210D550F4EA176C3123ED886F8F87F5 /* Frameworks */, - 1F86AA6785DF34AFD5A71790761717DE /* Products */, - D456857FB6E5BC3266BEC21401D60DB5 /* Targets Support Files */, + 73010CC983E3809BECEE5348DA1BB8C6 /* Foundation.framework */, ); + name = iOS; sourceTree = ""; }; - D210D550F4EA176C3123ED886F8F87F5 /* Frameworks */ = { + 6E2D88EA0A916D116383DAFCE4BC46DD /* Frameworks */ = { isa = PBXGroup; children = ( - 578452D2E740E91742655AC8F1636D1F /* iOS */, + 8C79858733E6BECC4D9CD03C15051A72 /* shared.framework */, ); name = Frameworks; sourceTree = ""; }; - D456857FB6E5BC3266BEC21401D60DB5 /* Targets Support Files */ = { + CD6C10345B56CBB7EE822B2D6ED267E2 /* shared */ = { isa = PBXGroup; children = ( - BA6B7BC2729F657E9D3682E55CA6E980 /* Pods-iosApp */, + 6E2D88EA0A916D116383DAFCE4BC46DD /* Frameworks */, + 2D696F51575A4504A458FD9155B8919F /* Pod */, + 036E461C32C1F48BA43823A2282B0000 /* Support Files */, ); - name = "Targets Support Files"; + name = shared; + path = ../../shared; sourceTree = ""; }; - DAC212B1D8E90084ABE25A3B3FFC6E00 /* Support Files */ = { + CF1408CF629C7361332E53B88F7BD30C = { isa = PBXGroup; children = ( - 45CF8D032A049A7012F30257A8F121AF /* shared-copy-dsyms.sh */, - 215D65FA831B0E4F8AC0B3826A846FDA /* shared.debug.xcconfig */, - B7C6BC0A7177E1D2CCCA8D9834206E64 /* shared.release.xcconfig */, + 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */, + 313FE5FE915A4A924C55AAC02A910D61 /* Development Pods */, + D210D550F4EA176C3123ED886F8F87F5 /* Frameworks */, + 1F86AA6785DF34AFD5A71790761717DE /* Products */, + 11C970DEAE48C6D0282DFE54684F53F1 /* Targets Support Files */, ); - name = "Support Files"; - path = "../ios/Pods/Target Support Files/shared"; sourceTree = ""; }; - DFA1DB9AC4A7CFB0C413CC312D0FE2B7 /* Frameworks */ = { + D210D550F4EA176C3123ED886F8F87F5 /* Frameworks */ = { isa = PBXGroup; children = ( - 670597F16E31933FDD530ED5140D4EEF /* shared.framework */, + 578452D2E740E91742655AC8F1636D1F /* iOS */, ); name = Frameworks; sourceTree = ""; @@ -204,7 +200,7 @@ buildRules = ( ); dependencies = ( - 6D08CF75140F29DD6A6970543E494B09 /* PBXTargetDependency */, + 4A9525469F8E46F3883CC6599FCCFEDB /* PBXTargetDependency */, ); name = "Pods-iosApp"; productName = Pods_iosApp; @@ -217,8 +213,8 @@ BFDFE7DC352907FC980B868725387E98 /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 1300; - LastUpgradeCheck = 1300; + LastSwiftUpdateCheck = 1500; + LastUpgradeCheck = 1500; }; buildConfigurationList = 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */; compatibilityVersion = "Xcode 12.0"; @@ -291,18 +287,18 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - 6D08CF75140F29DD6A6970543E494B09 /* PBXTargetDependency */ = { + 4A9525469F8E46F3883CC6599FCCFEDB /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = shared; target = 8777C9F6889E59EFFD631D80AEE9048B /* shared */; - targetProxy = 3B3DD97234976EAE23DEF848B521F3BD /* PBXContainerItemProxy */; + targetProxy = C1E4F2F8101E860063100BCC8448BC3B /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ 02DDCCED053337F381DEBAFDEC6F354F /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 244FC2DAA936A0B07ACEFDC74E2D54B8 /* Pods-iosApp.release.xcconfig */; + baseConfigurationReference = 9FECCB5AFF87D121F15BA01E683C7932 /* Pods-iosApp.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; CLANG_ENABLE_OBJC_WEAK = NO; @@ -402,7 +398,7 @@ }; 8E1767EF3E210BC4C8DDA6D5A57FC3C6 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 215D65FA831B0E4F8AC0B3826A846FDA /* shared.debug.xcconfig */; + baseConfigurationReference = FD7FD28F3CCDF64520DA04A7CA0DDDC7 /* shared.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -485,7 +481,7 @@ }; AF088B6CD92A52AC4DCB62DEEC871231 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E462E23B3674BF94EAB1504D506F2803 /* Pods-iosApp.debug.xcconfig */; + baseConfigurationReference = 3F64BADB16C311E9D09DAD0116F684F4 /* Pods-iosApp.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; CLANG_ENABLE_OBJC_WEAK = NO; @@ -522,7 +518,7 @@ }; E387420B1FEC2EAB6CA01993F647E86F /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B7C6BC0A7177E1D2CCCA8D9834206E64 /* shared.release.xcconfig */; + baseConfigurationReference = 20D1F143BB7A4CD522117C279A4C293E /* shared.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; diff --git a/ios/Pods/Pods.xcodeproj/xcuserdata/joelkanyi.xcuserdatad/xcschemes/Pods-iosApp.xcscheme b/ios/Pods/Pods.xcodeproj/xcuserdata/joelkanyi.xcuserdatad/xcschemes/Pods-iosApp.xcscheme index 0673282..d58b01a 100644 --- a/ios/Pods/Pods.xcodeproj/xcuserdata/joelkanyi.xcuserdatad/xcschemes/Pods-iosApp.xcscheme +++ b/ios/Pods/Pods.xcodeproj/xcuserdata/joelkanyi.xcuserdatad/xcschemes/Pods-iosApp.xcscheme @@ -1,6 +1,6 @@ { binaries.withType { + @OptIn(ExperimentalKotlinGradlePluginApi::class) transitiveExport = true compilations.all { kotlinOptions.freeCompilerArgs += arrayOf("-linker-options", "-lsqlite3") @@ -70,87 +72,74 @@ kotlin { } sourceSets { - val commonMain by getting { - dependencies { - api(libs.koin.core) - api(libs.koin.compose) + commonMain.dependencies { + api(libs.koin.core) + api(libs.koin.compose) - implementation(compose.material3) - implementation(compose.material) - implementation(compose.materialIconsExtended) + implementation(compose.material3) + implementation(compose.material) + implementation(compose.materialIconsExtended) - @OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class) - implementation(compose.components.resources) + implementation(compose.components.resources) - implementation(libs.voyager.navigator) - implementation(libs.voyager.bottomSheetNavigator) - implementation(libs.voyager.transitions) - implementation(libs.voyager.tabNavigator) + implementation(libs.voyager.navigator) + implementation(libs.voyager.bottomSheetNavigator) + implementation(libs.voyager.transitions) + implementation(libs.voyager.tabNavigator) + implementation(libs.voyager.koin) - implementation(libs.kotlinX.serializationJson) + implementation(libs.kotlinX.serializationJson) - implementation(libs.material3.window.size.multiplatform) + implementation(libs.material3.window.size.multiplatform) - implementation(libs.sqlDelight.runtime) - implementation(libs.sqlDelight.coroutine) - implementation(libs.primitive.adapters) + implementation(libs.sqlDelight.runtime) + implementation(libs.coroutines.extensions) + implementation(libs.primitive.adapters) - api(libs.multiplatformSettings.noArg) - api(libs.multiplatformSettings.coroutines) + api(libs.multiplatformSettings.noArg) + api(libs.multiplatformSettings.coroutines) - api(libs.napier) + api(libs.napier) - implementation(libs.kotlinX.dateTime) - implementation(libs.koalaplot.core) + implementation(libs.kotlinX.dateTime) + implementation(libs.koalaplot.core) - implementation(libs.korau) - implementation(libs.stdlib) - } + implementation(libs.stdlib) } - val androidMain by getting { - dependencies { - implementation(libs.sqlDelight.android) - implementation(libs.accompanist.systemUIController) - implementation(libs.core) - implementation(libs.compose.activity) - } - } + androidMain.dependencies { + implementation(libs.android.driver) - val nativeMain by creating { - dependsOn(commonMain) + implementation(libs.accompanist.systemUIController) + implementation(libs.core) + implementation(libs.compose.activity) } - val jvmMain by getting { - dependencies { - implementation(libs.sqlDelight.jvm) - implementation(libs.kotlinx.coroutines.swing) + jvmMain.dependencies { + implementation(libs.sqlite.driver) - // Toaster for Windows - implementation(libs.toast4j) + implementation(libs.kotlinx.coroutines.swing) - // JNA for Linux - implementation("de.jangassen:jfa:1.2.0") { - // not excluding this leads to a strange error during build: - // > Could not find jna-5.13.0-jpms.jar (net.java.dev.jna:jna:5.13.0) - exclude(group = "net.java.dev.jna", module = "jna") - } - } - } + // Toaster for Windows + implementation(libs.toast4j) - val iosX64Main by getting - val iosArm64Main by getting - val iosSimulatorArm64Main by getting - val iosMain by creating { - dependsOn(commonMain) - iosArm64Main.dependsOn(this) - iosX64Main.dependsOn(this) - iosSimulatorArm64Main.dependsOn(this) - dependencies { - implementation(libs.sqlDelight.native) - implementation(libs.components.resources) + // JNA for Linux + implementation("de.jangassen:jfa:1.2.0") { + // not excluding this leads to a strange error during build: + // > Could not find jna-5.13.0-jpms.jar (net.java.dev.jna:jna:5.13.0) + exclude(group = "net.java.dev.jna", module = "jna") } + + // JNA for Windows + implementation(libs.jna) } + + iosMain.dependencies { + implementation(libs.native.driver) + + + @OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class) + implementation(compose.components.resources) } } } @@ -160,4 +149,4 @@ sqldelight { packageName.set("com.joelkanyi.focusbloom.database") } } -} +} \ No newline at end of file diff --git a/shared/shared.podspec b/shared/shared.podspec index a3151bc..0854e51 100644 --- a/shared/shared.podspec +++ b/shared/shared.podspec @@ -8,9 +8,24 @@ Pod::Spec.new do |spec| spec.summary = 'Some description for a Kotlin/Native module' spec.vendored_frameworks = 'build/cocoapods/framework/shared.framework' spec.libraries = 'c++' - spec.ios.deployment_target = '14.1' + spec.ios.deployment_target = '14.1' + if !Dir.exist?('build/cocoapods/framework/shared.framework') || Dir.empty?('build/cocoapods/framework/shared.framework') + raise " + + Kotlin framework 'shared' doesn't exist yet, so a proper Xcode project can't be generated. + 'pod install' should be executed after running ':generateDummyFramework' Gradle task: + + ./gradlew :shared:generateDummyFramework + + Alternatively, proper pod installation is performed during Gradle sync in the IDE (if Podfile location is set)" + end + + spec.xcconfig = { + 'ENABLE_USER_SCRIPT_SANDBOXING' => 'NO', + } + spec.pod_target_xcconfig = { 'KOTLIN_PROJECT_PATH' => ':shared', 'PRODUCT_MODULE_NAME' => 'shared', @@ -35,5 +50,5 @@ Pod::Spec.new do |spec| SCRIPT } ] - spec.resources = ['build/compose/ios/shared/compose-resources'] + spec.resources = ['build/compose/cocoapods/compose-resources'] end \ No newline at end of file diff --git a/shared/src/androidMain/kotlin/com/joelkanyi/focusbloom/di/Module.kt b/shared/src/androidMain/kotlin/com/joelkanyi/focusbloom/di/Module.kt index b167d04..973d123 100644 --- a/shared/src/androidMain/kotlin/com/joelkanyi/focusbloom/di/Module.kt +++ b/shared/src/androidMain/kotlin/com/joelkanyi/focusbloom/di/Module.kt @@ -18,11 +18,9 @@ package com.joelkanyi.focusbloom.di import com.joelkanyi.focusbloom.platform.DatabaseDriverFactory import com.joelkanyi.focusbloom.platform.MultiplatformSettingsWrapper import com.joelkanyi.focusbloom.platform.NotificationsManager -import com.russhwolf.settings.ExperimentalSettingsApi import org.koin.core.module.Module import org.koin.dsl.module -@OptIn(ExperimentalSettingsApi::class) actual fun platformModule(): Module = module { single { MultiplatformSettingsWrapper(context = get()).createSettings() } single { DatabaseDriverFactory(context = get()) } diff --git a/shared/src/androidMain/kotlin/com/joelkanyi/focusbloom/platform/Font.android.kt b/shared/src/androidMain/kotlin/com/joelkanyi/focusbloom/platform/Font.android.kt deleted file mode 100644 index 6585ad5..0000000 --- a/shared/src/androidMain/kotlin/com/joelkanyi/focusbloom/platform/Font.android.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2023 Joel Kanyi. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.joelkanyi.focusbloom.platform - -import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.font.Font -import androidx.compose.ui.text.font.FontStyle -import androidx.compose.ui.text.font.FontWeight - -@Composable -actual fun font(name: String, res: String, weight: FontWeight, style: FontStyle): Font { - val context = LocalContext.current - val id = context.resources.getIdentifier(res, "font", context.packageName) - return Font(id, weight, style) -} diff --git a/shared/src/androidMain/kotlin/com/joelkanyi/focusbloom/platform/NotificationsManager.android.kt b/shared/src/androidMain/kotlin/com/joelkanyi/focusbloom/platform/NotificationsManager.android.kt index 19cdefe..68d0133 100644 --- a/shared/src/androidMain/kotlin/com/joelkanyi/focusbloom/platform/NotificationsManager.android.kt +++ b/shared/src/androidMain/kotlin/com/joelkanyi/focusbloom/platform/NotificationsManager.android.kt @@ -28,14 +28,14 @@ import androidx.core.app.NotificationManagerCompat import com.joelkanyi.focusbloom.shared.R actual class NotificationsManager( - private val context: Context + private val context: Context, ) { private val notificationManager get() = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager @SuppressLint("MissingPermission") actual fun showNotification( title: String, - description: String + description: String, ) { // TODO: use a PendingIntent to open the app on notification click // Intent for the notification click @@ -45,7 +45,7 @@ actual class NotificationsManager( }*/ val sound = Uri.parse( - "${ContentResolver.SCHEME_ANDROID_RESOURCE}://${context.applicationContext.packageName}/${R.raw.alarm}" + "${ContentResolver.SCHEME_ANDROID_RESOURCE}://${context.applicationContext.packageName}/${R.raw.alarm}", ) println("sound: $sound") @@ -76,7 +76,7 @@ actual class NotificationsManager( channelId, channelName, - NotificationManager.IMPORTANCE_HIGH + NotificationManager.IMPORTANCE_HIGH, ).apply { description = channelDescription setSound(sound, Notification.AUDIO_ATTRIBUTES_DEFAULT) diff --git a/shared/src/commonMain/resources/add_filled.xml b/shared/src/commonMain/composeResources/drawable/add_filled.xml similarity index 100% rename from shared/src/commonMain/resources/add_filled.xml rename to shared/src/commonMain/composeResources/drawable/add_filled.xml diff --git a/shared/src/commonMain/resources/add_outlined.xml b/shared/src/commonMain/composeResources/drawable/add_outlined.xml similarity index 100% rename from shared/src/commonMain/resources/add_outlined.xml rename to shared/src/commonMain/composeResources/drawable/add_outlined.xml diff --git a/shared/src/commonMain/resources/calendar_filled.xml b/shared/src/commonMain/composeResources/drawable/calendar_filled.xml similarity index 100% rename from shared/src/commonMain/resources/calendar_filled.xml rename to shared/src/commonMain/composeResources/drawable/calendar_filled.xml diff --git a/shared/src/commonMain/resources/calendar_outlined.xml b/shared/src/commonMain/composeResources/drawable/calendar_outlined.xml similarity index 100% rename from shared/src/commonMain/resources/calendar_outlined.xml rename to shared/src/commonMain/composeResources/drawable/calendar_outlined.xml diff --git a/shared/src/commonMain/resources/end_time.xml b/shared/src/commonMain/composeResources/drawable/end_time.xml similarity index 100% rename from shared/src/commonMain/resources/end_time.xml rename to shared/src/commonMain/composeResources/drawable/end_time.xml diff --git a/shared/src/commonMain/resources/home_filled.xml b/shared/src/commonMain/composeResources/drawable/home_filled.xml similarity index 100% rename from shared/src/commonMain/resources/home_filled.xml rename to shared/src/commonMain/composeResources/drawable/home_filled.xml diff --git a/shared/src/commonMain/resources/home_outlined.xml b/shared/src/commonMain/composeResources/drawable/home_outlined.xml similarity index 100% rename from shared/src/commonMain/resources/home_outlined.xml rename to shared/src/commonMain/composeResources/drawable/home_outlined.xml diff --git a/shared/src/commonMain/resources/ic_complete.xml b/shared/src/commonMain/composeResources/drawable/ic_complete.xml similarity index 100% rename from shared/src/commonMain/resources/ic_complete.xml rename to shared/src/commonMain/composeResources/drawable/ic_complete.xml diff --git a/shared/src/commonMain/resources/ic_school.xml b/shared/src/commonMain/composeResources/drawable/ic_school.xml similarity index 100% rename from shared/src/commonMain/resources/ic_school.xml rename to shared/src/commonMain/composeResources/drawable/ic_school.xml diff --git a/shared/src/commonMain/resources/ic_time.xml b/shared/src/commonMain/composeResources/drawable/ic_time.xml similarity index 100% rename from shared/src/commonMain/resources/ic_time.xml rename to shared/src/commonMain/composeResources/drawable/ic_time.xml diff --git a/shared/src/commonMain/resources/ic_work.xml b/shared/src/commonMain/composeResources/drawable/ic_work.xml similarity index 100% rename from shared/src/commonMain/resources/ic_work.xml rename to shared/src/commonMain/composeResources/drawable/ic_work.xml diff --git a/shared/src/commonMain/resources/il_completed.xml b/shared/src/commonMain/composeResources/drawable/il_completed.xml similarity index 100% rename from shared/src/commonMain/resources/il_completed.xml rename to shared/src/commonMain/composeResources/drawable/il_completed.xml diff --git a/shared/src/commonMain/resources/il_empty.xml b/shared/src/commonMain/composeResources/drawable/il_empty.xml similarity index 100% rename from shared/src/commonMain/resources/il_empty.xml rename to shared/src/commonMain/composeResources/drawable/il_empty.xml diff --git a/shared/src/commonMain/resources/il_statistics.xml b/shared/src/commonMain/composeResources/drawable/il_statistics.xml similarity index 100% rename from shared/src/commonMain/resources/il_statistics.xml rename to shared/src/commonMain/composeResources/drawable/il_statistics.xml diff --git a/shared/src/commonMain/resources/il_tasks.xml b/shared/src/commonMain/composeResources/drawable/il_tasks.xml similarity index 100% rename from shared/src/commonMain/resources/il_tasks.xml rename to shared/src/commonMain/composeResources/drawable/il_tasks.xml diff --git a/shared/src/commonMain/resources/il_work_time.xml b/shared/src/commonMain/composeResources/drawable/il_work_time.xml similarity index 100% rename from shared/src/commonMain/resources/il_work_time.xml rename to shared/src/commonMain/composeResources/drawable/il_work_time.xml diff --git a/shared/src/commonMain/resources/other.xml b/shared/src/commonMain/composeResources/drawable/other.xml similarity index 100% rename from shared/src/commonMain/resources/other.xml rename to shared/src/commonMain/composeResources/drawable/other.xml diff --git a/shared/src/commonMain/resources/personal.xml b/shared/src/commonMain/composeResources/drawable/personal.xml similarity index 100% rename from shared/src/commonMain/resources/personal.xml rename to shared/src/commonMain/composeResources/drawable/personal.xml diff --git a/shared/src/commonMain/resources/redo.xml b/shared/src/commonMain/composeResources/drawable/redo.xml similarity index 100% rename from shared/src/commonMain/resources/redo.xml rename to shared/src/commonMain/composeResources/drawable/redo.xml diff --git a/shared/src/commonMain/resources/settings_filled.xml b/shared/src/commonMain/composeResources/drawable/settings_filled.xml similarity index 100% rename from shared/src/commonMain/resources/settings_filled.xml rename to shared/src/commonMain/composeResources/drawable/settings_filled.xml diff --git a/shared/src/commonMain/resources/settings_outlined.xml b/shared/src/commonMain/composeResources/drawable/settings_outlined.xml similarity index 100% rename from shared/src/commonMain/resources/settings_outlined.xml rename to shared/src/commonMain/composeResources/drawable/settings_outlined.xml diff --git a/shared/src/commonMain/resources/start_time.xml b/shared/src/commonMain/composeResources/drawable/start_time.xml similarity index 100% rename from shared/src/commonMain/resources/start_time.xml rename to shared/src/commonMain/composeResources/drawable/start_time.xml diff --git a/shared/src/commonMain/resources/statistics_filled.xml b/shared/src/commonMain/composeResources/drawable/statistics_filled.xml similarity index 100% rename from shared/src/commonMain/resources/statistics_filled.xml rename to shared/src/commonMain/composeResources/drawable/statistics_filled.xml diff --git a/shared/src/commonMain/resources/statistics_outlined.xml b/shared/src/commonMain/composeResources/drawable/statistics_outlined.xml similarity index 100% rename from shared/src/commonMain/resources/statistics_outlined.xml rename to shared/src/commonMain/composeResources/drawable/statistics_outlined.xml diff --git a/shared/src/commonMain/resources/study.xml b/shared/src/commonMain/composeResources/drawable/study.xml similarity index 100% rename from shared/src/commonMain/resources/study.xml rename to shared/src/commonMain/composeResources/drawable/study.xml diff --git a/shared/src/commonMain/resources/work.xml b/shared/src/commonMain/composeResources/drawable/work.xml similarity index 100% rename from shared/src/commonMain/resources/work.xml rename to shared/src/commonMain/composeResources/drawable/work.xml diff --git a/shared/src/commonMain/resources/font/montserrat_black.ttf b/shared/src/commonMain/composeResources/font/montserrat_black.ttf similarity index 100% rename from shared/src/commonMain/resources/font/montserrat_black.ttf rename to shared/src/commonMain/composeResources/font/montserrat_black.ttf diff --git a/shared/src/commonMain/resources/font/montserrat_bold.ttf b/shared/src/commonMain/composeResources/font/montserrat_bold.ttf similarity index 100% rename from shared/src/commonMain/resources/font/montserrat_bold.ttf rename to shared/src/commonMain/composeResources/font/montserrat_bold.ttf diff --git a/shared/src/commonMain/resources/font/montserrat_extrabold.ttf b/shared/src/commonMain/composeResources/font/montserrat_extrabold.ttf similarity index 100% rename from shared/src/commonMain/resources/font/montserrat_extrabold.ttf rename to shared/src/commonMain/composeResources/font/montserrat_extrabold.ttf diff --git a/shared/src/commonMain/resources/font/montserrat_extralight.ttf b/shared/src/commonMain/composeResources/font/montserrat_extralight.ttf similarity index 100% rename from shared/src/commonMain/resources/font/montserrat_extralight.ttf rename to shared/src/commonMain/composeResources/font/montserrat_extralight.ttf diff --git a/shared/src/commonMain/resources/font/montserrat_light.ttf b/shared/src/commonMain/composeResources/font/montserrat_light.ttf similarity index 100% rename from shared/src/commonMain/resources/font/montserrat_light.ttf rename to shared/src/commonMain/composeResources/font/montserrat_light.ttf diff --git a/shared/src/commonMain/resources/font/montserrat_medium.ttf b/shared/src/commonMain/composeResources/font/montserrat_medium.ttf similarity index 100% rename from shared/src/commonMain/resources/font/montserrat_medium.ttf rename to shared/src/commonMain/composeResources/font/montserrat_medium.ttf diff --git a/shared/src/commonMain/resources/font/montserrat_regular.ttf b/shared/src/commonMain/composeResources/font/montserrat_regular.ttf similarity index 100% rename from shared/src/commonMain/resources/font/montserrat_regular.ttf rename to shared/src/commonMain/composeResources/font/montserrat_regular.ttf diff --git a/shared/src/commonMain/resources/font/montserrat_semi_bold.ttf b/shared/src/commonMain/composeResources/font/montserrat_semi_bold.ttf similarity index 100% rename from shared/src/commonMain/resources/font/montserrat_semi_bold.ttf rename to shared/src/commonMain/composeResources/font/montserrat_semi_bold.ttf diff --git a/shared/src/commonMain/resources/font/montserrat_thin.ttf b/shared/src/commonMain/composeResources/font/montserrat_thin.ttf similarity index 100% rename from shared/src/commonMain/resources/font/montserrat_thin.ttf rename to shared/src/commonMain/composeResources/font/montserrat_thin.ttf diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/FocusBloomApp.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/FocusBloomApp.kt index bf8a8b4..f0dec41 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/FocusBloomApp.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/FocusBloomApp.kt @@ -21,56 +21,56 @@ import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier +import cafe.adriel.voyager.navigator.CurrentScreen import cafe.adriel.voyager.navigator.Navigator -import cafe.adriel.voyager.transitions.SlideTransition import com.joelkanyi.focusbloom.core.presentation.theme.FocusBloomTheme -import com.joelkanyi.focusbloom.core.utils.ProvideAppNavigator import com.joelkanyi.focusbloom.feature.onboarding.OnboardingScreen import com.joelkanyi.focusbloom.main.MainScreen import com.joelkanyi.focusbloom.main.MainViewModel import com.joelkanyi.focusbloom.main.OnBoardingState import com.joelkanyi.focusbloom.platform.StatusBarColors -import org.koin.compose.rememberKoinInject +import org.koin.compose.KoinContext +import org.koin.compose.koinInject @Composable -fun FocusBloomApp() { - val mainViewModel = rememberKoinInject() +fun FocusBloomApp( + mainViewModel: MainViewModel = koinInject(), +) { val darkTheme = when (mainViewModel.appTheme.collectAsState().value) { 1 -> true else -> false } val onBoardingCompleted = mainViewModel.onBoardingCompleted.collectAsState().value - FocusBloomTheme( - useDarkTheme = darkTheme - ) { - StatusBarColors( - statusBarColor = MaterialTheme.colorScheme.background, - navBarColor = MaterialTheme.colorScheme.background - ) - when (onBoardingCompleted) { - is OnBoardingState.Success -> { - Surface( - modifier = Modifier.fillMaxSize(), - color = MaterialTheme.colorScheme.background - ) { - Navigator( - screen = if (onBoardingCompleted.completed) { - MainScreen() - } else { - OnboardingScreen() - }, - content = { navigator -> - ProvideAppNavigator( - navigator = navigator, - content = { SlideTransition(navigator = navigator) } - ) - } - ) + KoinContext { + FocusBloomTheme( + useDarkTheme = darkTheme, + ) { + StatusBarColors( + statusBarColor = MaterialTheme.colorScheme.background, + navBarColor = MaterialTheme.colorScheme.background, + ) + when (onBoardingCompleted) { + is OnBoardingState.Success -> { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background, + ) { + Navigator( + screen = if (onBoardingCompleted.completed) { + MainScreen() + } else { + OnboardingScreen() + }, + content = { + CurrentScreen() + }, + ) + } } - } - else -> {} + else -> {} + } } } } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/data/local/setting/PreferenceManager.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/data/local/setting/PreferenceManager.kt index 47dec2e..7161f67 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/data/local/setting/PreferenceManager.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/data/local/setting/PreferenceManager.kt @@ -38,7 +38,7 @@ class PreferenceManager constructor(private val settings: Settings) { @OptIn(ExperimentalSettingsApi::class) fun getNonFlowString(key: String) = observableSettings.getString( key = key, - defaultValue = "" + defaultValue = "", ) @OptIn(ExperimentalCoroutinesApi::class, ExperimentalSettingsApi::class) @@ -77,7 +77,7 @@ class PreferenceManager constructor(private val settings: Settings) { fun getBoolean(key: String): Flow { return observableSettings.getBooleanFlow( key = key, - defaultValue = false + defaultValue = false, ) } @@ -90,11 +90,10 @@ class PreferenceManager constructor(private val settings: Settings) { fun getLong(key: Any): Flow { return observableSettings.getLongFlow( key = key.toString(), - defaultValue = 0 + defaultValue = 0, ) } - @OptIn(ExperimentalSettingsApi::class) fun setLong(key: String, value: Long) { observableSettings.set(key = key, value = value) } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/data/mapper/Mappers.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/data/mapper/Mappers.kt index b5a0e9b..6ed9b12 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/data/mapper/Mappers.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/data/mapper/Mappers.kt @@ -36,7 +36,7 @@ fun TaskEntity.toTask() = Task( consumedLongBreakTime = consumedLongBreakTime, inProgressTask = inProgressTask, currentCycle = currentCycle, - active = active + active = active, ) fun Task.toTaskEntity() = TaskEntity( @@ -55,5 +55,5 @@ fun Task.toTaskEntity() = TaskEntity( consumedLongBreakTime = consumedLongBreakTime, inProgressTask = inProgressTask, currentCycle = currentCycle, - active = active + active = active, ) diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/data/repository/settings/SettingsRepositoryImpl.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/data/repository/settings/SettingsRepositoryImpl.kt index f92d328..1b70482 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/data/repository/settings/SettingsRepositoryImpl.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/data/repository/settings/SettingsRepositoryImpl.kt @@ -20,7 +20,7 @@ import com.joelkanyi.focusbloom.core.domain.repository.settings.SettingsReposito import kotlinx.coroutines.flow.Flow class SettingsRepositoryImpl( - private val preferenceManager: PreferenceManager + private val preferenceManager: PreferenceManager, ) : SettingsRepository { override suspend fun saveAppTheme(theme: Int) { preferenceManager.setInt(key = PreferenceManager.APP_THEME, value = theme) diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/data/repository/tasks/TasksRepositoryImpl.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/data/repository/tasks/TasksRepositoryImpl.kt index d1e9716..64e22ab 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/data/repository/tasks/TasksRepositoryImpl.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/data/repository/tasks/TasksRepositoryImpl.kt @@ -27,7 +27,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map class TasksRepositoryImpl( - bloomDatabase: BloomDatabase + bloomDatabase: BloomDatabase, ) : TasksRepository { private val dbQuery = bloomDatabase.taskQueries override fun getTasks(): Flow> { @@ -69,7 +69,7 @@ class TasksRepositoryImpl( consumedLongBreakTime = it.consumedLongBreakTime, inProgressTask = it.inProgressTask, currentCycle = it.currentCycle, - active = it.active + active = it.active, ) } } @@ -86,7 +86,7 @@ class TasksRepositoryImpl( date = it.date, focusSessions = it.focusSessions, completed = it.completed, - active = it.active + active = it.active, ) } } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/data/utils/DbHelpers.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/data/utils/DbHelpers.kt index 3b20f50..64ad8a8 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/data/utils/DbHelpers.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/data/utils/DbHelpers.kt @@ -26,7 +26,7 @@ import kotlin.jvm.JvmOverloads @JvmOverloads fun Flow>.mapToOne( - context: CoroutineContext = Dispatchers.Default + context: CoroutineContext = Dispatchers.Default, ): Flow = map { withContext(context) { it.executeAsOne() @@ -36,7 +36,7 @@ fun Flow>.mapToOne( @JvmOverloads fun Flow>.mapToOneOrDefault( defaultValue: T, - context: CoroutineContext = Dispatchers.Default + context: CoroutineContext = Dispatchers.Default, ): Flow = map { withContext(context) { it.executeAsOneOrNull() ?: defaultValue @@ -45,7 +45,7 @@ fun Flow>.mapToOneOrDefault( @JvmOverloads fun Flow>.mapToOneOrNull( - context: CoroutineContext = Dispatchers.Default + context: CoroutineContext = Dispatchers.Default, ): Flow = map { withContext(context) { it.executeAsOneOrNull() @@ -54,7 +54,7 @@ fun Flow>.mapToOneOrNull( @JvmOverloads fun Flow>.mapToOneNotNull( - context: CoroutineContext = Dispatchers.Default + context: CoroutineContext = Dispatchers.Default, ): Flow = mapNotNull { withContext(context) { it.executeAsOneOrNull() @@ -63,7 +63,7 @@ fun Flow>.mapToOneNotNull( @JvmOverloads fun Flow>.mapToList( - context: CoroutineContext = Dispatchers.Default + context: CoroutineContext = Dispatchers.Default, ): Flow> = map { withContext(context) { it.executeAsList() diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/domain/model/Task.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/domain/model/Task.kt index cbb7eaa..92d3168 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/domain/model/Task.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/domain/model/Task.kt @@ -33,5 +33,5 @@ data class Task( val consumedShortBreakTime: Long, val consumedLongBreakTime: Long, val inProgressTask: Boolean, - val active: Boolean + val active: Boolean, ) diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/domain/model/TaskType.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/domain/model/TaskType.kt index 5376696..1fe93de 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/domain/model/TaskType.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/domain/model/TaskType.kt @@ -15,35 +15,44 @@ */ package com.joelkanyi.focusbloom.core.domain.model -data class TaskType( +import focusbloom.shared.generated.resources.Res +import focusbloom.shared.generated.resources.other +import focusbloom.shared.generated.resources.personal +import focusbloom.shared.generated.resources.study +import focusbloom.shared.generated.resources.work +import org.jetbrains.compose.resources.DrawableResource +import org.jetbrains.compose.resources.ExperimentalResourceApi + +data class TaskType @OptIn(ExperimentalResourceApi::class) constructor( val name: String, - val icon: String, - val color: Long + val icon: DrawableResource, + val color: Long, ) { override fun toString(): String { return name } } +@OptIn(ExperimentalResourceApi::class) val taskTypes = listOf( TaskType( name = "Work", - icon = "work.xml", - color = 0xFF3375fd + icon = Res.drawable.work, + color = 0xFF3375fd, ), TaskType( name = "Study", - icon = "study.xml", - color = 0xFFff686d + icon = Res.drawable.study, + color = 0xFFff686d, ), TaskType( name = "Personal", - icon = "personal.xml", - color = 0xFF24c469 + icon = Res.drawable.personal, + color = 0xFF24c469, ), TaskType( name = "Other", - icon = "other.xml", - color = 0xFF734efe - ) + icon = Res.drawable.other, + color = 0xFF734efe, + ), ) diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/domain/model/TextFieldState.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/domain/model/TextFieldState.kt index 73f1114..f231d8f 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/domain/model/TextFieldState.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/domain/model/TextFieldState.kt @@ -17,5 +17,5 @@ package com.joelkanyi.focusbloom.core.domain.model data class TextFieldState( val text: String = "", - val error: String? = null + val error: String? = null, ) diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomButton.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomButton.kt index b5e473b..0ef329a 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomButton.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomButton.kt @@ -24,14 +24,20 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape @Composable -fun BloomButton(modifier: Modifier = Modifier, onClick: () -> Unit, shape: Shape = MaterialTheme.shapes.medium, backgroundColor: Color = MaterialTheme.colorScheme.primary, content: @Composable () -> Unit) { +fun BloomButton( + modifier: Modifier = Modifier, + onClick: () -> Unit, + shape: Shape = MaterialTheme.shapes.medium, + backgroundColor: Color = MaterialTheme.colorScheme.primary, + content: @Composable () -> Unit, +) { Button( modifier = modifier, onClick = onClick, shape = shape, colors = ButtonDefaults.buttonColors( - containerColor = backgroundColor - ) + containerColor = backgroundColor, + ), ) { content() } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomCircleButton.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomCircleButton.kt index b5ff5e0..87a530c 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomCircleButton.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomCircleButton.kt @@ -28,7 +28,12 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp @Composable -fun BloomCircleButton(modifier: Modifier = Modifier, icon: @Composable () -> Unit, onClick: () -> Unit, color: Color) { +fun BloomCircleButton( + modifier: Modifier = Modifier, + icon: @Composable () -> Unit, + onClick: () -> Unit, + color: Color, +) { Box( modifier = modifier .size(48.dp) @@ -37,7 +42,7 @@ fun BloomCircleButton(modifier: Modifier = Modifier, icon: @Composable () -> Uni .clickable { onClick() }, - contentAlignment = Alignment.Center + contentAlignment = Alignment.Center, ) { icon() } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomDropDown.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomDropDown.kt index e0c2601..14b4fd7 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomDropDown.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomDropDown.kt @@ -57,7 +57,7 @@ fun BloomDropDown( selectedOption: TextFieldState, onOptionSelected: (T) -> Unit, textStyle: TextStyle = MaterialTheme.typography.titleSmall, - shape: CornerBasedShape = MaterialTheme.shapes.small + shape: CornerBasedShape = MaterialTheme.shapes.small, ) { var expanded by remember { mutableStateOf(false) } Column { @@ -76,7 +76,7 @@ fun BloomDropDown( } else { MaterialTheme.colorScheme.onBackground.copy(alpha = .4f) }, - shape = shape + shape = shape, ) .clip(shape) .clickable { @@ -84,21 +84,21 @@ fun BloomDropDown( expanded = !expanded } }, - contentAlignment = Alignment.CenterStart + contentAlignment = Alignment.CenterStart, ) { Row( modifier = Modifier .fillMaxWidth() .padding( vertical = 8.dp, - horizontal = 12.dp + horizontal = 12.dp, ), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween + horizontalArrangement = Arrangement.SpaceBetween, ) { Text( text = selectedOption.text, - style = textStyle + style = textStyle, ) if (enabled) { Icon( @@ -110,26 +110,26 @@ fun BloomDropDown( } else { Icons.Filled.ArrowDropDown }, - contentDescription = null + contentDescription = null, ) } } DropdownMenu( expanded = expanded, - onDismissRequest = { expanded = false } + onDismissRequest = { expanded = false }, ) { options.forEach { selectionOption -> DropdownMenuItem( text = { Text( text = selectionOption.toString(), - style = MaterialTheme.typography.labelLarge + style = MaterialTheme.typography.labelLarge, ) }, onClick = { onOptionSelected(selectionOption) expanded = false - } + }, ) } } @@ -140,7 +140,7 @@ fun BloomDropDown( text = selectedOption.error, style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.error, - textAlign = TextAlign.End + textAlign = TextAlign.End, ) } } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomIncrementer.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomIncrementer.kt index 0c6e9ed..8f9f04a 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomIncrementer.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomIncrementer.kt @@ -31,29 +31,34 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp @Composable -fun BloomIncrementer(modifier: Modifier = Modifier, onClickRemove: () -> Unit, onClickAdd: () -> Unit, currentValue: Int) { +fun BloomIncrementer( + modifier: Modifier = Modifier, + onClickRemove: () -> Unit, + onClickAdd: () -> Unit, + currentValue: Int, +) { Row( modifier = modifier, verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center + horizontalArrangement = Arrangement.Center, ) { BloomCircleButton( icon = { Icon( imageVector = Icons.Filled.Remove, contentDescription = null, - tint = MaterialTheme.colorScheme.onPrimary + tint = MaterialTheme.colorScheme.onPrimary, ) }, onClick = onClickRemove, - color = MaterialTheme.colorScheme.primary + color = MaterialTheme.colorScheme.primary, ) Spacer(modifier = Modifier.width(12.dp)) Text( text = "$currentValue", - style = MaterialTheme.typography.titleLarge + style = MaterialTheme.typography.titleLarge, ) Spacer(modifier = Modifier.width(12.dp)) @@ -63,11 +68,11 @@ fun BloomIncrementer(modifier: Modifier = Modifier, onClickRemove: () -> Unit, o Icon( imageVector = Icons.Filled.Add, contentDescription = null, - tint = MaterialTheme.colorScheme.onPrimary + tint = MaterialTheme.colorScheme.onPrimary, ) }, onClick = onClickAdd, - color = MaterialTheme.colorScheme.primary + color = MaterialTheme.colorScheme.primary, ) } } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomInputTextField.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomInputTextField.kt index d670b75..adfc1ea 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomInputTextField.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomInputTextField.kt @@ -57,10 +57,10 @@ fun BloomInputTextField( onValueChange: (String) -> Unit, textStyle: TextStyle = MaterialTheme.typography.titleSmall, shape: CornerBasedShape = MaterialTheme.shapes.small, - keyboardOptions: KeyboardOptions = KeyboardOptions.Default + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, ) { Column( - modifier = modifier + modifier = modifier, ) { if (label != null) { label() @@ -79,21 +79,29 @@ fun BloomInputTextField( maxLines = maxLines, singleLine = maxLines == 1, keyboardOptions = keyboardOptions, - readOnly = !editable + readOnly = !editable, ) if (!value.error.isNullOrEmpty()) { Text( text = value.error, style = MaterialTheme.typography.bodyMedium.copy( - color = MaterialTheme.colorScheme.error - ) + color = MaterialTheme.colorScheme.error, + ), ) } } } @Composable -fun BloomDateBoxField(modifier: Modifier = Modifier, label: (@Composable () -> Unit)? = null, enabled: Boolean = true, currentTextState: TextFieldState, onClick: () -> Unit, textStyle: TextStyle = MaterialTheme.typography.titleSmall, shape: CornerBasedShape = MaterialTheme.shapes.small) { +fun BloomDateBoxField( + modifier: Modifier = Modifier, + label: (@Composable () -> Unit)? = null, + enabled: Boolean = true, + currentTextState: TextFieldState, + onClick: () -> Unit, + textStyle: TextStyle = MaterialTheme.typography.titleSmall, + shape: CornerBasedShape = MaterialTheme.shapes.small, +) { Column { if (label != null) { label() @@ -109,27 +117,27 @@ fun BloomDateBoxField(modifier: Modifier = Modifier, label: (@Composable () -> U } else { MaterialTheme.colorScheme.onBackground.copy(alpha = .4f) }, - shape = shape + shape = shape, ) .clip(shape) .clickable { onClick() }, - contentAlignment = Alignment.CenterStart + contentAlignment = Alignment.CenterStart, ) { Row( modifier = Modifier .fillMaxWidth() .padding( vertical = 8.dp, - horizontal = 12.dp + horizontal = 12.dp, ), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween + horizontalArrangement = Arrangement.SpaceBetween, ) { Text( text = currentTextState.text, - style = textStyle + style = textStyle, ) if (enabled) { Icon( @@ -137,7 +145,7 @@ fun BloomDateBoxField(modifier: Modifier = Modifier, label: (@Composable () -> U .padding(start = 8.dp) .size(24.dp), imageVector = Icons.Default.DateRange, - contentDescription = null + contentDescription = null, ) } } @@ -148,7 +156,7 @@ fun BloomDateBoxField(modifier: Modifier = Modifier, label: (@Composable () -> U text = currentTextState.error, style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.error, - textAlign = TextAlign.End + textAlign = TextAlign.End, ) } } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomNavigationRailBar.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomNavigationRailBar.kt index 1d73cbc..ecddbac 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomNavigationRailBar.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomNavigationRailBar.kt @@ -31,7 +31,11 @@ import cafe.adriel.voyager.navigator.tab.TabNavigator import com.joelkanyi.focusbloom.core.presentation.utils.FilledIcon @Composable -fun BloomNavigationRailBar(modifier: Modifier = Modifier, tabNavigator: TabNavigator, navRailItems: List) { +fun BloomNavigationRailBar( + modifier: Modifier = Modifier, + tabNavigator: TabNavigator, + navRailItems: List, +) { NavigationRail( modifier = modifier.fillMaxHeight().alpha(0.95F), containerColor = MaterialTheme.colorScheme.surface, @@ -43,7 +47,7 @@ fun BloomNavigationRailBar(modifier: Modifier = Modifier, tabNavigator: TabNavig contentDescription = "Logo", )*/ }, - contentColor = MaterialTheme.colorScheme.onSurface + contentColor = MaterialTheme.colorScheme.onSurface, ) { navRailItems.forEach { item -> val isSelected = tabNavigator.current == item @@ -57,14 +61,14 @@ fun BloomNavigationRailBar(modifier: Modifier = Modifier, tabNavigator: TabNavig } else { it }, - contentDescription = item.options.title + contentDescription = item.options.title, ) } }, label = { Text(text = item.options.title) }, alwaysShowLabel = true, selected = tabNavigator.current == item, - onClick = { tabNavigator.current = item } + onClick = { tabNavigator.current = item }, ) } } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomTab.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomTab.kt index 79375f8..9552c38 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomTab.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomTab.kt @@ -24,6 +24,12 @@ import com.joelkanyi.focusbloom.feature.calendar.CalendarScreen import com.joelkanyi.focusbloom.feature.home.HomeScreen import com.joelkanyi.focusbloom.feature.settings.SettingsScreen import com.joelkanyi.focusbloom.feature.statistics.StatisticsScreen +import focusbloom.shared.generated.resources.Res +import focusbloom.shared.generated.resources.add_outlined +import focusbloom.shared.generated.resources.calendar_outlined +import focusbloom.shared.generated.resources.home_outlined +import focusbloom.shared.generated.resources.settings_outlined +import focusbloom.shared.generated.resources.statistics_outlined import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.painterResource @@ -34,13 +40,13 @@ internal sealed class BloomTab { @Composable get() { val title = "Home" - val icon = painterResource("home_outlined.xml") + val icon = painterResource(Res.drawable.home_outlined) return remember { TabOptions( index = 0u, title = title, - icon = icon + icon = icon, ) } } @@ -57,13 +63,13 @@ internal sealed class BloomTab { @Composable get() { val title = "Calendar" - val icon = painterResource("calendar_outlined.xml") + val icon = painterResource(Res.drawable.calendar_outlined) return remember { TabOptions( index = 1u, title = title, - icon = icon + icon = icon, ) } } @@ -80,13 +86,13 @@ internal sealed class BloomTab { @Composable get() { val title = "Statistics" - val icon = painterResource("statistics_outlined.xml") + val icon = painterResource(Res.drawable.statistics_outlined) return remember { TabOptions( index = 2u, title = title, - icon = icon + icon = icon, ) } } @@ -103,13 +109,13 @@ internal sealed class BloomTab { @Composable get() { val title = "Settings" - val icon = painterResource("settings_outlined.xml") + val icon = painterResource(Res.drawable.settings_outlined) return remember { TabOptions( index = 3u, title = title, - icon = icon + icon = icon, ) } } @@ -121,20 +127,20 @@ internal sealed class BloomTab { } internal data class AddTaskTab( - val taskId: Int? = null + val taskId: Int? = null, ) : Tab { @OptIn(ExperimentalResourceApi::class) override val options: TabOptions @Composable get() { val title = "Add Task" - val icon = painterResource("add_outlined.xml") + val icon = painterResource(Res.drawable.add_outlined) return remember { TabOptions( index = 4u, title = title, - icon = icon + icon = icon, ) } } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomTimerControls.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomTimerControls.kt index 3538aa5..7176790 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomTimerControls.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomTimerControls.kt @@ -33,18 +33,24 @@ import androidx.compose.ui.unit.dp import com.joelkanyi.focusbloom.feature.taskprogress.TimerState @Composable -fun BloomTimerControls(modifier: Modifier = Modifier, state: TimerState, onClickReset: () -> Unit, onClickNext: () -> Unit, onClickAction: (state: TimerState) -> Unit) { +fun BloomTimerControls( + modifier: Modifier = Modifier, + state: TimerState, + onClickReset: () -> Unit, + onClickNext: () -> Unit, + onClickAction: (state: TimerState) -> Unit, +) { Row( modifier = modifier, verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceAround + horizontalArrangement = Arrangement.SpaceAround, ) { IconButton(onClick = onClickReset) { Icon( modifier = Modifier.size(120.dp), imageVector = Icons.Filled.Replay, contentDescription = "Reset Timer", - tint = MaterialTheme.colorScheme.onPrimary + tint = MaterialTheme.colorScheme.onPrimary, ) } @@ -57,24 +63,27 @@ fun BloomTimerControls(modifier: Modifier = Modifier, state: TimerState, onClick TimerState.Paused -> { Icons.Filled.PlayArrow } + TimerState.Ticking -> { Icons.Filled.Pause } + TimerState.Finished -> { Icons.Filled.Replay } + else -> { Icons.Filled.PlayArrow } }, contentDescription = null, - tint = MaterialTheme.colorScheme.onPrimary + tint = MaterialTheme.colorScheme.onPrimary, ) }, onClick = { onClickAction(state) }, - color = MaterialTheme.colorScheme.primary + color = MaterialTheme.colorScheme.primary, ) IconButton(onClick = onClickNext) { @@ -82,7 +91,7 @@ fun BloomTimerControls(modifier: Modifier = Modifier, state: TimerState, onClick modifier = Modifier.size(120.dp), imageVector = Icons.Filled.SkipNext, contentDescription = "Next Timer", - tint = MaterialTheme.colorScheme.onPrimary + tint = MaterialTheme.colorScheme.onPrimary, ) } } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomTopAppBar.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomTopAppBar.kt index 4d9a727..dada451 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomTopAppBar.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/BloomTopAppBar.kt @@ -31,9 +31,9 @@ fun BloomTopAppBar( actions: (@Composable () -> Unit)? = null, navigationIcon: (@Composable () -> Unit)? = null, colors: TopAppBarColors = TopAppBarDefaults.topAppBarColors( - containerColor = MaterialTheme.colorScheme.background + containerColor = MaterialTheme.colorScheme.background, ), - title: @Composable () -> Unit = {} + title: @Composable () -> Unit = {}, ) { TopAppBar( modifier = modifier, @@ -50,6 +50,6 @@ fun BloomTopAppBar( } } }, - colors = colors + colors = colors, ) } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/TaskCard.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/TaskCard.kt index d98dec0..92dfd25 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/TaskCard.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/TaskCard.kt @@ -32,7 +32,6 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material3.Card -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -55,20 +54,32 @@ import com.joelkanyi.focusbloom.core.utils.calculateEndTime import com.joelkanyi.focusbloom.core.utils.durationInMinutes import com.joelkanyi.focusbloom.core.utils.prettyFormat import com.joelkanyi.focusbloom.core.utils.prettyTimeDifference +import focusbloom.shared.generated.resources.Res +import focusbloom.shared.generated.resources.ic_complete import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.painterResource -@OptIn(ExperimentalMaterial3Api::class, ExperimentalResourceApi::class) +@OptIn(ExperimentalResourceApi::class) @Composable -fun TaskCard(task: Task, focusSessions: Int, sessionTime: Int, shortBreakTime: Int, longBreakTime: Int, hourFormat: Int, onClick: (task: Task) -> Unit, onShowTaskOption: (task: Task) -> Unit, type: String) { +fun TaskCard( + task: Task, + focusSessions: Int, + sessionTime: Int, + shortBreakTime: Int, + longBreakTime: Int, + hourFormat: Int, + onClick: (task: Task) -> Unit, + onShowTaskOption: (task: Task) -> Unit, + type: String, +) { val end by remember { mutableStateOf( task.start.calculateEndTime( focusSessions = focusSessions, sessionTime = sessionTime, shortBreakTime = shortBreakTime, - longBreakTime = longBreakTime - ) + longBreakTime = longBreakTime, + ), ) } Card( @@ -76,38 +87,38 @@ fun TaskCard(task: Task, focusSessions: Int, sessionTime: Int, shortBreakTime: I .fillMaxWidth(), onClick = { onClick(task) - } + }, ) { Column( modifier = Modifier .padding(12.dp), - verticalArrangement = Arrangement.spacedBy(8.dp) + verticalArrangement = Arrangement.spacedBy(8.dp), ) { Row( modifier = Modifier .fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween + horizontalArrangement = Arrangement.SpaceBetween, ) { Column( modifier = Modifier .fillMaxWidth(.85f), - verticalArrangement = Arrangement.spacedBy(4.dp) + verticalArrangement = Arrangement.spacedBy(4.dp), ) { Text( text = task.name, style = MaterialTheme.typography.titleSmall.copy( fontWeight = FontWeight.SemiBold, - fontSize = 16.sp + fontSize = 16.sp, ), maxLines = 2, - overflow = TextOverflow.Ellipsis + overflow = TextOverflow.Ellipsis, ) if (task.description != null) { Text( text = task.description, style = MaterialTheme.typography.bodyMedium, maxLines = 3, - overflow = TextOverflow.Ellipsis + overflow = TextOverflow.Ellipsis, ) } } @@ -117,7 +128,7 @@ fun TaskCard(task: Task, focusSessions: Int, sessionTime: Int, shortBreakTime: I onShowTaskOption(task) }, imageVector = Icons.Filled.MoreVert, - contentDescription = "Task Options" + contentDescription = "Task Options", ) } @@ -125,7 +136,7 @@ fun TaskCard(task: Task, focusSessions: Int, sessionTime: Int, shortBreakTime: I modifier = Modifier .fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Column { Text( @@ -133,13 +144,13 @@ fun TaskCard(task: Task, focusSessions: Int, sessionTime: Int, shortBreakTime: I withStyle( style = SpanStyle( fontWeight = FontWeight.SemiBold, - fontSize = 18.sp - ) + fontSize = 18.sp, + ), ) { append("${task.currentCycle}") } append("/${task.focusSessions}") - } + }, ) Spacer(modifier = Modifier.height(4.dp)) Text( @@ -148,27 +159,27 @@ fun TaskCard(task: Task, focusSessions: Int, sessionTime: Int, shortBreakTime: I focusSessions = focusSessions, sessionTime = sessionTime, shortBreakTime = shortBreakTime, - longBreakTime = longBreakTime + longBreakTime = longBreakTime, ) } minutes", - style = MaterialTheme.typography.bodySmall + style = MaterialTheme.typography.bodySmall, ) Spacer(modifier = Modifier.height(4.dp)) Text( text = prettyTimeDifference( start = task.start, end = end, - timeFormat = hourFormat + timeFormat = hourFormat, ), - style = MaterialTheme.typography.bodySmall + style = MaterialTheme.typography.bodySmall, ) if (type == "overdue") { Spacer(modifier = Modifier.height(4.dp)) Text( text = task.date.date.prettyFormat(), style = MaterialTheme.typography.bodySmall.copy( - fontWeight = FontWeight.SemiBold - ) + fontWeight = FontWeight.SemiBold, + ), ) } } @@ -176,8 +187,9 @@ fun TaskCard(task: Task, focusSessions: Int, sessionTime: Int, shortBreakTime: I Image( modifier = Modifier .size(48.dp), - painter = painterResource("ic_complete.xml"), - contentDescription = "Task Options" + painter = painterResource( + Res.drawable.ic_complete), + contentDescription = "Task Options", ) } else { Box( @@ -185,12 +197,12 @@ fun TaskCard(task: Task, focusSessions: Int, sessionTime: Int, shortBreakTime: I .size(48.dp) .clip(CircleShape) .background(MaterialTheme.colorScheme.primary), - contentAlignment = Alignment.Center + contentAlignment = Alignment.Center, ) { Icon( imageVector = Icons.Filled.PlayArrow, contentDescription = "Task Options", - tint = MaterialTheme.colorScheme.onPrimary + tint = MaterialTheme.colorScheme.onPrimary, ) } } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/TaskProgress.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/TaskProgress.kt index 56b812a..c3354e1 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/TaskProgress.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/component/TaskProgress.kt @@ -43,7 +43,16 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @Composable -fun TaskProgress(content: String? = null, percentage: Float, radius: Dp = 20.dp, mainColor: Color, counterColor: Color, strokeWidth: Dp = 8.dp, animationDuration: Int = 800, animDelay: Int = 0) { +fun TaskProgress( + content: String? = null, + percentage: Float, + radius: Dp = 20.dp, + mainColor: Color, + counterColor: Color, + strokeWidth: Dp = 8.dp, + animationDuration: Int = 800, + animDelay: Int = 0, +) { var animationPlayed by remember { mutableStateOf(false) } @@ -52,9 +61,9 @@ fun TaskProgress(content: String? = null, percentage: Float, radius: Dp = 20.dp, targetValue = if (animationPlayed) percentage else 0f, animationSpec = tween( durationMillis = animationDuration, - delayMillis = animDelay + delayMillis = animDelay, ), - label = "" + label = "", ) LaunchedEffect(key1 = true) { @@ -62,18 +71,18 @@ fun TaskProgress(content: String? = null, percentage: Float, radius: Dp = 20.dp, } Box( - contentAlignment = Alignment.Center + contentAlignment = Alignment.Center, ) { Canvas( modifier = Modifier - .size(radius * 5f) + .size(radius * 5f), ) { drawArc( color = Color.LightGray, startAngle = 0f, sweepAngle = 360f, useCenter = false, - style = Stroke(strokeWidth.toPx(), cap = StrokeCap.Round) + style = Stroke(strokeWidth.toPx(), cap = StrokeCap.Round), ) drawArc( @@ -81,7 +90,7 @@ fun TaskProgress(content: String? = null, percentage: Float, radius: Dp = 20.dp, startAngle = -360f, sweepAngle = (360 * (currentPercentage.value * 0.01)).toFloat(), useCenter = false, - style = Stroke(strokeWidth.toPx(), cap = StrokeCap.Round) + style = Stroke(strokeWidth.toPx(), cap = StrokeCap.Round), ) } @@ -89,25 +98,25 @@ fun TaskProgress(content: String? = null, percentage: Float, radius: Dp = 20.dp, contentAlignment = Alignment.Center, modifier = Modifier .size(110.dp) - .clip(CircleShape) + .clip(CircleShape), ) { Column( modifier = Modifier.padding(8.dp), verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally + horizontalAlignment = Alignment.CenterHorizontally, ) { if (content?.isEmpty()?.not() == true) { Text( text = content, fontSize = 22.sp, - fontWeight = FontWeight.Bold + fontWeight = FontWeight.Bold, ) } else { Text( text = "${(currentPercentage.value).toInt()}%", fontSize = 22.sp, fontWeight = FontWeight.Bold, - color = counterColor + color = counterColor, ) } } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/theme/Shapes.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/theme/Shapes.kt index d79b5ca..368a953 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/theme/Shapes.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/theme/Shapes.kt @@ -22,5 +22,5 @@ import androidx.compose.ui.unit.dp internal val Shapes = Shapes( small = RoundedCornerShape(4.dp), medium = RoundedCornerShape(8.dp), - large = RoundedCornerShape(12.dp) + large = RoundedCornerShape(12.dp), ) diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/theme/Theme.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/theme/Theme.kt index 1a0bd4e..1d4fce6 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/theme/Theme.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/theme/Theme.kt @@ -38,7 +38,7 @@ private val LightColors = lightColorScheme( secondaryContainer = PrimaryColor, onSecondaryContainer = Color.White, error = ErrorColor, - onError = OnErrorColor + onError = OnErrorColor, ) private val DarkColors = darkColorScheme( @@ -57,17 +57,20 @@ private val DarkColors = darkColorScheme( secondaryContainer = PrimaryColor, onSecondaryContainer = Color.White, error = ErrorColor, - onError = OnErrorColor + onError = OnErrorColor, ) @Composable -internal fun FocusBloomTheme(useDarkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) { +internal fun FocusBloomTheme( + useDarkTheme: Boolean = isSystemInDarkTheme(), + content: @Composable () -> Unit, +) { val autoColors = if (useDarkTheme) DarkColors else LightColors MaterialTheme( colorScheme = autoColors, typography = getTypography(), shapes = Shapes, - content = content + content = content, ) } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/theme/Type.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/theme/Type.kt index d452888..d4447b9 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/theme/Type.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/theme/Type.kt @@ -22,82 +22,82 @@ import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.sp -import com.joelkanyi.focusbloom.platform.font +import focusbloom.shared.generated.resources.Res +import focusbloom.shared.generated.resources.montserrat_black +import focusbloom.shared.generated.resources.montserrat_bold +import focusbloom.shared.generated.resources.montserrat_extrabold +import focusbloom.shared.generated.resources.montserrat_extralight +import focusbloom.shared.generated.resources.montserrat_light +import focusbloom.shared.generated.resources.montserrat_medium +import focusbloom.shared.generated.resources.montserrat_regular +import focusbloom.shared.generated.resources.montserrat_semi_bold +import focusbloom.shared.generated.resources.montserrat_thin +import org.jetbrains.compose.resources.Font @Composable -internal fun getTypography(): Typography { +fun montserrat(): FontFamily { val montserratRegular = - font( - "Montserrat", - "montserrat_regular", - FontWeight.Normal, - FontStyle.Normal + Font( + resource = Res.font.montserrat_regular, + weight = FontWeight.Normal, + style = FontStyle.Normal, ) val montserratBold = - font( - "Montserrat", - "montserrat_bold", + Font( + resource = Res.font.montserrat_bold, FontWeight.Bold, - FontStyle.Normal + FontStyle.Normal, ) val montserratLight = - font( - "Montserrat", - "montserrat_light", + Font( + resource = Res.font.montserrat_light, FontWeight.Light, - FontStyle.Normal + FontStyle.Normal, ) val montserratMedium = - font( - "Montserrat", - "montserrat_medium", + Font( + resource = Res.font.montserrat_medium, FontWeight.Medium, - FontStyle.Normal + FontStyle.Normal, ) val montserratSemiBold = - font( - "Montserrat", - "montserrat_semi_bold", + Font( + resource = Res.font.montserrat_semi_bold, FontWeight.SemiBold, - FontStyle.Normal + FontStyle.Normal, ) val montserratThin = - font( - "Montserrat", - "montserrat_thin", + Font( + resource = Res.font.montserrat_thin, FontWeight.Thin, - FontStyle.Normal + FontStyle.Normal, ) val montserratExtraBold = - font( - "Montserrat", - "montserrat_extrabold", + Font( + resource = Res.font.montserrat_extrabold, FontWeight.ExtraBold, - FontStyle.Normal + FontStyle.Normal, ) val montserratExtraLight = - font( - "Montserrat", - "montserrat_extralight", + Font( + resource = Res.font.montserrat_extralight, FontWeight.ExtraLight, - FontStyle.Normal + FontStyle.Normal, ) - val montserratBlack = font( - "Montserrat", - "montserrat_black", + val montserratBlack = Font( + resource = Res.font.montserrat_black, FontWeight.Black, - FontStyle.Normal + FontStyle.Normal, ) - @Composable - fun montserrat() = FontFamily( + return FontFamily( montserratThin, montserratExtraLight, montserratLight, @@ -106,107 +106,88 @@ internal fun getTypography(): Typography { montserratSemiBold, montserratBold, montserratExtraBold, - montserratBlack + montserratBlack, ) +} +@Composable +internal fun getTypography(): Typography { + val montserrat = montserrat() return Typography( displayLarge = TextStyle( - fontFamily = montserrat(), + fontFamily = montserrat, fontWeight = FontWeight.W400, - fontSize = 50.sp - // lineHeight = 64.sp, - // letterSpacing = (-0.25).sp, + fontSize = 50.sp, ), displayMedium = TextStyle( - fontFamily = montserrat(), + fontFamily = montserrat, fontWeight = FontWeight.W400, - fontSize = 40.sp - // lineHeight = 52.sp, + fontSize = 40.sp, ), displaySmall = TextStyle( - fontFamily = montserrat(), + fontFamily = montserrat, fontWeight = FontWeight.W400, - fontSize = 30.sp - // lineHeight = 44.sp, + fontSize = 30.sp, ), headlineLarge = TextStyle( - fontFamily = montserrat(), + fontFamily = montserrat, fontWeight = FontWeight.W400, - fontSize = 28.sp - // lineHeight = 40.sp, + fontSize = 28.sp, ), headlineMedium = TextStyle( - fontFamily = montserrat(), + fontFamily = montserrat, fontWeight = FontWeight.W400, - fontSize = 24.sp - // lineHeight = 36.sp, + fontSize = 24.sp, ), headlineSmall = TextStyle( - fontFamily = montserrat(), + fontFamily = montserrat, fontWeight = FontWeight.W400, - fontSize = 20.sp - // lineHeight = 32.sp, + fontSize = 20.sp, ), titleLarge = TextStyle( - fontFamily = montserrat(), + fontFamily = montserrat, fontWeight = FontWeight.W700, - fontSize = 18.sp - // lineHeight = 28.sp, + fontSize = 18.sp, ), titleMedium = TextStyle( - fontFamily = montserrat(), + fontFamily = montserrat, fontWeight = FontWeight.W700, - fontSize = 14.sp - // lineHeight = 24.sp, - // letterSpacing = 0.1.sp, + fontSize = 14.sp, ), titleSmall = TextStyle( - fontFamily = montserrat(), + fontFamily = montserrat, fontWeight = FontWeight.W500, - fontSize = 12.sp - // lineHeight = 20.sp, - // letterSpacing = 0.1.sp, + fontSize = 12.sp, ), bodyLarge = TextStyle( - fontFamily = montserrat(), + fontFamily = montserrat, fontWeight = FontWeight.W400, - fontSize = 14.sp - // lineHeight = 24.sp, - // letterSpacing = 0.5.sp, + fontSize = 14.sp, ), bodyMedium = TextStyle( - fontFamily = montserrat(), + fontFamily = montserrat, fontWeight = FontWeight.W400, - fontSize = 12.sp - // lineHeight = 20.sp, - // letterSpacing = 0.25.sp, + fontSize = 12.sp, ), bodySmall = TextStyle( - fontFamily = montserrat(), + fontFamily = montserrat, fontWeight = FontWeight.W400, - fontSize = 11.sp - // lineHeight = 16.sp, - // letterSpacing = 0.4.sp, + fontSize = 11.sp, ), labelLarge = TextStyle( - fontFamily = montserrat(), + fontFamily = montserrat, fontWeight = FontWeight.W400, - fontSize = 13.sp - // lineHeight = 20.sp, - // letterSpacing = 0.1.sp, + fontSize = 13.sp, ), labelMedium = TextStyle( - fontFamily = montserrat(), + fontFamily = montserrat, fontWeight = FontWeight.W400, - fontSize = 11.sp - // lineHeight = 16.sp, - // letterSpacing = 0.5.sp, + fontSize = 11.sp, ), labelSmall = TextStyle( - fontFamily = montserrat(), + fontFamily = montserrat, fontWeight = FontWeight.W500, - fontSize = 9.sp - // lineHeight = 16.sp, - ) + fontSize = 9.sp, + ), ) } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/utils/FilledIcon.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/utils/FilledIcon.kt index 11cb1e3..2d11724 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/utils/FilledIcon.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/presentation/utils/FilledIcon.kt @@ -17,16 +17,22 @@ package com.joelkanyi.focusbloom.core.presentation.utils import androidx.compose.runtime.Composable import cafe.adriel.voyager.navigator.tab.Tab +import focusbloom.shared.generated.resources.Res +import focusbloom.shared.generated.resources.add_filled +import focusbloom.shared.generated.resources.calendar_filled +import focusbloom.shared.generated.resources.home_filled +import focusbloom.shared.generated.resources.settings_filled +import focusbloom.shared.generated.resources.statistics_filled import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.painterResource @Composable @OptIn(ExperimentalResourceApi::class) fun FilledIcon(item: Tab) = when (item.options.index) { - (0u).toUShort() -> painterResource("home_filled.xml") - (1u).toUShort() -> painterResource("calendar_filled.xml") - (2u).toUShort() -> painterResource("statistics_filled.xml") - (3u).toUShort() -> painterResource("settings_filled.xml") - (4u).toUShort() -> painterResource("add_filled.xml") - else -> painterResource("home_filled.xml") + (0u).toUShort() -> painterResource(Res.drawable.home_filled) + (1u).toUShort() -> painterResource(Res.drawable.calendar_filled) + (2u).toUShort() -> painterResource(Res.drawable.statistics_filled) + (3u).toUShort() -> painterResource(Res.drawable.settings_filled) + (4u).toUShort() -> painterResource(Res.drawable.add_filled) + else -> painterResource(Res.drawable.home_filled) } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/utils/Utils.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/utils/Utils.kt index d50a63f..0ebd70b 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/utils/Utils.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/core/utils/Utils.kt @@ -16,9 +16,6 @@ package com.joelkanyi.focusbloom.core.utils import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.ProvidableCompositionLocal -import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ParentDataModifier import androidx.compose.ui.platform.LocalDensity @@ -26,10 +23,11 @@ import androidx.compose.ui.text.capitalize import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.Dp -import cafe.adriel.voyager.navigator.Navigator import com.joelkanyi.focusbloom.core.domain.model.SessionType import com.joelkanyi.focusbloom.core.domain.model.Task import com.joelkanyi.focusbloom.core.domain.model.taskTypes +import focusbloom.shared.generated.resources.Res +import focusbloom.shared.generated.resources.other import kotlinx.datetime.Clock import kotlinx.datetime.DateTimeUnit import kotlinx.datetime.Instant @@ -42,6 +40,7 @@ import kotlinx.datetime.minus import kotlinx.datetime.plus import kotlinx.datetime.toInstant import kotlinx.datetime.toLocalDateTime +import org.jetbrains.compose.resources.DrawableResource import kotlin.jvm.JvmInline @Composable @@ -51,7 +50,10 @@ fun differenceBetweenMinutes(minTime: LocalTime, maxTime: LocalTime): Int { return (maxTime.hour - minTime.hour) * 60 } -fun differenceBetweenDays(minDate: LocalDate = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()).date, maxDate: LocalDate = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()).date): Int { +fun differenceBetweenDays( + minDate: LocalDate = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()).date, + maxDate: LocalDate = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()).date, +): Int { Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()).time return (maxDate.dayOfMonth - minDate.dayOfMonth) } @@ -64,14 +66,6 @@ fun LocalDateTime.plusDays(days: Int): LocalDateTime { return this.date.plus(days, DateTimeUnit.DAY).atTime(this.time) } -fun LocalDate.minusDays(days: Int): LocalDate { - return this.minus(days, DateTimeUnit.DAY) -} - -fun LocalDateTime.minusDays(days: Int): LocalDateTime { - return this.date.minus(days, DateTimeUnit.DAY).atTime(this.time) -} - fun LocalTime.plusHours(hours: Int): LocalTime { val addedHours = this.hour + hours return LocalTime(addedHours, this.minute) @@ -107,7 +101,7 @@ data class PositionedTask( val end: LocalTime, val col: Int = 0, val colSpan: Int = 1, - val colTotal: Int = 1 + val colTotal: Int = 1, ) sealed class ScheduleSize { @@ -118,21 +112,26 @@ sealed class ScheduleSize { } class TaskDataModifier( - private val positionedTask: PositionedTask + private val positionedTask: PositionedTask, ) : ParentDataModifier { override fun Density.modifyParentData(parentData: Any?) = positionedTask } fun Modifier.taskData(positionedTask: PositionedTask) = this.then(TaskDataModifier(positionedTask)) -fun splitTasks(tasks: List, sessionTime: Int, shortBreakTime: Int, longBreakTime: Int): List { +fun splitTasks( + tasks: List, + sessionTime: Int, + shortBreakTime: Int, + longBreakTime: Int, +): List { return tasks .map { task -> val end = task.start.calculateEndTime( focusSessions = task.focusSessions, sessionTime = sessionTime, shortBreakTime = shortBreakTime, - longBreakTime = longBreakTime + longBreakTime = longBreakTime, ) val startDate = task.start.date val endDate = end.date @@ -143,8 +142,8 @@ fun splitTasks(tasks: List, sessionTime: Int, shortBreakTime: Int, longBre SplitType.None, task.start.date, task.start.time, - end.time - ) + end.time, + ), ) } else { val days = differenceBetweenDays(startDate, endDate) @@ -164,7 +163,7 @@ fun splitTasks(tasks: List, sessionTime: Int, shortBreakTime: Int, longBre end.time } else { max() - } + }, ) } splitTasks @@ -240,7 +239,13 @@ fun Long?.selectedDateMillisToLocalDateTime(): LocalDateTime { .toLocalDateTime(TimeZone.currentSystemDefault()) } -fun calculateFromFocusSessions(focusSessions: Int, sessionTime: Int = 25, shortBreakTime: Int = 5, longBreakTime: Int = 15, currentLocalDateTime: LocalDateTime): LocalTime { +fun calculateFromFocusSessions( + focusSessions: Int, + sessionTime: Int = 25, + shortBreakTime: Int = 5, + longBreakTime: Int = 15, + currentLocalDateTime: LocalDateTime, +): LocalTime { return if (focusSessions <= 0) { currentLocalDateTime.time } else { @@ -268,7 +273,7 @@ fun LocalDateTime.dateTimeToString(): String { fun toLocalDateTime(hour: Int, minute: Int, date: LocalDate): LocalDateTime { return LocalDateTime( date, - LocalTime(hour, minute) + LocalTime(hour, minute), ) } @@ -310,8 +315,8 @@ fun String.taskColor(): Long { return taskTypes.find { it.name == this }?.color ?: 0xFFAFBBF2 } -fun String.taskIcon(): String { - return taskTypes.find { it.name == this }?.icon ?: "other.xml" +fun String.taskIcon(): DrawableResource { + return taskTypes.find { it.name == this }?.icon ?: Res.drawable.other } fun getThisWeek(): List { @@ -371,13 +376,13 @@ fun getLast52Weeks(): List>> { weeks += "${ week.first().month.name.lowercase().capitalize(Locale.current).substring( 0, - 3 + 3, ) } ${week.first().dayOfMonth} ${if (week.first().year != thisYear) week.first().year else ""}" + " - ${ week.last().month.name.lowercase().capitalize(Locale.current).substring( 0, - 3 + 3, ) } ${week.last().dayOfMonth} ${if (week.last().year != thisYear) week.last().year else ""}" to week } @@ -478,12 +483,17 @@ fun Int.timeFormat(): String { } } -fun Task.durationInMinutes(focusSessions: Int, sessionTime: Int, shortBreakTime: Int, longBreakTime: Int): Int { +fun Task.durationInMinutes( + focusSessions: Int, + sessionTime: Int, + shortBreakTime: Int, + longBreakTime: Int, +): Int { val end = start.calculateEndTime( focusSessions = focusSessions, sessionTime = sessionTime, shortBreakTime = shortBreakTime, - longBreakTime = longBreakTime + longBreakTime = longBreakTime, ) return (end.time.toSecondOfDay() - start.time.toSecondOfDay()) / 60 } @@ -568,20 +578,16 @@ fun String?.sessionType(): SessionType { } } -val LocalAppNavigator: ProvidableCompositionLocal = staticCompositionLocalOf { null } - -@Composable -fun ProvideAppNavigator(navigator: Navigator, content: @Composable () -> Unit) { - CompositionLocalProvider(LocalAppNavigator provides navigator) { - content() - } -} - fun String.pickFirstName(): String { return this.split(" ").first() } -fun LocalDateTime.calculateEndTime(focusSessions: Int, sessionTime: Int, shortBreakTime: Int, longBreakTime: Int): LocalDateTime { +fun LocalDateTime.calculateEndTime( + focusSessions: Int, + sessionTime: Int, + shortBreakTime: Int, + longBreakTime: Int, +): LocalDateTime { val totalSessionTimeMinutes = sessionTime * focusSessions val totalShortBreakTimeMinutes = shortBreakTime * (focusSessions - 1) val totalLongBreakTimeMinutes = longBreakTime * (focusSessions / 4) diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/di/CommonModule.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/di/CommonModule.kt index 271f5dc..ee51fce 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/di/CommonModule.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/di/CommonModule.kt @@ -38,12 +38,10 @@ import com.joelkanyi.focusbloom.feature.statistics.StatisticsScreenModel import com.joelkanyi.focusbloom.feature.taskprogress.TaskProgressScreenModel import com.joelkanyi.focusbloom.main.MainViewModel import com.joelkanyi.focusbloom.platform.DatabaseDriverFactory -import com.russhwolf.settings.ExperimentalSettingsApi import database.TaskEntity import org.koin.core.module.Module import org.koin.dsl.module -@OptIn(ExperimentalSettingsApi::class) fun commonModule() = module { /** * Database @@ -59,8 +57,8 @@ fun commonModule() = module { consumedShortBreakTimeAdapter = consumedShortBreakTimeAdapter, currentAdapter = currentAdapter, currentCycleAdapter = currentCycleAdapter, - focusSessionsAdapter = focusSessionsAdapter - ) + focusSessionsAdapter = focusSessionsAdapter, + ), ) } /** @@ -75,13 +73,13 @@ fun commonModule() = module { */ single { SettingsRepositoryImpl( - preferenceManager = get() + preferenceManager = get(), ) } single { TasksRepositoryImpl( - bloomDatabase = get() + bloomDatabase = get(), ) } @@ -90,31 +88,31 @@ fun commonModule() = module { */ single { SettingsScreenModel( - settingsRepository = get() + settingsRepository = get(), ) } single { AddTaskScreenModel( settingsRepository = get(), - tasksRepository = get() + tasksRepository = get(), ) } single { HomeScreenModel( tasksRepository = get(), - settingsRepository = get() + settingsRepository = get(), ) } single { StatisticsScreenModel( tasksRepository = get(), - settingsRepository = get() + settingsRepository = get(), ) } single { CalendarScreenModel( tasksRepository = get(), - settingsRepository = get() + settingsRepository = get(), ) } @@ -122,19 +120,19 @@ fun commonModule() = module { TaskProgressScreenModel( settingsRepository = get(), tasksRepository = get(), - notificationManager = get() + notificationManager = get(), ) } single { MainViewModel( - settingsRepository = get() + settingsRepository = get(), ) } single { OnboadingViewModel( - settingsRepository = get() + settingsRepository = get(), ) } } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/di/KoinInit.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/di/KoinInit.kt index 5a484ab..ccdbe13 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/di/KoinInit.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/di/KoinInit.kt @@ -25,8 +25,8 @@ class KoinInit { modules( listOf( platformModule(), - commonModule() - ) + commonModule(), + ), ) appDeclaration() }.koin diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/addtask/AddTaskScreen.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/addtask/AddTaskScreen.kt index b65c6c7..6226bb7 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/addtask/AddTaskScreen.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/addtask/AddTaskScreen.kt @@ -54,12 +54,9 @@ import androidx.compose.material3.rememberTimePickerState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment.Companion.CenterVertically -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.PathEffect @@ -92,26 +89,29 @@ import com.joelkanyi.focusbloom.core.utils.selectedDateMillisToLocalDateTime import com.joelkanyi.focusbloom.core.utils.toLocalDateTime import com.joelkanyi.focusbloom.core.utils.today import com.joelkanyi.focusbloom.platform.StatusBarColors +import focusbloom.shared.generated.resources.Res +import focusbloom.shared.generated.resources.end_time +import focusbloom.shared.generated.resources.ic_complete +import focusbloom.shared.generated.resources.start_time import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.withContext import kotlinx.datetime.Clock import kotlinx.datetime.LocalDateTime import kotlinx.datetime.LocalTime +import org.jetbrains.compose.resources.DrawableResource import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.painterResource -import org.koin.compose.rememberKoinInject +import org.koin.compose.koinInject -@OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class) +@OptIn(ExperimentalMaterial3Api::class) @Composable fun AddTaskScreen( - taskId: Int? = null + taskId: Int? = null, + screenModel: AddTaskScreenModel = koinInject(), ) { - val screenModel: AddTaskScreenModel = rememberKoinInject() - StatusBarColors( statusBarColor = MaterialTheme.colorScheme.background, - navBarColor = MaterialTheme.colorScheme.background + navBarColor = MaterialTheme.colorScheme.background, ) val snackbarHostState = remember { SnackbarHostState() } val keyboardController = LocalSoftwareKeyboardController.current @@ -134,28 +134,11 @@ fun AddTaskScreen( val startTimeState = rememberTimePickerState( initialHour = today().hour, initialMinute = today().minute, - is24Hour = hourFormat == 24 + is24Hour = hourFormat == 24, ) val datePickerState = rememberDatePickerState( - initialSelectedDateMillis = Clock.System.now().toEpochMilliseconds() + initialSelectedDateMillis = Clock.System.now().toEpochMilliseconds(), ) - val calculatedFocusTime by remember { - mutableStateOf( - calculateFromFocusSessions( - focusSessions = focusSessions, - sessionTime = sessionTime, - shortBreakTime = shortBreakTime, - longBreakTime = longBreakTime, - currentLocalDateTime = LocalDateTime( - year = taskDate.year, - month = taskDate.month, - dayOfMonth = taskDate.dayOfMonth, - hour = startTime.hour, - minute = startTime.minute - ) - ) - ) - } LaunchedEffect(key1 = true) { screenModel.getTask(taskId) @@ -170,16 +153,16 @@ fun AddTaskScreen( month = taskDate.month, dayOfMonth = taskDate.dayOfMonth, hour = startTime.hour, - minute = startTime.minute - ) - ) + minute = startTime.minute, + ), + ), ) withContext(Dispatchers.Main.immediate) { screenModel.eventsFlow.collect { event -> when (event) { is UiEvents.ShowSnackbar -> { snackbarHostState.showSnackbar( - message = event.message + message = event.message, ) } @@ -213,12 +196,12 @@ fun AddTaskScreen( month = taskDate.month, dayOfMonth = taskDate.dayOfMonth, hour = it.hour, - minute = it.minute - ) - ) + minute = it.minute, + ), + ), ) screenModel.setShowStartTimeInputDialog(false) - } + }, ) } @@ -231,14 +214,13 @@ fun AddTaskScreen( onConfirmDate = { screenModel.setTaskDate(it) screenModel.setShowTaskDatePickerDialog(false) - } + }, ) } AddTaskScreenContent( snackbarHostState = snackbarHostState, hourFormat = hourFormat, - calculatedFocusTime = calculatedFocusTime, taskOptions = taskTypes, selectedTaskType = selectedTaskType, taskName = taskName, @@ -279,7 +261,7 @@ fun AddTaskScreen( start = toLocalDateTime( date = taskDate.date, hour = startTime.hour, - minute = startTime.minute + minute = startTime.minute, ), color = selectedTaskType.color, current = "Focus", @@ -292,8 +274,8 @@ fun AddTaskScreen( consumedLongBreakTime = 0L, inProgressTask = false, currentCycle = 0, - active = false - ) + active = false, + ), ) } else { screenModel.addTask( @@ -303,7 +285,7 @@ fun AddTaskScreen( start = toLocalDateTime( date = taskDate.date, hour = startTime.hour, - minute = startTime.minute + minute = startTime.minute, ), color = selectedTaskType.color, current = "Focus", @@ -316,11 +298,11 @@ fun AddTaskScreen( consumedLongBreakTime = 0L, inProgressTask = false, currentCycle = 0, - active = false - ) + active = false, + ), ) } - } + }, ) } @@ -328,7 +310,6 @@ fun AddTaskScreen( @Composable private fun AddTaskScreenContent( snackbarHostState: SnackbarHostState, - calculatedFocusTime: LocalTime, hourFormat: Int, taskOptions: List, selectedTaskType: TaskType, @@ -347,13 +328,13 @@ private fun AddTaskScreenContent( // datePickerState: DatePickerState, taskDate: LocalDateTime, startTime: LocalTime, - endTime: LocalTime + endTime: LocalTime, ) { Scaffold( snackbarHost = { Box( modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.TopCenter // Change to your desired position + contentAlignment = Alignment.TopCenter, // Change to your desired position ) { SnackbarHost( hostState = snackbarHostState, @@ -366,33 +347,34 @@ private fun AddTaskScreenContent( }, border = BorderStroke(2.dp, MaterialTheme.colorScheme.primary), colors = CardDefaults.cardColors( - containerColor = MaterialTheme.colorScheme.secondary - ) + containerColor = MaterialTheme.colorScheme.secondary, + ), ) { Row( modifier = Modifier .fillMaxWidth() .padding(16.dp), verticalAlignment = CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween + horizontalArrangement = Arrangement.SpaceBetween, ) { Text( modifier = Modifier .fillMaxWidth(.85f), text = it.visuals.message, style = MaterialTheme.typography.titleSmall.copy( - color = MaterialTheme.colorScheme.onPrimary - ) + color = MaterialTheme.colorScheme.onPrimary, + ), ) Image( modifier = Modifier .size(32.dp), - painter = painterResource("ic_complete.xml"), - contentDescription = "Task Options" + painter = painterResource( + Res.drawable.ic_complete), + contentDescription = "Task Options", ) } } - } + }, ) } }, @@ -400,12 +382,12 @@ private fun AddTaskScreenContent( BloomTopAppBar { Text(text = "Add Task") } - } + }, ) { paddingValues -> LazyColumn( modifier = Modifier.padding(paddingValues), contentPadding = PaddingValues(horizontal = 16.dp), - verticalArrangement = Arrangement.spacedBy(8.dp) + verticalArrangement = Arrangement.spacedBy(8.dp), ) { item { BloomInputTextField( @@ -416,8 +398,8 @@ private fun AddTaskScreenContent( text = "Task Name", style = MaterialTheme.typography.titleSmall.copy( fontWeight = FontWeight.SemiBold, - fontSize = 16.sp - ) + fontSize = 16.sp, + ), ) }, value = TextFieldState(text = taskName), @@ -425,16 +407,16 @@ private fun AddTaskScreenContent( placeholder = { Text( text = "Enter Task Name", - style = MaterialTheme.typography.titleSmall + style = MaterialTheme.typography.titleSmall, ) }, keyboardOptions = KeyboardOptions.Default.copy( - capitalization = KeyboardCapitalization.Words + capitalization = KeyboardCapitalization.Words, ), textStyle = MaterialTheme.typography.titleSmall.copy( - fontSize = 16.sp - ) + fontSize = 16.sp, + ), ) } item { @@ -446,8 +428,8 @@ private fun AddTaskScreenContent( text = "Description", style = MaterialTheme.typography.titleSmall.copy( fontWeight = FontWeight.SemiBold, - fontSize = 16.sp - ) + fontSize = 16.sp, + ), ) }, @@ -456,15 +438,15 @@ private fun AddTaskScreenContent( placeholder = { Text( text = "Enter Description", - style = MaterialTheme.typography.titleSmall + style = MaterialTheme.typography.titleSmall, ) }, keyboardOptions = KeyboardOptions.Default.copy( - capitalization = KeyboardCapitalization.Sentences + capitalization = KeyboardCapitalization.Sentences, ), textStyle = MaterialTheme.typography.titleSmall.copy( - fontSize = 16.sp - ) + fontSize = 16.sp, + ), ) } item { @@ -476,17 +458,17 @@ private fun AddTaskScreenContent( text = "Date", style = MaterialTheme.typography.titleSmall.copy( fontWeight = FontWeight.SemiBold, - fontSize = 16.sp - ) + fontSize = 16.sp, + ), ) }, currentTextState = TextFieldState( - text = taskDate.date.toString() + text = taskDate.date.toString(), ), onClick = onClickPickDate, textStyle = MaterialTheme.typography.titleSmall.copy( - fontSize = 16.sp - ) + fontSize = 16.sp, + ), ) } @@ -497,8 +479,8 @@ private fun AddTaskScreenContent( text = "Task Type", style = MaterialTheme.typography.titleSmall.copy( fontWeight = FontWeight.SemiBold, - fontSize = 16.sp - ) + fontSize = 16.sp, + ), ) }, modifier = Modifier.fillMaxWidth(), @@ -506,8 +488,8 @@ private fun AddTaskScreenContent( selectedOption = TextFieldState(selectedTaskType.name), onOptionSelected = onSelectedTaskTypeChange, textStyle = MaterialTheme.typography.titleSmall.copy( - fontSize = 16.sp - ) + fontSize = 16.sp, + ), ) } @@ -516,16 +498,16 @@ private fun AddTaskScreenContent( Row( modifier = Modifier.fillMaxWidth(), verticalAlignment = CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween + horizontalArrangement = Arrangement.SpaceBetween, ) { TimeComponent( time = startTime, hourFormat = hourFormat, title = "Start Time", - icon = "start_time.xml", + icon = Res.drawable.start_time, iconColor = MaterialTheme.colorScheme.primary, iconSize = 24, - onClick = onClickPickStartTime + onClick = onClickPickStartTime, ) DashedDivider( @@ -533,16 +515,16 @@ private fun AddTaskScreenContent( thickness = 3.dp, phase = 5f, modifier = Modifier - .width(180.dp) + .width(180.dp), ) TimeComponent( time = endTime, hourFormat = hourFormat, title = "End Time", - icon = "end_time.xml", + icon = Res.drawable.end_time, iconColor = SuccessColor, - onClick = {} + onClick = {}, ) } } @@ -554,8 +536,8 @@ private fun AddTaskScreenContent( style = MaterialTheme.typography.titleMedium.copy( textAlign = TextAlign.Center, fontWeight = FontWeight.SemiBold, - fontSize = 18.sp - ) + fontSize = 18.sp, + ), ) } @@ -568,7 +550,7 @@ private fun AddTaskScreenContent( onClickAdd = { onIncrementFocusSessions() }, - currentValue = focusSessions + currentValue = focusSessions, ) } @@ -584,7 +566,7 @@ private fun AddTaskScreenContent( onClick = onClickAddTask, content = { Text(text = "Save") - } + }, ) } } @@ -593,30 +575,38 @@ private fun AddTaskScreenContent( @OptIn(ExperimentalResourceApi::class) @Composable -private fun TimeComponent(title: String, icon: String, iconColor: Color, iconSize: Int = 32, time: LocalTime, hourFormat: Int, onClick: () -> Unit) { +private fun TimeComponent( + title: String, + icon: DrawableResource, + iconColor: Color, + iconSize: Int = 32, + time: LocalTime, + hourFormat: Int, + onClick: () -> Unit, +) { Column( modifier = Modifier.clickable { onClick() }, - verticalArrangement = Arrangement.spacedBy(8.dp) + verticalArrangement = Arrangement.spacedBy(8.dp), ) { Text( text = title, style = MaterialTheme.typography.titleMedium.copy( fontWeight = FontWeight.SemiBold, - fontSize = 16.sp - ) + fontSize = 16.sp, + ), ) Row( modifier = Modifier, horizontalArrangement = Arrangement.spacedBy(8.dp), - verticalAlignment = CenterVertically + verticalAlignment = CenterVertically, ) { Text( text = time.formattedTimeBasedOnTimeFormat(hourFormat), style = MaterialTheme.typography.titleSmall.copy( - fontSize = 16.sp - ) + fontSize = 16.sp, + ), ) Icon( @@ -624,7 +614,7 @@ private fun TimeComponent(title: String, icon: String, iconColor: Color, iconSiz .size(iconSize.dp), painter = painterResource(icon), contentDescription = title, - tint = iconColor + tint = iconColor, ) } } @@ -632,7 +622,13 @@ private fun TimeComponent(title: String, icon: String, iconColor: Color, iconSiz @OptIn(ExperimentalMaterial3Api::class) @Composable -fun TimerInputDialog(title: String, modifier: Modifier = Modifier, onDismiss: () -> Unit, state: TimePickerState, onConfirmStartTime: (LocalTime) -> Unit) { +fun TimerInputDialog( + title: String, + modifier: Modifier = Modifier, + onDismiss: () -> Unit, + state: TimePickerState, + onConfirmStartTime: (LocalTime) -> Unit, +) { AlertDialog( properties = DialogProperties(usePlatformDefaultWidth = true), modifier = modifier, @@ -640,13 +636,13 @@ fun TimerInputDialog(title: String, modifier: Modifier = Modifier, onDismiss: () title = { Text( text = title, - style = MaterialTheme.typography.titleMedium + style = MaterialTheme.typography.titleMedium, ) }, text = { TimeInput( modifier = Modifier.fillMaxWidth(), - state = state + state = state, ) }, dismissButton = { @@ -654,7 +650,7 @@ fun TimerInputDialog(title: String, modifier: Modifier = Modifier, onDismiss: () onClick = onDismiss, content = { Text(text = "Cancel") - } + }, ) }, confirmButton = { @@ -663,22 +659,26 @@ fun TimerInputDialog(title: String, modifier: Modifier = Modifier, onDismiss: () onConfirmStartTime( LocalTime( hour = state.hour, - minute = state.minute - ) + minute = state.minute, + ), ) onDismiss() }, content = { Text(text = "OK") - } + }, ) - } + }, ) } @OptIn(ExperimentalMaterial3Api::class) @Composable -fun TaskDatePicker(datePickerState: DatePickerState, dismiss: () -> Unit, onConfirmDate: (LocalDateTime) -> Unit) { +fun TaskDatePicker( + datePickerState: DatePickerState, + dismiss: () -> Unit, + onConfirmDate: (LocalDateTime) -> Unit, +) { DatePickerDialog( onDismissRequest = { dismiss() }, dismissButton = { @@ -691,20 +691,26 @@ fun TaskDatePicker(datePickerState: DatePickerState, dismiss: () -> Unit, onConf onClick = { onConfirmDate(datePickerState.selectedDateMillis.selectedDateMillisToLocalDateTime()) dismiss() - } + }, ) { Text(text = "OK") } - } + }, ) { DatePicker(state = datePickerState) } } @Composable -fun DashedDivider(thickness: Dp, color: Color = MaterialTheme.colorScheme.onSurfaceVariant, phase: Float = 10f, intervals: FloatArray = floatArrayOf(10f, 10f), modifier: Modifier) { +fun DashedDivider( + thickness: Dp, + color: Color = MaterialTheme.colorScheme.onSurfaceVariant, + phase: Float = 10f, + intervals: FloatArray = floatArrayOf(10f, 10f), + modifier: Modifier, +) { Canvas( - modifier = modifier + modifier = modifier, ) { val dividerHeight = thickness.toPx() drawRoundRect( @@ -713,9 +719,9 @@ fun DashedDivider(thickness: Dp, color: Color = MaterialTheme.colorScheme.onSurf width = dividerHeight, pathEffect = PathEffect.dashPathEffect( intervals = intervals, - phase = phase - ) - ) + phase = phase, + ), + ), ) } } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/addtask/AddTaskScreenModel.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/addtask/AddTaskScreenModel.kt index 3be92d1..cfc2a4c 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/addtask/AddTaskScreenModel.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/addtask/AddTaskScreenModel.kt @@ -18,7 +18,7 @@ package com.joelkanyi.focusbloom.feature.addtask import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf import cafe.adriel.voyager.core.model.ScreenModel -import cafe.adriel.voyager.core.model.coroutineScope +import cafe.adriel.voyager.core.model.screenModelScope import com.joelkanyi.focusbloom.core.domain.model.Task import com.joelkanyi.focusbloom.core.domain.model.TaskType import com.joelkanyi.focusbloom.core.domain.model.taskTypes @@ -41,7 +41,7 @@ import kotlinx.datetime.LocalTime class AddTaskScreenModel( settingsRepository: SettingsRepository, - private val tasksRepository: TasksRepository + private val tasksRepository: TasksRepository, ) : ScreenModel { private val _eventsFlow = Channel(Channel.UNLIMITED) val eventsFlow = _eventsFlow.receiveAsFlow() @@ -51,30 +51,30 @@ class AddTaskScreenModel( it } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = null + initialValue = null, ) val shortBreakTime = settingsRepository.getShortBreakTime() .map { it } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = null + initialValue = null, ) val longBreakTime = settingsRepository.getLongBreakTime() .map { it } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = null + initialValue = null, ) val hourFormat = settingsRepository.getHourFormat() .map { it } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = null + initialValue = null, ) private val _focusSessions = MutableStateFlow(1) @@ -92,9 +92,9 @@ class AddTaskScreenModel( month = taskDate.value.month, dayOfMonth = taskDate.value.dayOfMonth, hour = startTime.value.hour, - minute = startTime.value.minute - ) - ) + minute = startTime.value.minute, + ), + ), ) } @@ -112,14 +112,14 @@ class AddTaskScreenModel( month = taskDate.value.month, dayOfMonth = taskDate.value.dayOfMonth, hour = startTime.value.hour, - minute = startTime.value.minute - ) - ) + minute = startTime.value.minute, + ), + ), ) } } - fun setFocusSessions(sessions: Int) { + private fun setFocusSessions(sessions: Int) { _focusSessions.value = sessions } @@ -172,7 +172,7 @@ class AddTaskScreenModel( } fun addTask(task: Task) { - coroutineScope.launch { + screenModelScope.launch { tasksRepository.addTask(task) reset() setEndTime(today().time) @@ -199,7 +199,7 @@ class AddTaskScreenModel( private val _task = MutableStateFlow(null) val task = _task.asStateFlow() fun getTask(taskId: Int?) { - coroutineScope.launch { + screenModelScope.launch { if (taskId != null) { tasksRepository.getTask(taskId).collectLatest { _task.value = it @@ -217,7 +217,7 @@ class AddTaskScreenModel( setSelectedOption( taskTypes.firstOrNull { taskType -> taskType.name == it?.type - } ?: taskTypes.last() + } ?: taskTypes.last(), ) setFocusSessions(it?.focusSessions ?: 1) setTaskDate(it?.date ?: today()) @@ -233,14 +233,14 @@ class AddTaskScreenModel( month = it?.date?.month ?: today().month, dayOfMonth = it?.date?.dayOfMonth ?: today().dayOfMonth, hour = it?.start?.time?.hour ?: today().time.hour, - minute = it?.start?.time?.minute ?: today().time.minute - ) - ) + minute = it?.start?.time?.minute ?: today().time.minute, + ), + ), ) } fun updateTask(task: Task) { - coroutineScope.launch { + screenModelScope.launch { tasksRepository.updateTask(task) reset() setEndTime(today().time) diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/calendar/CalendarScreen.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/calendar/CalendarScreen.kt index c6d0506..68b086e 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/calendar/CalendarScreen.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/calendar/CalendarScreen.kt @@ -107,6 +107,8 @@ import com.joelkanyi.focusbloom.core.utils.today import com.joelkanyi.focusbloom.core.utils.truncatedTo import com.joelkanyi.focusbloom.feature.home.component.TaskOptionsBottomSheet import com.joelkanyi.focusbloom.platform.StatusBarColors +import focusbloom.shared.generated.resources.Res +import focusbloom.shared.generated.resources.redo import kotlinx.coroutines.launch import kotlinx.datetime.Clock import kotlinx.datetime.LocalDate @@ -115,17 +117,17 @@ import kotlinx.datetime.TimeZone import kotlinx.datetime.toLocalDateTime import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.painterResource -import org.koin.compose.rememberKoinInject +import org.koin.compose.koinInject import kotlin.math.roundToInt @OptIn(ExperimentalMaterial3WindowSizeClassApi::class, ExperimentalMaterial3Api::class) @Composable -fun CalendarScreen() { - val screenModel: CalendarScreenModel = rememberKoinInject() - +fun CalendarScreen( + screenModel: CalendarScreenModel = koinInject(), +) { StatusBarColors( statusBarColor = MaterialTheme.colorScheme.background, - navBarColor = MaterialTheme.colorScheme.background + navBarColor = MaterialTheme.colorScheme.background, ) val coroutineScope = rememberCoroutineScope() val tasks = screenModel.tasks.collectAsState().value @@ -144,7 +146,7 @@ fun CalendarScreen() { LaunchedEffect(key1 = tasks, block = { calendarPagerState.animateScrollToItem( index = calendarLocalDates().indexOf(selectedDay), - scrollOffset = 0 + scrollOffset = 0, ) }) BoxWithConstraints { @@ -178,7 +180,7 @@ fun CalendarScreen() { onClickEditTask = { tabNavigator.current = BloomTab.AddTaskTab(taskId = it.id) }, - task = selectedTask + task = selectedTask, ) } } @@ -201,13 +203,13 @@ fun CalendarScreen() { calendarPagerState.animateScrollToItem( index = calendarLocalDates().indexOf( Clock.System.now() - .toLocalDateTime(TimeZone.currentSystemDefault()).date + .toLocalDateTime(TimeZone.currentSystemDefault()).date, ), - scrollOffset = 0 + scrollOffset = 0, ) screenModel.setSelectedDay( Clock.System.now() - .toLocalDateTime(TimeZone.currentSystemDefault()).date + .toLocalDateTime(TimeZone.currentSystemDefault()).date, ) } }, @@ -217,7 +219,7 @@ fun CalendarScreen() { onShowTaskOption = { screenModel.selectTask(it) screenModel.openBottomSheet(true) - } + }, ) } } @@ -237,7 +239,7 @@ fun CalendarScreenContent( selectedDay: LocalDate, onClickThisWeek: () -> Unit, onSelectDay: (LocalDate) -> Unit, - onShowTaskOption: (task: Task) -> Unit + onShowTaskOption: (task: Task) -> Unit, ) { Scaffold( topBar = { @@ -249,37 +251,37 @@ fun CalendarScreenContent( actions = { AnimatedVisibility(selectedDay.insideThisWeek().not()) { TextButton( - onClick = onClickThisWeek + onClick = onClickThisWeek, ) { Row( verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(4.dp) + horizontalArrangement = Arrangement.spacedBy(4.dp), ) { Icon( modifier = Modifier.size(18.dp), - painter = painterResource("redo.xml"), - contentDescription = "Today" + painter = painterResource(Res.drawable.redo), + contentDescription = "Today", ) Text( text = "TODAY", style = MaterialTheme.typography.labelLarge.copy( fontWeight = FontWeight.SemiBold, color = MaterialTheme.colorScheme.primary, - textDecoration = TextDecoration.Underline - ) + textDecoration = TextDecoration.Underline, + ), ) } } } - } + }, ) - } + }, ) { paddingValues -> Column( modifier = Modifier .padding(paddingValues) .padding(PaddingValues(horizontal = 16.dp)), - verticalArrangement = Arrangement.spacedBy(12.dp) + verticalArrangement = Arrangement.spacedBy(12.dp), ) { Text( modifier = Modifier.fillMaxWidth(), @@ -287,12 +289,12 @@ fun CalendarScreenContent( style = MaterialTheme.typography.labelLarge.copy( fontSize = 16.sp, fontWeight = FontWeight.SemiBold, - textAlign = TextAlign.End - ) + textAlign = TextAlign.End, + ), ) LazyRow( state = calendarPagerState, - horizontalArrangement = Arrangement.spacedBy(8.dp) + horizontalArrangement = Arrangement.spacedBy(8.dp), ) { items(calendarLocalDates()) { date -> Box( @@ -301,27 +303,27 @@ fun CalendarScreenContent( .clipToBounds() .background( color = if (date == selectedDay) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surfaceVariant, - shape = RoundedCornerShape(4.dp) + shape = RoundedCornerShape(4.dp), ) .clickable { onSelectDay(date) }, - contentAlignment = Alignment.Center + contentAlignment = Alignment.Center, ) { Column { Text( text = date.dayOfWeek.name.substring(0, 3), style = MaterialTheme.typography.labelMedium.copy( - color = if (date == selectedDay) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onSurface + color = if (date == selectedDay) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onSurface, ), - modifier = Modifier.align(Alignment.CenterHorizontally) + modifier = Modifier.align(Alignment.CenterHorizontally), ) Text( text = date.dayOfMonth.toString(), style = MaterialTheme.typography.labelMedium.copy( - color = if (date == selectedDay) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onSurface + color = if (date == selectedDay) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onSurface, ), - modifier = Modifier.align(Alignment.CenterHorizontally) + modifier = Modifier.align(Alignment.CenterHorizontally), ) } } @@ -338,7 +340,7 @@ fun CalendarScreenContent( sessionTime = sessionTime, shortBreakTime = shortBreakTime, longBreakTime = longBreakTime, - onShowTaskOption = onShowTaskOption + onShowTaskOption = onShowTaskOption, ) } else { Text( @@ -351,7 +353,7 @@ fun CalendarScreenContent( }", style = MaterialTheme.typography.labelLarge, textAlign = TextAlign.Center, - modifier = Modifier.align(Alignment.Center) + modifier = Modifier.align(Alignment.Center), ) } } @@ -361,7 +363,15 @@ fun CalendarScreenContent( @OptIn(ExperimentalMaterial3Api::class) @Composable -fun BasicTask(hourFormat: Int, sessionTime: Int, shortBreakTime: Int, longBreakTime: Int, positionedTask: PositionedTask, modifier: Modifier = Modifier, onShowTaskOption: (task: Task) -> Unit) { +fun BasicTask( + hourFormat: Int, + sessionTime: Int, + shortBreakTime: Int, + longBreakTime: Int, + positionedTask: PositionedTask, + modifier: Modifier = Modifier, + onShowTaskOption: (task: Task) -> Unit, +) { val task = positionedTask.task val end by remember { mutableStateOf( @@ -369,8 +379,8 @@ fun BasicTask(hourFormat: Int, sessionTime: Int, shortBreakTime: Int, longBreakT focusSessions = task.focusSessions, sessionTime = sessionTime, shortBreakTime = shortBreakTime, - longBreakTime = longBreakTime - ) + longBreakTime = longBreakTime, + ), ) } val topRadius = @@ -383,7 +393,7 @@ fun BasicTask(hourFormat: Int, sessionTime: Int, shortBreakTime: Int, longBreakT .padding( top = 2.dp, end = 2.dp, - bottom = if (positionedTask.splitType == SplitType.End) 0.dp else 2.dp + bottom = if (positionedTask.splitType == SplitType.End) 0.dp else 2.dp, ) .clipToBounds() .padding(4.dp), @@ -391,26 +401,26 @@ fun BasicTask(hourFormat: Int, sessionTime: Int, shortBreakTime: Int, longBreakT topStart = topRadius, topEnd = topRadius, bottomEnd = bottomRadius, - bottomStart = bottomRadius + bottomStart = bottomRadius, ), colors = CardDefaults.cardColors( - containerColor = Color(task.type.taskColor()) + containerColor = Color(task.type.taskColor()), ), onClick = { onShowTaskOption(task) - } + }, ) { Column( modifier = Modifier .fillMaxWidth() .padding(8.dp), - verticalArrangement = Arrangement.spacedBy(4.dp) + verticalArrangement = Arrangement.spacedBy(4.dp), ) { Row( modifier = Modifier .fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Text( modifier = Modifier @@ -418,11 +428,11 @@ fun BasicTask(hourFormat: Int, sessionTime: Int, shortBreakTime: Int, longBreakT text = task.name, style = MaterialTheme.typography.labelMedium.copy( color = MaterialTheme.colorScheme.onPrimary, - fontSize = 14.sp + fontSize = 14.sp, ), fontWeight = FontWeight.Bold, maxLines = 1, - overflow = TextOverflow.Ellipsis + overflow = TextOverflow.Ellipsis, ) Icon( @@ -433,7 +443,7 @@ fun BasicTask(hourFormat: Int, sessionTime: Int, shortBreakTime: Int, longBreakT }, imageVector = Icons.Filled.MoreVert, contentDescription = "Task Options", - tint = MaterialTheme.colorScheme.onPrimary + tint = MaterialTheme.colorScheme.onPrimary, ) } if (task.description != null) { @@ -441,10 +451,10 @@ fun BasicTask(hourFormat: Int, sessionTime: Int, shortBreakTime: Int, longBreakT text = task.description, style = MaterialTheme.typography.bodySmall.copy( color = MaterialTheme.colorScheme.onPrimary, - fontSize = 12.sp + fontSize = 12.sp, ), maxLines = 1, - overflow = TextOverflow.Ellipsis + overflow = TextOverflow.Ellipsis, ) } @@ -452,7 +462,7 @@ fun BasicTask(hourFormat: Int, sessionTime: Int, shortBreakTime: Int, longBreakT modifier = Modifier .fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Text( text = "${ @@ -460,30 +470,30 @@ fun BasicTask(hourFormat: Int, sessionTime: Int, shortBreakTime: Int, longBreakT focusSessions = task.focusSessions, sessionTime = sessionTime, shortBreakTime = shortBreakTime, - longBreakTime = longBreakTime + longBreakTime = longBreakTime, ) } minutes", style = MaterialTheme.typography.displaySmall.copy( color = MaterialTheme.colorScheme.onPrimary, fontWeight = FontWeight.SemiBold, - fontSize = 10.sp - ) + fontSize = 10.sp, + ), ) Text( modifier = Modifier.fillMaxWidth(), text = prettyTimeDifference( start = task.start, end = end, - timeFormat = hourFormat + timeFormat = hourFormat, ), style = MaterialTheme.typography.bodySmall.copy( color = MaterialTheme.colorScheme.onPrimary, fontWeight = FontWeight.SemiBold, - fontSize = 10.sp + fontSize = 10.sp, ), maxLines = 1, overflow = TextOverflow.Clip, - textAlign = TextAlign.End + textAlign = TextAlign.End, ) } } @@ -498,8 +508,8 @@ fun BasicSidebarLabel(hourFormat: Int, time: LocalTime, modifier: Modifier = Mod .fillMaxHeight() .padding(4.dp), style = MaterialTheme.typography.labelMedium.copy( - fontSize = 12.sp - ) + fontSize = 12.sp, + ), ) } @@ -513,9 +523,9 @@ fun ScheduleSidebar( label: @Composable (hourFormat: Int, time: LocalTime) -> Unit = { _, time -> BasicSidebarLabel( hourFormat = hourFormat, - time = time + time = time, ) - } + }, ) { val numMinutes = differenceBetweenMinutes(minTime, maxTime) + 1 val numHours = numMinutes / 60 @@ -530,7 +540,7 @@ fun ScheduleSidebar( Box(modifier = Modifier.height(hourHeight)) { label( hourFormat, - startTime.plusHours(i) + startTime.plusHours(i), ) } } @@ -548,7 +558,7 @@ fun Schedule( verticalScrollState: ScrollState, modifier: Modifier = Modifier, taskContent: @Composable ( - positionedTask: PositionedTask + positionedTask: PositionedTask, ) -> Unit = { positionedTask -> BasicTask( hourFormat = hourFormat, @@ -556,13 +566,13 @@ fun Schedule( sessionTime = sessionTime, shortBreakTime = shortBreakTime, longBreakTime = longBreakTime, - onShowTaskOption = onShowTaskOption + onShowTaskOption = onShowTaskOption, ) }, timeLabel: @Composable (hourFormat: Int, time: LocalTime) -> Unit = { hrFormat, time -> BasicSidebarLabel( hourFormat = hrFormat, - time = time + time = time, ) }, minDate: LocalDate = tasks.minByOrNull(Task::start)?.start?.date ?: Clock.System.now() @@ -574,13 +584,13 @@ fun Schedule( focusSessions = it.focusSessions, sessionTime = sessionTime, shortBreakTime = shortBreakTime, - longBreakTime = longBreakTime + longBreakTime = longBreakTime, ) }.maxOfOrNull { it.date } ?: today().date, minTime: LocalTime = min(), maxTime: LocalTime = max(), daySize: ScheduleSize, - hourSize: ScheduleSize + hourSize: ScheduleSize, ) { val numDays = 0 + 1 val numMinutes = differenceBetweenMinutes(minTime, maxTime) + 1 @@ -645,7 +655,7 @@ fun Schedule( is ScheduleSize.Adaptive -> with(LocalDensity.current) { maxOf( ((constraints.maxWidth - sidebarWidth) / numDays).toDp(), - daySize.minSize + daySize.minSize, ) } } @@ -655,7 +665,7 @@ fun Schedule( is ScheduleSize.Adaptive -> with(LocalDensity.current) { maxOf( ((constraints.maxHeight) / numHours).toDp(), - hourSize.minSize + hourSize.minSize, ) } } @@ -663,7 +673,7 @@ fun Schedule( Row( modifier = Modifier .weight(1f) - .align(Alignment.Start) + .align(Alignment.Start), ) { ScheduleSidebar( hourFormat = hourFormat, @@ -673,7 +683,7 @@ fun Schedule( label = timeLabel, modifier = Modifier .verticalScroll(verticalScrollState) - .onGloballyPositioned { sidebarWidth = it.size.width } + .onGloballyPositioned { sidebarWidth = it.size.width }, ) BasicSchedule( hourFormat = hourFormat, @@ -691,7 +701,7 @@ fun Schedule( onShowTaskOption = onShowTaskOption, modifier = Modifier .weight(1f) - .verticalScroll(verticalScrollState) + .verticalScroll(verticalScrollState), ) } } @@ -714,7 +724,7 @@ fun BasicSchedule( sessionTime = sessionTime, shortBreakTime = shortBreakTime, longBreakTime = longBreakTime, - onShowTaskOption = onShowTaskOption + onShowTaskOption = onShowTaskOption, ) }, minDate: LocalDate = tasks.minByOrNull(Task::start)?.start?.date ?: Clock.System.now() @@ -724,13 +734,13 @@ fun BasicSchedule( focusSessions = it.focusSessions, sessionTime = sessionTime, shortBreakTime = shortBreakTime, - longBreakTime = longBreakTime + longBreakTime = longBreakTime, ) }.maxOfOrNull { it.date } ?: today().date, minTime: LocalTime = min(), maxTime: LocalTime = max(), dayWidth: Dp, - hourHeight: Dp + hourHeight: Dp, ) { val numDays = differenceBetweenDays(minDate, maxDate) + 1 val numMinutes = differenceBetweenMinutes(minTime, maxTime) + 1 @@ -743,8 +753,8 @@ fun BasicSchedule( tasks = tasks.sortedBy(Task::start), sessionTime = sessionTime, shortBreakTime = shortBreakTime, - longBreakTime = longBreakTime - ) + longBreakTime = longBreakTime, + ), ).filter { it.end > minTime && it.start < maxTime } } Layout( @@ -763,10 +773,10 @@ fun BasicSchedule( dividerColor, start = Offset(0f, (it + 1) * hourHeight.toPx()), end = Offset(size.width, (it + 1) * hourHeight.toPx()), - strokeWidth = 1.dp.toPx() + strokeWidth = 1.dp.toPx(), ) } - } + }, ) { measureables, constraints -> val height = (hourHeight.toPx() * (numMinutes / 60f)).roundToInt() val width = dayWidth.roundToPx() * numDays @@ -782,8 +792,8 @@ fun BasicSchedule( minWidth = taskWidth, maxWidth = taskWidth, minHeight = taskHeight, - maxHeight = taskHeight - ) + maxHeight = taskHeight, + ), ) Pair(placeable, splitTask) } @@ -792,7 +802,7 @@ fun BasicSchedule( val taskOffsetMinutes = if (splitTask.start > minTime) { differenceBetweenMinutes( minTime, - splitTask.start + splitTask.start, ) } else { 0 diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/calendar/CalendarScreenModel.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/calendar/CalendarScreenModel.kt index 79ff600..67e1320 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/calendar/CalendarScreenModel.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/calendar/CalendarScreenModel.kt @@ -16,7 +16,7 @@ package com.joelkanyi.focusbloom.feature.calendar import cafe.adriel.voyager.core.model.ScreenModel -import cafe.adriel.voyager.core.model.coroutineScope +import cafe.adriel.voyager.core.model.screenModelScope import com.joelkanyi.focusbloom.core.domain.model.Task import com.joelkanyi.focusbloom.core.domain.repository.settings.SettingsRepository import com.joelkanyi.focusbloom.core.domain.repository.tasks.TasksRepository @@ -33,19 +33,19 @@ import kotlinx.datetime.toLocalDateTime class CalendarScreenModel( private val tasksRepository: TasksRepository, - settingsRepository: SettingsRepository + settingsRepository: SettingsRepository, ) : ScreenModel { private val _selectedDay = MutableStateFlow( Clock.System.now().toLocalDateTime( - TimeZone.currentSystemDefault() - ).date + TimeZone.currentSystemDefault(), + ).date, ) val selectedDay = _selectedDay.stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(5_000), initialValue = Clock.System.now().toLocalDateTime( - TimeZone.currentSystemDefault() - ).date + TimeZone.currentSystemDefault(), + ).date, ) fun setSelectedDay(date: kotlinx.datetime.LocalDate) { @@ -59,17 +59,17 @@ class CalendarScreenModel( } } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(), - initialValue = emptyList() + initialValue = emptyList(), ) val hourFormat = settingsRepository.getHourFormat() .map { it } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = null + initialValue = null, ) val sessionTime = settingsRepository.getSessionTime() @@ -77,23 +77,23 @@ class CalendarScreenModel( it } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = null + initialValue = null, ) val shortBreakTime = settingsRepository.getShortBreakTime() .map { it } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = null + initialValue = null, ) val longBreakTime = settingsRepository.getLongBreakTime() .map { it } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = null + initialValue = null, ) private val _selectedTask = MutableStateFlow(null) @@ -109,35 +109,35 @@ class CalendarScreenModel( } fun pushToTomorrow(task: Task) { - coroutineScope.launch { + screenModelScope.launch { tasksRepository.updateTask( task.copy( date = task.date.plusDays(1), - start = task.start.plusDays(1) - ) + start = task.start.plusDays(1), + ), ) } } fun markAsCompleted(task: Task) { - coroutineScope.launch { + screenModelScope.launch { tasksRepository.updateTaskCompleted( id = task.id, - completed = true + completed = true, ) tasksRepository.updateTaskActive( id = task.id, - active = false + active = false, ) tasksRepository.updateTaskInProgress( id = task.id, - inProgressTask = false + inProgressTask = false, ) } } fun deleteTask(task: Task) { - coroutineScope.launch { + screenModelScope.launch { tasksRepository.deleteTask(task.id) } } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/home/AllTasksScreen.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/home/AllTasksScreen.kt index 02ece1b..7bd838c 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/home/AllTasksScreen.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/home/AllTasksScreen.kt @@ -46,20 +46,21 @@ import com.joelkanyi.focusbloom.core.presentation.component.TaskCard import com.joelkanyi.focusbloom.feature.home.component.TaskOptionsBottomSheet import com.joelkanyi.focusbloom.feature.taskprogress.TaskProgressScreen import com.joelkanyi.focusbloom.platform.StatusBarColors +import org.koin.compose.rememberKoinInject import org.koin.core.component.KoinComponent import org.koin.core.component.get data class AllTasksScreen( - val type: String + val type: String, ) : Screen, KoinComponent { @OptIn(ExperimentalMaterial3Api::class) @Composable override fun Content() { - val screenModel = get() + val screenModel = rememberKoinInject() StatusBarColors( statusBarColor = MaterialTheme.colorScheme.background, - navBarColor = MaterialTheme.colorScheme.background + navBarColor = MaterialTheme.colorScheme.background, ) val navigator = LocalNavigator.currentOrThrow val tasksState = screenModel.tasks.collectAsState().value @@ -97,11 +98,11 @@ data class AllTasksScreen( screenModel.markAsCompleted(it) }, onClickEditTask = { - /*tabNavigator.current = BloomTab.AddTaskTab( - taskId = it.id - )*/ + /*tabNavigator.current = BloomTab.AddTaskTab( + taskId = it.id + )*/ }, - task = selectedTask + task = selectedTask, ) } } @@ -122,21 +123,31 @@ data class AllTasksScreen( }, onClickTask = { navigator.push(TaskProgressScreen(taskId = it.id)) - } + }, ) } } @OptIn(ExperimentalMaterial3Api::class) @Composable -fun AllTasksScreenContent(tasksState: TasksState, timeFormat: Int, sessionTime: Int, shortBreakTime: Int, longBreakTime: Int, onClickNavigateBack: () -> Unit, onClickTaskOptions: (task: Task) -> Unit, onClickTask: (task: Task) -> Unit, type: String) { +fun AllTasksScreenContent( + tasksState: TasksState, + timeFormat: Int, + sessionTime: Int, + shortBreakTime: Int, + longBreakTime: Int, + onClickNavigateBack: () -> Unit, + onClickTaskOptions: (task: Task) -> Unit, + onClickTask: (task: Task) -> Unit, + type: String, +) { Box( - modifier = Modifier.fillMaxSize() + modifier = Modifier.fillMaxSize(), ) { when (tasksState) { is TasksState.Loading -> { CircularProgressIndicator( - modifier = Modifier.align(Alignment.Center) + modifier = Modifier.align(Alignment.Center), ) } @@ -151,10 +162,10 @@ fun AllTasksScreenContent(tasksState: TasksState, timeFormat: Int, sessionTime: IconButton(onClick = onClickNavigateBack) { Icon( imageVector = Icons.Outlined.ArrowBack, - contentDescription = "Back" + contentDescription = "Back", ) } - } + }, ) { Text( text = "${ @@ -162,21 +173,21 @@ fun AllTasksScreenContent(tasksState: TasksState, timeFormat: Int, sessionTime: } Tasks (${ if (type == "today") tasks.size else overdueTasks.size })", - color = if (type == "today") MaterialTheme.colorScheme.onBackground else MaterialTheme.colorScheme.error + color = if (type == "today") MaterialTheme.colorScheme.onBackground else MaterialTheme.colorScheme.error, ) } - } + }, ) { paddingValues -> LazyColumn( modifier = Modifier .padding(paddingValues) .fillMaxSize(), contentPadding = PaddingValues(horizontal = 16.dp), - verticalArrangement = Arrangement.spacedBy(12.dp) + verticalArrangement = Arrangement.spacedBy(12.dp), ) { items( items = if (type == "today") tasks else overdueTasks, - key = { it.id } + key = { it.id }, ) { TaskCard( type = type, @@ -187,7 +198,7 @@ fun AllTasksScreenContent(tasksState: TasksState, timeFormat: Int, sessionTime: focusSessions = it.focusSessions, sessionTime = sessionTime, shortBreakTime = shortBreakTime, - longBreakTime = longBreakTime + longBreakTime = longBreakTime, ) } } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/home/HomeScreen.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/home/HomeScreen.kt index 0311f9d..40309fe 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/home/HomeScreen.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/home/HomeScreen.kt @@ -55,6 +55,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.tab.LocalTabNavigator import com.joelkanyi.focusbloom.core.domain.model.SessionType @@ -65,7 +66,6 @@ import com.joelkanyi.focusbloom.core.presentation.component.TaskProgress import com.joelkanyi.focusbloom.core.presentation.theme.LongBreakColor import com.joelkanyi.focusbloom.core.presentation.theme.SessionColor import com.joelkanyi.focusbloom.core.presentation.theme.ShortBreakColor -import com.joelkanyi.focusbloom.core.utils.LocalAppNavigator import com.joelkanyi.focusbloom.core.utils.pickFirstName import com.joelkanyi.focusbloom.core.utils.sessionType import com.joelkanyi.focusbloom.core.utils.taskCompleteMessage @@ -77,18 +77,20 @@ import com.joelkanyi.focusbloom.feature.taskprogress.TaskProgressScreen import com.joelkanyi.focusbloom.feature.taskprogress.Timer import com.joelkanyi.focusbloom.feature.taskprogress.TimerState import com.joelkanyi.focusbloom.platform.StatusBarColors -import org.jetbrains.compose.resources.ExperimentalResourceApi +import focusbloom.shared.generated.resources.Res +import focusbloom.shared.generated.resources.il_completed +import focusbloom.shared.generated.resources.il_empty import org.jetbrains.compose.resources.painterResource -import org.koin.compose.rememberKoinInject +import org.koin.compose.koinInject @OptIn(ExperimentalMaterial3Api::class) @Composable -fun HomeScreen() { - val screenModel: HomeScreenModel = rememberKoinInject() - +fun HomeScreen( + screenModel: HomeScreenModel = koinInject(), +) { StatusBarColors( statusBarColor = MaterialTheme.colorScheme.background, - navBarColor = MaterialTheme.colorScheme.background + navBarColor = MaterialTheme.colorScheme.background, ) val tasksState = screenModel.tasks.collectAsState().value val username = screenModel.username.collectAsState().value ?: "" @@ -96,7 +98,8 @@ fun HomeScreen() { val sessionTime = screenModel.sessionTime.collectAsState().value ?: 25 val shortBreakTime = screenModel.shortBreakTime.collectAsState().value ?: 5 val longBreakTime = screenModel.longBreakTime.collectAsState().value ?: 15 - val navigator = LocalAppNavigator.currentOrThrow + val navigator = LocalNavigator.currentOrThrow + val tabNavigator = LocalTabNavigator.current val selectedTask = screenModel.selectedTask.collectAsState().value val openBottomSheet = screenModel.openBottomSheet.collectAsState().value val shortBreakColor = screenModel.shortBreakColor.collectAsState().value @@ -105,7 +108,6 @@ fun HomeScreen() { val timerState = Timer.timerState.collectAsState().value val tickingTime = Timer.tickingTime.collectAsState().value val bottomSheetState = rememberModalBottomSheetState() - val tabNavigator = LocalTabNavigator.current val remindersOn = screenModel.remindersOn.collectAsState().value LaunchedEffect(Unit) { @@ -150,7 +152,7 @@ fun HomeScreen() { onClickEditTask = { tabNavigator.current = BloomTab.AddTaskTab(taskId = it.id) }, - task = selectedTask + task = selectedTask, ) } } @@ -172,11 +174,11 @@ fun HomeScreen() { screenModel.selectTask(it) screenModel.openBottomSheet(true) } else { - navigator.push(TaskProgressScreen(taskId = it.id)) + navigator.parent?.push(TaskProgressScreen(taskId = it.id)) } }, onClickSeeAllTasks = { - navigator.push(AllTasksScreen(it)) + navigator.parent?.push(AllTasksScreen(it)) }, onClickTaskOptions = { screenModel.selectTask(it) @@ -194,11 +196,10 @@ fun HomeScreen() { else -> {} } - } + }, ) } -@OptIn(ExperimentalResourceApi::class) @Composable private fun HomeScreenContent( tasksState: TasksState, @@ -215,18 +216,18 @@ private fun HomeScreenContent( focusTimeColor: Long?, shortBreakColor: Long?, longBreakColor: Long?, - onClickActiveTaskOptions: (task: Task) -> Unit + onClickActiveTaskOptions: (task: Task) -> Unit, ) { Scaffold { paddingValues -> Box( modifier = Modifier .padding(paddingValues) - .fillMaxSize() + .fillMaxSize(), ) { when (tasksState) { TasksState.Loading -> { CircularProgressIndicator( - modifier = Modifier.align(Alignment.Center) + modifier = Modifier.align(Alignment.Center), ) } @@ -236,7 +237,7 @@ private fun HomeScreenContent( val activeTask = tasks.firstOrNull { it.active } LazyColumn( contentPadding = PaddingValues(horizontal = 16.dp), - verticalArrangement = Arrangement.spacedBy(8.dp) + verticalArrangement = Arrangement.spacedBy(8.dp), ) { item { if (activeTask != null) { @@ -246,7 +247,7 @@ private fun HomeScreenContent( Color(SessionColor) } else { Color( - focusTimeColor + focusTimeColor, ) } } @@ -256,7 +257,7 @@ private fun HomeScreenContent( Color(LongBreakColor) } else { Color( - longBreakColor + longBreakColor, ) } } @@ -266,7 +267,7 @@ private fun HomeScreenContent( Color(ShortBreakColor) } else { Color( - shortBreakColor + shortBreakColor, ) } } @@ -277,14 +278,14 @@ private fun HomeScreenContent( containerColor = containerColor, onClickTaskOptions = onClickActiveTaskOptions, timerState = timerState, - tickingTime = tickingTime + tickingTime = tickingTime, ) } } item { Text( text = "Hello, ${username.pickFirstName()}!", - style = MaterialTheme.typography.displaySmall + style = MaterialTheme.typography.displaySmall, ) } item { @@ -297,14 +298,14 @@ private fun HomeScreenContent( Row( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween + horizontalArrangement = Arrangement.SpaceBetween, ) { Text( text = "Overdue Tasks (${overdueTasks.size})", style = MaterialTheme.typography.titleLarge.copy( fontWeight = FontWeight.Bold, - color = MaterialTheme.colorScheme.error - ) + color = MaterialTheme.colorScheme.error, + ), ) if (overdueTasks.size > 3) { Text( @@ -315,8 +316,8 @@ private fun HomeScreenContent( style = MaterialTheme.typography.labelLarge.copy( fontWeight = FontWeight.SemiBold, color = MaterialTheme.colorScheme.error, - fontSize = 16.sp - ) + fontSize = 16.sp, + ), ) } } @@ -324,7 +325,7 @@ private fun HomeScreenContent( items( items = tasksState.overdueTasks.take(3), - key = { it.id } + key = { it.id }, ) { TaskCard( type = "overdue", @@ -335,7 +336,7 @@ private fun HomeScreenContent( focusSessions = it.focusSessions, sessionTime = sessionTime, shortBreakTime = shortBreakTime, - longBreakTime = longBreakTime + longBreakTime = longBreakTime, ) } } @@ -345,13 +346,13 @@ private fun HomeScreenContent( Row( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween + horizontalArrangement = Arrangement.SpaceBetween, ) { Text( text = "Today's Tasks (${tasks.size})", style = MaterialTheme.typography.titleLarge.copy( - fontWeight = FontWeight.Bold - ) + fontWeight = FontWeight.Bold, + ), ) if (tasks.size > 3) { Text( @@ -362,8 +363,8 @@ private fun HomeScreenContent( style = MaterialTheme.typography.labelLarge.copy( fontWeight = FontWeight.SemiBold, color = MaterialTheme.colorScheme.primary, - fontSize = 16.sp - ) + fontSize = 16.sp, + ), ) } } @@ -372,7 +373,7 @@ private fun HomeScreenContent( if (tasks.all { it.completed }.not()) { items( items = tasks.take(3), - key = { it.id } + key = { it.id }, ) { TaskCard( type = "today", @@ -383,7 +384,7 @@ private fun HomeScreenContent( focusSessions = it.focusSessions, sessionTime = sessionTime, shortBreakTime = shortBreakTime, - longBreakTime = longBreakTime + longBreakTime = longBreakTime, ) } } @@ -393,17 +394,15 @@ private fun HomeScreenContent( Column( modifier = Modifier .fillMaxWidth(), - horizontalAlignment = CenterHorizontally + horizontalAlignment = CenterHorizontally, ) { Spacer(modifier = Modifier.height(24.dp)) Image( modifier = Modifier .size(300.dp) .align(CenterHorizontally), - painter = painterResource( - if (tasks.isEmpty()) "il_empty.xml" else "il_completed.xml" - ), - contentDescription = null + painter = painterResource(if (tasks.isEmpty()) Res.drawable.il_empty else Res.drawable.il_completed), + contentDescription = null, ) Spacer(modifier = Modifier.height(24.dp)) Text( @@ -412,7 +411,7 @@ private fun HomeScreenContent( .align(CenterHorizontally), style = MaterialTheme.typography.titleSmall.copy( fontSize = 14.sp, - fontWeight = FontWeight.Bold + fontWeight = FontWeight.Bold, ), text = if (tasks.isEmpty()) { "Start your day productively! Add your first task." @@ -421,7 +420,7 @@ private fun HomeScreenContent( } else { "" }, - textAlign = TextAlign.Center + textAlign = TextAlign.Center, ) Spacer(modifier = Modifier.height(8.dp)) Text( @@ -436,9 +435,9 @@ private fun HomeScreenContent( "" }, style = MaterialTheme.typography.labelLarge.copy( - fontSize = 14.sp + fontSize = 14.sp, ), - textAlign = TextAlign.Center + textAlign = TextAlign.Center, ) } } @@ -452,63 +451,70 @@ private fun HomeScreenContent( @OptIn(ExperimentalMaterial3Api::class) @Composable -fun ActiveTaskCard(task: Task, timerState: TimerState, tickingTime: Long, onClick: (task: Task) -> Unit, onClickTaskOptions: (task: Task) -> Unit, containerColor: Color) { +fun ActiveTaskCard( + task: Task, + timerState: TimerState, + tickingTime: Long, + onClick: (task: Task) -> Unit, + onClickTaskOptions: (task: Task) -> Unit, + containerColor: Color, +) { Card( onClick = { onClick(task) }, colors = CardDefaults.cardColors( - containerColor = containerColor - ) + containerColor = containerColor, + ), ) { Row( modifier = Modifier .padding(horizontal = 12.dp, vertical = 8.dp), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween + horizontalArrangement = Arrangement.SpaceBetween, ) { Column( modifier = Modifier .fillMaxWidth(.85f), - verticalArrangement = Arrangement.spacedBy(6.dp) + verticalArrangement = Arrangement.spacedBy(6.dp), ) { Text( text = task.name, style = MaterialTheme.typography.titleSmall.copy( fontWeight = FontWeight.SemiBold, - color = MaterialTheme.colorScheme.onPrimary + color = MaterialTheme.colorScheme.onPrimary, ), maxLines = 1, - overflow = TextOverflow.Ellipsis + overflow = TextOverflow.Ellipsis, ) Text( text = "${ - when (task.current.sessionType()) { - SessionType.Focus -> { - "Focus Session" - } + when (task.current.sessionType()) { + SessionType.Focus -> { + "Focus Session" + } - SessionType.ShortBreak -> { - "Short Break" - } + SessionType.ShortBreak -> { + "Short Break" + } - SessionType.LongBreak -> { - "Long Break" + SessionType.LongBreak -> { + "Long Break" + } } - } } - ${ - tickingTime.toTimer() + tickingTime.toTimer() }", style = MaterialTheme.typography.labelMedium.copy( color = MaterialTheme.colorScheme.onPrimary, - fontWeight = FontWeight.SemiBold - ) + fontWeight = FontWeight.SemiBold, + ), ) } IconButton( onClick = { onClickTaskOptions(task) - } + }, ) { Icon( modifier = Modifier, @@ -526,7 +532,7 @@ fun ActiveTaskCard(task: Task, timerState: TimerState, tickingTime: Long, onClic } }, contentDescription = "Play/Pause", - tint = MaterialTheme.colorScheme.onPrimary + tint = MaterialTheme.colorScheme.onPrimary, ) } } @@ -541,29 +547,29 @@ private fun TodayTaskProgressCard(tasks: List) { .fillMaxWidth() .padding(12.dp), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(12.dp) + horizontalArrangement = Arrangement.spacedBy(12.dp), ) { TaskProgress( mainColor = MaterialTheme.colorScheme.primary, percentage = taskCompletionPercentage(tasks).toFloat(), - counterColor = MaterialTheme.colorScheme.onSurface + counterColor = MaterialTheme.colorScheme.onSurface, ) Column( - verticalArrangement = Arrangement.spacedBy(4.dp) + verticalArrangement = Arrangement.spacedBy(4.dp), ) { Text( text = taskCompleteMessage(tasks), style = MaterialTheme.typography.headlineSmall.copy( - fontWeight = FontWeight.SemiBold - ) + fontWeight = FontWeight.SemiBold, + ), ) Spacer(modifier = Modifier.height(4.dp)) Text( text = "${tasks.filter { it.completed }.size} of ${tasks.size} tasks completed", style = MaterialTheme.typography.labelMedium.copy( - color = MaterialTheme.colorScheme.onSurface - ) + color = MaterialTheme.colorScheme.onSurface, + ), ) } } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/home/HomeScreenModel.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/home/HomeScreenModel.kt index 85d11f6..b1defcc 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/home/HomeScreenModel.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/home/HomeScreenModel.kt @@ -16,7 +16,7 @@ package com.joelkanyi.focusbloom.feature.home import cafe.adriel.voyager.core.model.ScreenModel -import cafe.adriel.voyager.core.model.coroutineScope +import cafe.adriel.voyager.core.model.screenModelScope import com.joelkanyi.focusbloom.core.domain.model.Task import com.joelkanyi.focusbloom.core.domain.repository.settings.SettingsRepository import com.joelkanyi.focusbloom.core.domain.repository.tasks.TasksRepository @@ -34,7 +34,7 @@ import kotlinx.datetime.toLocalDateTime class HomeScreenModel( private val tasksRepository: TasksRepository, - private val settingsRepository: SettingsRepository + private val settingsRepository: SettingsRepository, ) : ScreenModel { private val _openBottomSheet = MutableStateFlow(false) val openBottomSheet = _openBottomSheet.asStateFlow() @@ -45,9 +45,9 @@ class HomeScreenModel( val hourFormat = settingsRepository.getHourFormat() .map { it ?: 24 } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = 24 + initialValue = 24, ) val sessionTime = settingsRepository.getSessionTime() @@ -55,23 +55,23 @@ class HomeScreenModel( it } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = null + initialValue = null, ) val shortBreakTime = settingsRepository.getShortBreakTime() .map { it } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = null + initialValue = null, ) val longBreakTime = settingsRepository.getLongBreakTime() .map { it } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = null + initialValue = null, ) val remindersOn = settingsRepository.remindersOn() @@ -79,19 +79,19 @@ class HomeScreenModel( ReminderState.Success(it) } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = ReminderState.Loading + initialValue = ReminderState.Loading, ) fun deleteTask(task: Task) { - coroutineScope.launch { + screenModelScope.launch { tasksRepository.deleteTask(task.id) } } fun updateTask(task: Task) { - coroutineScope.launch { + screenModelScope.launch { tasksRepository.updateTask(task) } } @@ -103,40 +103,40 @@ class HomeScreenModel( } fun pushToTomorrow(task: Task) { - coroutineScope.launch { + screenModelScope.launch { tasksRepository.updateTask( task.copy( date = task.date.plusDays(1), - start = task.start.plusDays(1) - ) + start = task.start.plusDays(1), + ), ) } } fun pushToToday(task: Task) { - coroutineScope.launch { + screenModelScope.launch { tasksRepository.updateTask( task.copy( date = today(), - start = today() - ) + start = today(), + ), ) } } fun markAsCompleted(task: Task) { - coroutineScope.launch { + screenModelScope.launch { tasksRepository.updateTaskCompleted( id = task.id, - completed = true + completed = true, ) tasksRepository.updateTaskActive( id = task.id, - active = false + active = false, ) tasksRepository.updateTaskInProgress( id = task.id, - inProgressTask = false + inProgressTask = false, ) } } @@ -156,49 +156,49 @@ class HomeScreenModel( it.date.date < Clock.System.now() .toLocalDateTime(TimeZone.currentSystemDefault()).date && !it.completed - } + }, ) } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = TasksState.Loading + initialValue = TasksState.Loading, ) val username = settingsRepository.getUsername() .map { it } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = null + initialValue = null, ) val shortBreakColor = settingsRepository.shortBreakColor() .map { it } .stateIn( - coroutineScope, + screenModelScope, started = SharingStarted.WhileSubscribed(), - initialValue = null + initialValue = null, ) val longBreakColor = settingsRepository.longBreakColor() .map { it } .stateIn( - coroutineScope, + screenModelScope, started = SharingStarted.WhileSubscribed(), - initialValue = null + initialValue = null, ) val focusColor = settingsRepository.focusColor() .map { it } .stateIn( - coroutineScope, + screenModelScope, started = SharingStarted.WhileSubscribed(), - initialValue = null + initialValue = null, ) fun toggleReminder(value: Int) { - coroutineScope.launch { + screenModelScope.launch { settingsRepository.toggleReminder(value) } } @@ -208,7 +208,7 @@ sealed class TasksState { data object Loading : TasksState() data class Success( val tasks: List, - val overdueTasks: List + val overdueTasks: List, ) : TasksState() } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/home/component/Option.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/home/component/Option.kt index 193fa29..003896b 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/home/component/Option.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/home/component/Option.kt @@ -39,21 +39,21 @@ fun Option(icon: ImageVector, text: String, onClick: () -> Unit) { .fillMaxWidth() .clickable { onClick() }, verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(12.dp) + horizontalArrangement = Arrangement.spacedBy(12.dp), ) { Icon( modifier = Modifier, imageVector = icon, contentDescription = text, - tint = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f) + tint = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f), ) Text( modifier = Modifier, text = text, style = MaterialTheme.typography.labelMedium.copy( fontWeight = FontWeight.SemiBold, - fontSize = 16.sp - ) + fontSize = 16.sp, + ), ) } } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/home/component/TaskOptionsBottomSheet.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/home/component/TaskOptionsBottomSheet.kt index 4d71230..353f48e 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/home/component/TaskOptionsBottomSheet.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/home/component/TaskOptionsBottomSheet.kt @@ -35,13 +35,24 @@ import com.joelkanyi.focusbloom.core.domain.model.Task @OptIn(ExperimentalMaterial3Api::class) @Composable -fun TaskOptionsBottomSheet(bottomSheetState: SheetState, onClickCancel: (task: Task) -> Unit, onClickDelete: (task: Task) -> Unit, onClickPushToTomorrow: (task: Task) -> Unit, task: Task, onDismissRequest: () -> Unit, onClickMarkAsCompleted: (task: Task) -> Unit, onClickEditTask: (task: Task) -> Unit, type: String, onClickPushToToday: (task: Task) -> Unit) { +fun TaskOptionsBottomSheet( + bottomSheetState: SheetState, + onClickCancel: (task: Task) -> Unit, + onClickDelete: (task: Task) -> Unit, + onClickPushToTomorrow: (task: Task) -> Unit, + task: Task, + onDismissRequest: () -> Unit, + onClickMarkAsCompleted: (task: Task) -> Unit, + onClickEditTask: (task: Task) -> Unit, + type: String, + onClickPushToToday: (task: Task) -> Unit, +) { ModalBottomSheet( onDismissRequest = onDismissRequest, - sheetState = bottomSheetState + sheetState = bottomSheetState, ) { Column( - verticalArrangement = Arrangement.spacedBy(24.dp) + verticalArrangement = Arrangement.spacedBy(24.dp), ) { Option( icon = Icons.Default.Edit, @@ -49,7 +60,7 @@ fun TaskOptionsBottomSheet(bottomSheetState: SheetState, onClickCancel: (task: T onClick = { onClickEditTask(task) onDismissRequest() - } + }, ) Option( icon = Icons.Outlined.EditCalendar, @@ -57,7 +68,7 @@ fun TaskOptionsBottomSheet(bottomSheetState: SheetState, onClickCancel: (task: T onClick = { if (type == "overdue") onClickPushToToday(task) else onClickPushToTomorrow(task) onDismissRequest() - } + }, ) Option( icon = Icons.Outlined.Done, @@ -65,7 +76,7 @@ fun TaskOptionsBottomSheet(bottomSheetState: SheetState, onClickCancel: (task: T onClick = { onClickMarkAsCompleted(task) onDismissRequest() - } + }, ) Option( icon = Icons.Outlined.Delete, @@ -73,7 +84,7 @@ fun TaskOptionsBottomSheet(bottomSheetState: SheetState, onClickCancel: (task: T onClick = { onClickDelete(task) onDismissRequest() - } + }, ) Option( icon = Icons.Outlined.Close, @@ -81,7 +92,7 @@ fun TaskOptionsBottomSheet(bottomSheetState: SheetState, onClickCancel: (task: T onClick = { onClickCancel(task) onDismissRequest() - } + }, ) Spacer(modifier = Modifier.height(32.dp)) } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/onboarding/OnboadingViewModel.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/onboarding/OnboadingViewModel.kt index ab66fd6..a54f5a3 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/onboarding/OnboadingViewModel.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/onboarding/OnboadingViewModel.kt @@ -16,7 +16,7 @@ package com.joelkanyi.focusbloom.feature.onboarding import cafe.adriel.voyager.core.model.ScreenModel -import cafe.adriel.voyager.core.model.coroutineScope +import cafe.adriel.voyager.core.model.screenModelScope import com.joelkanyi.focusbloom.core.domain.repository.settings.SettingsRepository import com.joelkanyi.focusbloom.core.utils.UiEvents import kotlinx.coroutines.channels.Channel @@ -26,7 +26,7 @@ import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch class OnboadingViewModel( - private val settingsRepository: SettingsRepository + private val settingsRepository: SettingsRepository, ) : ScreenModel { private val _eventsFlow = Channel(Channel.UNLIMITED) val eventsFlow = _eventsFlow.receiveAsFlow() @@ -38,7 +38,7 @@ class OnboadingViewModel( } fun saveUsername() { - coroutineScope.launch { + screenModelScope.launch { settingsRepository.saveUsername(username.value.trim()) settingsRepository.toggleReminder(1) _eventsFlow.send(UiEvents.Navigation) @@ -58,6 +58,6 @@ class OnboadingViewModel( "be disciplined", "be motivated", "be consistent", - "be mindful" + "be mindful", ) } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/onboarding/OnboardingScreen.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/onboarding/OnboardingScreen.kt index ebb5372..357e0be 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/onboarding/OnboardingScreen.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/onboarding/OnboardingScreen.kt @@ -39,7 +39,6 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -51,7 +50,12 @@ import androidx.compose.ui.unit.sp import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import focusbloom.shared.generated.resources.Res +import focusbloom.shared.generated.resources.il_statistics +import focusbloom.shared.generated.resources.il_tasks +import focusbloom.shared.generated.resources.il_work_time import kotlinx.coroutines.launch +import org.jetbrains.compose.resources.DrawableResource import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.painterResource import org.koin.core.component.KoinComponent @@ -75,39 +79,44 @@ class OnboardingScreen : Screen, KoinComponent { }, onClickGetStarted = { navigator.push(UsernameScreen()) - } + }, ) } } @OptIn(ExperimentalFoundationApi::class) @Composable -fun OnboardingScreenContent(pageCount: Int, pagerState: PagerState, onClickNext: () -> Unit, onClickGetStarted: () -> Unit) { +fun OnboardingScreenContent( + pageCount: Int, + pagerState: PagerState, + onClickNext: () -> Unit, + onClickGetStarted: () -> Unit, +) { Scaffold( bottomBar = { if (pagerState.currentPage == pageCount - 1) { OnBoardingNavigationButton( modifier = Modifier.padding(16.dp), text = "Get Started", - onClick = onClickGetStarted + onClick = onClickGetStarted, ) } else { OnBoardingNavigationButton( modifier = Modifier.padding(16.dp), text = "Next", - onClick = onClickNext + onClick = onClickNext, ) } - } + }, ) { paddingValues -> Column( - modifier = Modifier.padding(paddingValues) + modifier = Modifier.padding(paddingValues), ) { HorizontalPager( modifier = Modifier .weight(.9f) .padding(16.dp), - state = pagerState + state = pagerState, ) { currentPage -> when (currentPage) { 0 -> OnboardingFirstPage() @@ -118,7 +127,7 @@ fun OnboardingScreenContent(pageCount: Int, pagerState: PagerState, onClickNext: PageIndicators( pageCount = pageCount, - currentPage = pagerState.currentPage + currentPage = pagerState.currentPage, ) } } @@ -130,7 +139,7 @@ private fun ColumnScope.PageIndicators(pageCount: Int, currentPage: Int) { Modifier .weight(.1f) .fillMaxWidth(), - horizontalArrangement = Arrangement.Center + horizontalArrangement = Arrangement.Center, ) { repeat(pageCount) { iteration -> val color = @@ -138,7 +147,7 @@ private fun ColumnScope.PageIndicators(pageCount: Int, currentPage: Int) { MaterialTheme.colorScheme.primary } else { MaterialTheme.colorScheme.onSurface.copy( - alpha = 0.2f + alpha = 0.2f, ) } Box( @@ -147,51 +156,54 @@ private fun ColumnScope.PageIndicators(pageCount: Int, currentPage: Int) { .clip(CircleShape) .background(color) .width(24.dp) - .height(8.dp) + .height(8.dp), ) } } } +@OptIn(ExperimentalResourceApi::class) @Composable private fun OnboardingFirstPage() { PageContent( title = "Organize Tasks and Boost Productivity", description = "Welcome to FocusBloom, your task management and productivity companion. Effortlessly organize your tasks and supercharge your productivity journey.", - illustration = "il_tasks.xml" + illustration = Res.drawable.il_tasks, ) } +@OptIn(ExperimentalResourceApi::class) @Composable private fun OnboardingSecondPage() { PageContent( title = "Tailor Your Work Sessions", description = "With FocusBloom, you have the power to customize your work and break durations to match your preferences and maximize efficiency.", - illustration = "il_work_time.xml" + illustration = Res.drawable.il_work_time, ) } +@OptIn(ExperimentalResourceApi::class) @Composable private fun OnboardingThirdPage() { PageContent( title = "Visualize Your Progress", description = "Experience the power of progress tracking with FocusBloom. Gain insights into your productivity journey and visualize task completion trends.", - illustration = "il_statistics.xml" + illustration = Res.drawable.il_statistics, ) } @OptIn(ExperimentalResourceApi::class) @Composable -private fun PageContent(title: String, description: String, illustration: String) { +private fun PageContent(title: String, description: String, illustration: DrawableResource) { Column( modifier = Modifier.fillMaxSize(), - horizontalAlignment = Alignment.CenterHorizontally + horizontalAlignment = Alignment.CenterHorizontally, ) { Image( painter = painterResource(illustration), - contentDescription = illustration, - modifier = Modifier.size(370.dp) + contentDescription = null, + modifier = Modifier.size(370.dp), ) Spacer(modifier = Modifier.height(32.dp)) Text( @@ -199,8 +211,8 @@ private fun PageContent(title: String, description: String, illustration: String text = title, style = MaterialTheme.typography.titleLarge.copy( fontSize = 22.sp, - textAlign = TextAlign.Center - ) + textAlign = TextAlign.Center, + ), ) Spacer(modifier = Modifier.height(16.dp)) Text( @@ -208,8 +220,8 @@ private fun PageContent(title: String, description: String, illustration: String text = description, style = MaterialTheme.typography.labelMedium.copy( fontSize = 14.sp, - textAlign = TextAlign.Center - ) + textAlign = TextAlign.Center, + ), ) } } @@ -221,14 +233,14 @@ fun OnBoardingNavigationButton(modifier: Modifier = Modifier, text: String, onCl .fillMaxWidth() .height(56.dp), onClick = onClick, - shape = MaterialTheme.shapes.medium + shape = MaterialTheme.shapes.medium, ) { Text( text = text, style = MaterialTheme.typography.titleLarge.copy( fontSize = 16.sp, - fontWeight = FontWeight.Bold - ) + fontWeight = FontWeight.Bold, + ), ) } } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/onboarding/UsernameScreen.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/onboarding/UsernameScreen.kt index 91b32a0..d3b4687 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/onboarding/UsernameScreen.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/onboarding/UsernameScreen.kt @@ -56,6 +56,7 @@ import com.joelkanyi.focusbloom.main.MainScreen import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.withContext +import org.koin.compose.rememberKoinInject import org.koin.core.component.KoinComponent import org.koin.core.component.get @@ -64,7 +65,7 @@ class UsernameScreen : Screen, KoinComponent { @OptIn(ExperimentalComposeUiApi::class) @Composable override fun Content() { - val onboadingViewModel = get() + val onboadingViewModel = rememberKoinInject() val navigator = LocalNavigator.currentOrThrow val username = onboadingViewModel.username.collectAsState().value val keyboardController = LocalSoftwareKeyboardController.current @@ -91,22 +92,27 @@ class UsernameScreen : Screen, KoinComponent { onClickContinue = { keyboardController?.hide() onboadingViewModel.saveUsername() - } + }, ) } } @Composable -fun UsernameScreenContent(username: String, typeWriterTextParts: List, onUsernameChange: (String) -> Unit, onClickContinue: () -> Unit) { +fun UsernameScreenContent( + username: String, + typeWriterTextParts: List, + onUsernameChange: (String) -> Unit, + onClickContinue: () -> Unit, +) { LazyColumn( modifier = Modifier .padding(16.dp) - .fillMaxSize() + .fillMaxSize(), ) { item { TypewriterText( baseText = "Focus Bloom app is what you need to", - parts = typeWriterTextParts + parts = typeWriterTextParts, ) } @@ -115,8 +121,8 @@ fun UsernameScreenContent(username: String, typeWriterTextParts: List, o Text( text = "What's your username?", style = MaterialTheme.typography.labelLarge.copy( - fontSize = 18.sp - ) + fontSize = 18.sp, + ), ) } @@ -135,13 +141,13 @@ fun UsernameScreenContent(username: String, typeWriterTextParts: List, o }, onClickDone = { onClickContinue() - } + }, ) } item { AnimatedVisibility( - username.isNotEmpty() + username.isNotEmpty(), ) { Column { Spacer(modifier = Modifier.height(56.dp)) @@ -150,13 +156,13 @@ fun UsernameScreenContent(username: String, typeWriterTextParts: List, o .fillMaxWidth() .height(56.dp), shape = MaterialTheme.shapes.medium, - onClick = onClickContinue + onClick = onClickContinue, ) { Text( text = "Continue", style = MaterialTheme.typography.labelLarge.copy( - fontWeight = FontWeight.SemiBold - ) + fontWeight = FontWeight.SemiBold, + ), ) } } @@ -166,7 +172,12 @@ fun UsernameScreenContent(username: String, typeWriterTextParts: List, o } @Composable -private fun UsernameTextField(modifier: Modifier, name: String, onNameChange: (String) -> Unit, onClickDone: () -> Unit) { +private fun UsernameTextField( + modifier: Modifier, + name: String, + onNameChange: (String) -> Unit, + onClickDone: () -> Unit, +) { TextField( modifier = modifier, value = name, @@ -180,8 +191,8 @@ private fun UsernameTextField(modifier: Modifier, name: String, onNameChange: (S fontWeight = FontWeight.ExtraLight, fontSize = 18.sp, letterSpacing = -(1.6).sp, - lineHeight = 32.sp - ) + lineHeight = 32.sp, + ), ) }, colors = TextFieldDefaults.colors( @@ -189,22 +200,22 @@ private fun UsernameTextField(modifier: Modifier, name: String, onNameChange: (S focusedContainerColor = MaterialTheme.colorScheme.background, focusedIndicatorColor = MaterialTheme.colorScheme.primary, unfocusedIndicatorColor = MaterialTheme.colorScheme.primary, - disabledIndicatorColor = MaterialTheme.colorScheme.primary + disabledIndicatorColor = MaterialTheme.colorScheme.primary, ), textStyle = MaterialTheme.typography.labelLarge.copy( fontWeight = FontWeight.SemiBold, - fontSize = 18.sp + fontSize = 18.sp, ), keyboardOptions = KeyboardOptions.Default.copy( imeAction = ImeAction.Done, keyboardType = KeyboardType.Text, - capitalization = KeyboardCapitalization.Words + capitalization = KeyboardCapitalization.Words, ), keyboardActions = KeyboardActions( onDone = { onClickDone() - } - ) + }, + ), ) } @@ -220,8 +231,8 @@ private fun TypewriterText(modifier: Modifier = Modifier, baseText: String, part fontWeight = FontWeight.SemiBold, fontSize = 24.sp, letterSpacing = -(1.6).sp, - lineHeight = 32.sp - ) + lineHeight = 32.sp, + ), ) LaunchedEffect(key1 = parts) { diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/settings/SettingsScreen.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/settings/SettingsScreen.kt index 3dadf8b..c4e6bb7 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/settings/SettingsScreen.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/settings/SettingsScreen.kt @@ -86,19 +86,19 @@ import com.joelkanyi.focusbloom.core.presentation.theme.Yellow import com.joelkanyi.focusbloom.core.utils.isDigitsOnly import com.joelkanyi.focusbloom.core.utils.timeFormat import com.joelkanyi.focusbloom.platform.StatusBarColors -import org.koin.compose.rememberKoinInject +import org.koin.compose.koinInject @Composable -fun SettingsScreen() { - val screenModel: SettingsScreenModel = rememberKoinInject() - +fun SettingsScreen( + screenModel: SettingsScreenModel = koinInject(), +) { val darkTheme = when (screenModel.appTheme.collectAsState().value) { 1 -> true else -> false } StatusBarColors( statusBarColor = MaterialTheme.colorScheme.background, - navBarColor = MaterialTheme.colorScheme.background + navBarColor = MaterialTheme.colorScheme.background, ) val sessionTime = screenModel.sessionTime.collectAsState().value ?: 25 @@ -204,9 +204,9 @@ fun SettingsScreen() { 1 } else { 0 - } + }, ) - } + }, ) } @@ -235,23 +235,23 @@ fun SettingsScreenContent( currentSessionColor: Long, onSelectColor: (Long) -> Unit, remindersOn: Boolean, - onRemindersChange: (Boolean) -> Unit + onRemindersChange: (Boolean) -> Unit, ) { Scaffold( topBar = { BloomTopAppBar( - hasBackNavigation = false + hasBackNavigation = false, ) { Text(text = "Settings") } - } + }, ) { paddingValues -> LazyColumn( modifier = Modifier .fillMaxSize() .padding(paddingValues), contentPadding = PaddingValues(16.dp), - verticalArrangement = Arrangement.spacedBy(8.dp) + verticalArrangement = Arrangement.spacedBy(8.dp), ) { item { FocusSessionsSetting( @@ -266,7 +266,7 @@ fun SettingsScreenContent( }, onExpand = { title -> openOptions(title) - } + }, ) } item { @@ -279,7 +279,7 @@ fun SettingsScreenContent( }, hourFormats = hourFormats, selectedHourFormat = selectedHourFormat, - onHourFormatChange = onHourFormatChange + onHourFormatChange = onHourFormatChange, ) } /*item { @@ -309,7 +309,7 @@ fun SettingsScreenContent( currentShortBreakColor = currentShortBreakColor, currentLongBreakColor = currentLongBreakColor, currentSessionColor = currentSessionColor, - onSelectColor = onSelectColor + onSelectColor = onSelectColor, ) } item { @@ -321,7 +321,7 @@ fun SettingsScreenContent( openOptions(title) }, remindersOn = remindersOn, - onRemindersChange = onRemindersChange + onRemindersChange = onRemindersChange, ) } } @@ -329,7 +329,16 @@ fun SettingsScreenContent( } @Composable -fun FocusSessionsSetting(focusSessionMinutes: Int, onFocusSessionMinutesChange: (String) -> Unit, shortBreakMinutes: Int, onShortBreakMinutesChange: (String) -> Unit, longBreakMinutes: Int, onLongBreakMinutesChange: (String) -> Unit, onExpand: (String) -> Unit, expanded: (String) -> Boolean) { +fun FocusSessionsSetting( + focusSessionMinutes: Int, + onFocusSessionMinutesChange: (String) -> Unit, + shortBreakMinutes: Int, + onShortBreakMinutesChange: (String) -> Unit, + longBreakMinutes: Int, + onLongBreakMinutesChange: (String) -> Unit, + onExpand: (String) -> Unit, + expanded: (String) -> Boolean, +) { SettingCard( onExpand = { onExpand("Focus Sessions") @@ -342,7 +351,7 @@ fun FocusSessionsSetting(focusSessionMinutes: Int, onFocusSessionMinutesChange: var autoStartFocusSession by remember { mutableStateOf(false) } Row( modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(12.dp) + horizontalArrangement = Arrangement.spacedBy(12.dp), ) { SessionTime( modifier = Modifier.weight(1f), @@ -350,7 +359,7 @@ fun FocusSessionsSetting(focusSessionMinutes: Int, onFocusSessionMinutesChange: currentValue = focusSessionMinutes.toString(), onValueChange = { onFocusSessionMinutesChange(it) - } + }, ) SessionTime( modifier = Modifier.weight(1f), @@ -358,7 +367,7 @@ fun FocusSessionsSetting(focusSessionMinutes: Int, onFocusSessionMinutesChange: currentValue = shortBreakMinutes.toString(), onValueChange = { onShortBreakMinutesChange(it) - } + }, ) SessionTime( modifier = Modifier.weight(1f), @@ -366,7 +375,7 @@ fun FocusSessionsSetting(focusSessionMinutes: Int, onFocusSessionMinutesChange: currentValue = longBreakMinutes.toString(), onValueChange = { onLongBreakMinutesChange(it) - } + }, ) } Spacer(modifier = Modifier.height(12.dp)) @@ -375,7 +384,7 @@ fun FocusSessionsSetting(focusSessionMinutes: Int, onFocusSessionMinutesChange: checked = autoStartBreaks, onCheckedChange = { autoStartBreaks = it - } + }, ) Spacer(modifier = Modifier.height(12.dp)) AutoStartSession( @@ -383,14 +392,20 @@ fun FocusSessionsSetting(focusSessionMinutes: Int, onFocusSessionMinutesChange: checked = autoStartFocusSession, onCheckedChange = { autoStartFocusSession = it - } + }, ) - } + }, ) } @Composable -fun TimeSetting(onExpand: (String) -> Unit, expanded: (String) -> Boolean, hourFormats: List, selectedHourFormat: Int, onHourFormatChange: (Int) -> Unit) { +fun TimeSetting( + onExpand: (String) -> Unit, + expanded: (String) -> Boolean, + hourFormats: List, + selectedHourFormat: Int, + onHourFormatChange: (Int) -> Unit, +) { SettingCard( onExpand = { onExpand("Time") @@ -406,9 +421,9 @@ fun TimeSetting(onExpand: (String) -> Unit, expanded: (String) -> Boolean, hourF onSelectOption = { onHourFormatChange(it.timeFormat()) onExpand("Time") - } + }, ) - } + }, ) } @@ -436,7 +451,7 @@ fun SoundSetting(onExpand: (String) -> Unit, expanded: (String) -> Boolean) { selectedOption = selectedAlarmSound, onSelectOption = { selectedAlarmSound = it - } + }, ) Slider( value = alarmSliderPosition, @@ -444,8 +459,8 @@ fun SoundSetting(onExpand: (String) -> Unit, expanded: (String) -> Boolean) { onValueChange = { alarmSliderPosition = it }, colors = SliderDefaults.colors( inactiveTickColor = MaterialTheme.colorScheme.secondary, - inactiveTrackColor = MaterialTheme.colorScheme.secondary - ) + inactiveTrackColor = MaterialTheme.colorScheme.secondary, + ), ) Spacer(modifier = Modifier.height(16.dp)) SoundSelection( @@ -454,7 +469,7 @@ fun SoundSetting(onExpand: (String) -> Unit, expanded: (String) -> Boolean) { selectedOption = selectedTickingSound, onSelectOption = { selectedTickingSound = it - } + }, ) Slider( value = tickingSliderPosition, @@ -462,10 +477,10 @@ fun SoundSetting(onExpand: (String) -> Unit, expanded: (String) -> Boolean) { onValueChange = { tickingSliderPosition = it }, colors = SliderDefaults.colors( inactiveTickColor = MaterialTheme.colorScheme.secondary, - inactiveTrackColor = MaterialTheme.colorScheme.secondary - ) + inactiveTrackColor = MaterialTheme.colorScheme.secondary, + ), ) - } + }, ) } @@ -482,7 +497,7 @@ fun ThemeSetting( currentShortBreakColor: Long, currentLongBreakColor: Long, currentSessionColor: Long, - onSelectColor: (Long) -> Unit + onSelectColor: (Long) -> Unit, ) { SettingCard( onExpand = { @@ -501,13 +516,13 @@ fun ThemeSetting( onSelectColor = { onShowColorDialog(false) onSelectColor(it) - } + }, ) } Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Text(text = "Sessions Color Scheme") ColorsSelection( @@ -525,7 +540,7 @@ fun ThemeSetting( }, currentSessionColor = currentSessionColor, currentShortBreakColor = currentShortBreakColor, - currentLongBreakColor = currentLongBreakColor + currentLongBreakColor = currentLongBreakColor, ) } Spacer(modifier = Modifier.height(16.dp)) @@ -540,14 +555,19 @@ fun ThemeSetting( checked = darkTheme, onCheckedChange = { onDarkThemeChange(it) - } + }, ) - } + }, ) } @Composable -fun NotificationsSetting(onExpand: (String) -> Unit, expanded: (String) -> Boolean, remindersOn: Boolean, onRemindersChange: (Boolean) -> Unit) { +fun NotificationsSetting( + onExpand: (String) -> Unit, + expanded: (String) -> Boolean, + remindersOn: Boolean, + onRemindersChange: (Boolean) -> Unit, +) { SettingCard( onExpand = { onExpand("Notifications") @@ -559,22 +579,27 @@ fun NotificationsSetting(onExpand: (String) -> Unit, expanded: (String) -> Boole AutoStartSession( title = "Reminders", checked = remindersOn, - onCheckedChange = onRemindersChange + onCheckedChange = onRemindersChange, ) - } + }, ) } @Composable -private fun SoundSelection(options: List, title: String, selectedOption: String, onSelectOption: (String) -> Unit) { +private fun SoundSelection( + options: List, + title: String, + selectedOption: String, + onSelectOption: (String) -> Unit, +) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Text( modifier = Modifier.fillMaxWidth(.5f), - text = title + text = title, ) BloomDropDown( modifier = Modifier @@ -583,7 +608,7 @@ private fun SoundSelection(options: List, title: String, selectedOption: selectedOption = TextFieldState(text = selectedOption), onOptionSelected = { onSelectOption(it) - } + }, ) } } @@ -593,67 +618,79 @@ fun AutoStartSession(title: String, checked: Boolean, onCheckedChange: (Boolean) Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Text(text = title) Switch( checked = checked, - onCheckedChange = onCheckedChange + onCheckedChange = onCheckedChange, ) } } @Composable -fun SessionTime(modifier: Modifier = Modifier, title: String, currentValue: String, onValueChange: (String) -> Unit) { +fun SessionTime( + modifier: Modifier = Modifier, + title: String, + currentValue: String, + onValueChange: (String) -> Unit, +) { BloomInputTextField( modifier = modifier, textStyle = MaterialTheme.typography.bodyMedium.copy( - textAlign = TextAlign.Start + textAlign = TextAlign.Start, ), label = { Text( text = title, style = MaterialTheme.typography.labelLarge.copy( - fontWeight = FontWeight.SemiBold - ) + fontWeight = FontWeight.SemiBold, + ), ) }, value = TextFieldState(currentValue), onValueChange = onValueChange, keyboardOptions = KeyboardOptions.Default.copy( - keyboardType = KeyboardType.Number - ) + keyboardType = KeyboardType.Number, + ), ) } @OptIn(ExperimentalMaterial3Api::class) @Composable -fun SettingCard(title: String, icon: ImageVector, modifier: Modifier = Modifier, content: @Composable () -> Unit, onExpand: () -> Unit, expanded: Boolean) { +fun SettingCard( + title: String, + icon: ImageVector, + modifier: Modifier = Modifier, + content: @Composable () -> Unit, + onExpand: () -> Unit, + expanded: Boolean, +) { Card( modifier = modifier, onClick = { onExpand() - } + }, ) { Column( - modifier = modifier.padding(16.dp) + modifier = modifier.padding(16.dp), ) { Row( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween + horizontalArrangement = Arrangement.SpaceBetween, ) { Row( verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(12.dp) + horizontalArrangement = Arrangement.spacedBy(12.dp), ) { Icon( imageVector = icon, - contentDescription = title + contentDescription = title, ) Text( text = title, - style = MaterialTheme.typography.titleLarge + style = MaterialTheme.typography.titleLarge, ) } @@ -664,7 +701,7 @@ fun SettingCard(title: String, icon: ImageVector, modifier: Modifier = Modifier, } else { Icons.Rounded.KeyboardArrowDown }, - contentDescription = null + contentDescription = null, ) } } @@ -679,22 +716,29 @@ fun SettingCard(title: String, icon: ImageVector, modifier: Modifier = Modifier, } @Composable -fun ColorsSelection(onSelectSessionColor: (Long) -> Unit, onSelectShortBreakColor: (Long) -> Unit, onSelectLongBreakColor: (Long) -> Unit, currentSessionColor: Long, currentShortBreakColor: Long, currentLongBreakColor: Long) { +fun ColorsSelection( + onSelectSessionColor: (Long) -> Unit, + onSelectShortBreakColor: (Long) -> Unit, + onSelectLongBreakColor: (Long) -> Unit, + currentSessionColor: Long, + currentShortBreakColor: Long, + currentLongBreakColor: Long, +) { Row( horizontalArrangement = Arrangement.spacedBy(12.dp), - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { ColorCard( color = currentSessionColor, - onClick = onSelectSessionColor + onClick = onSelectSessionColor, ) ColorCard( color = currentShortBreakColor, - onClick = onSelectShortBreakColor + onClick = onSelectShortBreakColor, ) ColorCard( color = currentLongBreakColor, - onClick = onSelectLongBreakColor + onClick = onSelectLongBreakColor, ) } } @@ -708,12 +752,17 @@ fun ColorCard(modifier: Modifier = Modifier, color: Long, onClick: (Long) -> Uni .background(Color(color)) .clickable { onClick(color) - } + }, ) } @Composable -fun ColorsDialog(modifier: Modifier = Modifier, onDismiss: () -> Unit, onSelectColor: (Long) -> Unit, title: String) { +fun ColorsDialog( + modifier: Modifier = Modifier, + onDismiss: () -> Unit, + onSelectColor: (Long) -> Unit, + title: String, +) { AlertDialog( modifier = modifier.fillMaxWidth(), shape = MaterialTheme.shapes.large, @@ -725,14 +774,14 @@ fun ColorsDialog(modifier: Modifier = Modifier, onDismiss: () -> Unit, onSelectC modifier = Modifier.fillMaxWidth(), text = title, style = MaterialTheme.typography.titleMedium.copy( - textAlign = TextAlign.Center - ) + textAlign = TextAlign.Center, + ), ) }, text = { LazyVerticalGrid( columns = GridCells.Fixed(4), - verticalArrangement = Arrangement.spacedBy(8.dp) + verticalArrangement = Arrangement.spacedBy(8.dp), ) { items(sessionColors) { ColorCard( @@ -740,13 +789,13 @@ fun ColorsDialog(modifier: Modifier = Modifier, onDismiss: () -> Unit, onSelectC .padding(horizontal = 4.dp) .size(48.dp), color = it, - onClick = onSelectColor + onClick = onSelectColor, ) } } }, dismissButton = {}, - confirmButton = {} + confirmButton = {}, ) } @@ -762,7 +811,7 @@ private val sessionColors = listOf( LightGreen, Yellow, LightBlue, - Pink + Pink, ) /** diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/settings/SettingsScreenModel.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/settings/SettingsScreenModel.kt index aa72c77..9db73f9 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/settings/SettingsScreenModel.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/settings/SettingsScreenModel.kt @@ -17,7 +17,7 @@ package com.joelkanyi.focusbloom.feature.settings import androidx.compose.runtime.mutableStateListOf import cafe.adriel.voyager.core.model.ScreenModel -import cafe.adriel.voyager.core.model.coroutineScope +import cafe.adriel.voyager.core.model.screenModelScope import com.joelkanyi.focusbloom.core.domain.repository.settings.SettingsRepository import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted @@ -28,7 +28,7 @@ import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch class SettingsScreenModel( - private val settingsRepository: SettingsRepository + private val settingsRepository: SettingsRepository, ) : ScreenModel { private val _selectedColorCardTitle = MutableStateFlow("") val selectedColorCardTitle = _selectedColorCardTitle.asStateFlow() @@ -50,13 +50,13 @@ class SettingsScreenModel( val appTheme: StateFlow = settingsRepository.getAppTheme() .map { it } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(), - initialValue = null + initialValue = null, ) fun setAppTheme(appTheme: Int) { - coroutineScope.launch { + screenModelScope.launch { settingsRepository.saveAppTheme(appTheme) } } @@ -66,13 +66,13 @@ class SettingsScreenModel( it } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = null + initialValue = null, ) fun setSessionTime(sessionTime: Int) { - coroutineScope.launch { + screenModelScope.launch { settingsRepository.saveSessionTime(sessionTime) } } @@ -80,13 +80,13 @@ class SettingsScreenModel( val shortBreakTime = settingsRepository.getShortBreakTime() .map { it } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = null + initialValue = null, ) fun setShortBreakTime(shortBreakTime: Int) { - coroutineScope.launch { + screenModelScope.launch { settingsRepository.saveShortBreakTime(shortBreakTime) } } @@ -94,13 +94,13 @@ class SettingsScreenModel( val longBreakTime = settingsRepository.getLongBreakTime() .map { it } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = null + initialValue = null, ) fun setLongBreakTime(longBreakTime: Int) { - coroutineScope.launch { + screenModelScope.launch { settingsRepository.saveLongBreakTime(longBreakTime) } } @@ -108,31 +108,31 @@ class SettingsScreenModel( val timeFormat = settingsRepository.getHourFormat() .map { it } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = null + initialValue = null, ) fun setHourFormat(timeFormat: Int) { - coroutineScope.launch { + screenModelScope.launch { settingsRepository.saveHourFormat(timeFormat) } } fun setShortBreakColor(color: Long) { - coroutineScope.launch { + screenModelScope.launch { settingsRepository.saveShortBreakColor(color) } } fun setLongBreakColor(color: Long) { - coroutineScope.launch { + screenModelScope.launch { settingsRepository.saveLongBreakColor(color) } } fun setFocusColor(color: Long) { - coroutineScope.launch { + screenModelScope.launch { settingsRepository.saveFocusColor(color) } } @@ -148,37 +148,37 @@ class SettingsScreenModel( it } .stateIn( - coroutineScope, + screenModelScope, started = SharingStarted.WhileSubscribed(), - initialValue = null + initialValue = null, ) val longBreakColor = settingsRepository.longBreakColor() .map { it } .stateIn( - coroutineScope, + screenModelScope, started = SharingStarted.WhileSubscribed(), - initialValue = null + initialValue = null, ) val focusColor = settingsRepository.focusColor() .map { it } .stateIn( - coroutineScope, + screenModelScope, started = SharingStarted.WhileSubscribed(), - initialValue = null + initialValue = null, ) val remindersOn = settingsRepository.remindersOn() .map { it } .stateIn( - coroutineScope, + screenModelScope, started = SharingStarted.WhileSubscribed(), - initialValue = false + initialValue = false, ) fun setReminders(value: Int) { - coroutineScope.launch { + screenModelScope.launch { settingsRepository.toggleReminder(value) } } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/statistics/AllStatisticsScreen.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/statistics/AllStatisticsScreen.kt index b4b39fe..1088bc8 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/statistics/AllStatisticsScreen.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/statistics/AllStatisticsScreen.kt @@ -45,6 +45,7 @@ import com.joelkanyi.focusbloom.core.presentation.component.BloomTopAppBar import com.joelkanyi.focusbloom.core.utils.prettyFormat import com.joelkanyi.focusbloom.platform.StatusBarColors import kotlinx.datetime.LocalDate +import org.koin.compose.rememberKoinInject import org.koin.core.component.KoinComponent import org.koin.core.component.get @@ -52,10 +53,10 @@ class AllStatisticsScreen : Screen, KoinComponent { @Composable override fun Content() { - val screenModel = get() + val screenModel = rememberKoinInject() StatusBarColors( statusBarColor = MaterialTheme.colorScheme.background, - navBarColor = MaterialTheme.colorScheme.background + navBarColor = MaterialTheme.colorScheme.background, ) val navigator = LocalNavigator.currentOrThrow val hourFormat = screenModel.hourFormat.collectAsState().value ?: 24 @@ -84,7 +85,7 @@ class AllStatisticsScreen : Screen, KoinComponent { }, onClickCancel = { screenModel.openTaskOptions(it) - } + }, ) } } @@ -101,7 +102,7 @@ fun AllStatisticsScreenContent( onClickDelete: (task: Task) -> Unit, onClickCancel: (task: Task) -> Unit, showTaskOption: (task: Task) -> Boolean, - onShowTaskOption: (task: Task) -> Unit + onShowTaskOption: (task: Task) -> Unit, ) { Scaffold( topBar = { @@ -111,20 +112,20 @@ fun AllStatisticsScreenContent( IconButton(onClick = onClickNavigateBack) { Icon( imageVector = Icons.Outlined.ArrowBack, - contentDescription = "Back" + contentDescription = "Back", ) } - } + }, ) { Text(text = "Tasks History") } - } + }, ) { paddingValues -> LazyColumn( modifier = Modifier .padding(paddingValues) .fillMaxSize(), - contentPadding = PaddingValues(horizontal = 16.dp) + contentPadding = PaddingValues(horizontal = 16.dp), ) { tasks.forEach { (date, tasks) -> stickyHeader { @@ -136,13 +137,13 @@ fun AllStatisticsScreenContent( text = date.prettyFormat(), style = MaterialTheme.typography.titleMedium.copy( fontWeight = FontWeight.Bold, - fontSize = 16.sp - ) + fontSize = 16.sp, + ), ) } items( items = tasks, - key = { it.id } + key = { it.id }, ) { HistoryCard( modifier = Modifier @@ -156,7 +157,7 @@ fun AllStatisticsScreenContent( onClickDelete = onClickDelete, onClickCancel = onClickCancel, showTaskOption = showTaskOption, - onShowTaskOption = onShowTaskOption + onShowTaskOption = onShowTaskOption, ) } } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/statistics/StatisticsScreen.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/statistics/StatisticsScreen.kt index b1c1d52..0ce6d1e 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/statistics/StatisticsScreen.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/statistics/StatisticsScreen.kt @@ -64,10 +64,10 @@ import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow import com.joelkanyi.focusbloom.core.domain.model.Task import com.joelkanyi.focusbloom.core.presentation.component.BloomTopAppBar -import com.joelkanyi.focusbloom.core.utils.LocalAppNavigator import com.joelkanyi.focusbloom.core.utils.calculateEndTime import com.joelkanyi.focusbloom.core.utils.completedTasks import com.joelkanyi.focusbloom.core.utils.durationInMinutes @@ -79,24 +79,26 @@ import com.joelkanyi.focusbloom.core.utils.taskIcon import com.joelkanyi.focusbloom.feature.statistics.component.BarChart import com.joelkanyi.focusbloom.feature.statistics.component.TickPositionState import com.joelkanyi.focusbloom.platform.StatusBarColors +import focusbloom.shared.generated.resources.Res +import focusbloom.shared.generated.resources.redo import io.github.koalaplot.core.ChartLayout import io.github.koalaplot.core.util.ExperimentalKoalaPlotApi import io.github.koalaplot.core.xychart.TickPosition import kotlinx.coroutines.launch import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.painterResource -import org.koin.compose.rememberKoinInject +import org.koin.compose.koinInject @OptIn(ExperimentalFoundationApi::class) @Composable -fun StatisticsScreen() { - val screenModel: StatisticsScreenModel = rememberKoinInject() - +fun StatisticsScreen( + screenModel: StatisticsScreenModel = koinInject(), +) { StatusBarColors( statusBarColor = MaterialTheme.colorScheme.background, - navBarColor = MaterialTheme.colorScheme.background + navBarColor = MaterialTheme.colorScheme.background, ) - val navigator = LocalAppNavigator.currentOrThrow + val navigator = LocalNavigator.currentOrThrow val tasksHistory = screenModel.tasks.collectAsState().value val lastFiftyTwoWeeks = getLast52Weeks().asReversed() val hourFormat = screenModel.hourFormat.collectAsState().value ?: 24 @@ -109,18 +111,18 @@ fun StatisticsScreen() { initialPageOffsetFraction = 0f, pageCount = { lastFiftyTwoWeeks.size - } + }, ) val selectedWeek = lastFiftyTwoWeeks[pagerState.currentPage].first val selectedWeekTasks = tasksHistory.completedTasks( - lastFiftyTwoWeeks[pagerState.currentPage].second + lastFiftyTwoWeeks[pagerState.currentPage].second, ).map { it.toFloat() } val tickPositionState by remember { mutableStateOf( TickPositionState( TickPosition.Outside, - TickPosition.Outside - ) + TickPosition.Outside, + ), ) } @@ -135,7 +137,7 @@ fun StatisticsScreen() { selectedWeekTasks = selectedWeekTasks, tasksHistory = tasksHistory, onClickSeeAllTasks = { - navigator.push(AllStatisticsScreen()) + navigator.parent?.push(AllStatisticsScreen()) }, onClickThisWeek = { coroutineScope.launch { @@ -163,7 +165,7 @@ fun StatisticsScreen() { }, onClickCancel = { screenModel.openTaskOptions(it) - } + }, ) } @@ -171,7 +173,6 @@ fun StatisticsScreen() { ExperimentalKoalaPlotApi::class, ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class, - ExperimentalResourceApi::class ) @Composable fun StatisticsScreenContent( @@ -191,7 +192,7 @@ fun StatisticsScreenContent( onClickDelete: (task: Task) -> Unit, onClickCancel: (task: Task) -> Unit, showTaskOption: (task: Task) -> Boolean, - onShowTaskOption: (task: Task) -> Unit + onShowTaskOption: (task: Task) -> Unit, ) { Scaffold( topBar = { @@ -203,64 +204,64 @@ fun StatisticsScreenContent( text = "Your Statistics", style = MaterialTheme.typography.displaySmall.copy( fontSize = 18.sp, - fontWeight = FontWeight.Bold - ) + fontWeight = FontWeight.Bold, + ), ) }, actions = { AnimatedVisibility(selectedWeek != "This Week") { TextButton( - onClick = onClickThisWeek + onClick = onClickThisWeek, ) { Row( verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(4.dp) + horizontalArrangement = Arrangement.spacedBy(4.dp), ) { Icon( modifier = Modifier.size(18.dp), - painter = painterResource("redo.xml"), - contentDescription = "This Week" + painter = painterResource(Res.drawable.redo), + contentDescription = "This Week", ) Text( text = "This Week", style = MaterialTheme.typography.labelLarge.copy( fontWeight = FontWeight.SemiBold, color = MaterialTheme.colorScheme.primary, - textDecoration = TextDecoration.Underline - ) + textDecoration = TextDecoration.Underline, + ), ) } } } - } + }, ) - } + }, ) { paddingValues -> LazyColumn( modifier = Modifier.padding(paddingValues), - contentPadding = PaddingValues(horizontal = 16.dp) + contentPadding = PaddingValues(horizontal = 16.dp), ) { item { WeeksController( onClickPreviousWeek = onClickPreviousWeek, selectedWeek = selectedWeek, - onClickNextWeek = onClickNextWeek + onClickNextWeek = onClickNextWeek, ) } item { HorizontalPager( state = pagerState, modifier = Modifier - .fillMaxWidth() + .fillMaxWidth(), ) { ChartLayout( modifier = Modifier .fillMaxWidth() - .sizeIn(maxHeight = 300.dp) + .sizeIn(maxHeight = 300.dp), ) { BarChart( tickPositionState = tickPositionState, - entries = selectedWeekTasks + entries = selectedWeekTasks, ) } } @@ -271,13 +272,13 @@ fun StatisticsScreenContent( Row( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween + horizontalArrangement = Arrangement.SpaceBetween, ) { Text( text = "Your History", style = MaterialTheme.typography.titleLarge.copy( - fontWeight = FontWeight.Bold - ) + fontWeight = FontWeight.Bold, + ), ) if (tasksHistory.size > 3) { TextButton(onClick = onClickSeeAllTasks) { @@ -286,8 +287,8 @@ fun StatisticsScreenContent( style = MaterialTheme.typography.labelLarge.copy( fontWeight = FontWeight.SemiBold, color = MaterialTheme.colorScheme.primary, - fontSize = 16.sp - ) + fontSize = 16.sp, + ), ) } } @@ -304,13 +305,13 @@ fun StatisticsScreenContent( style = MaterialTheme.typography.titleMedium.copy( fontWeight = FontWeight.Bold, fontSize = 16.sp, - textAlign = TextAlign.End - ) + textAlign = TextAlign.End, + ), ) } items( items = tasks, - key = { it.id } + key = { it.id }, ) { HistoryCard( modifier = Modifier @@ -324,7 +325,7 @@ fun StatisticsScreenContent( onClickDelete = onClickDelete, onClickCancel = onClickCancel, showTaskOption = showTaskOption, - onShowTaskOption = onShowTaskOption + onShowTaskOption = onShowTaskOption, ) } } @@ -333,27 +334,31 @@ fun StatisticsScreenContent( } @Composable -private fun WeeksController(onClickPreviousWeek: () -> Unit, selectedWeek: String, onClickNextWeek: () -> Unit) { +private fun WeeksController( + onClickPreviousWeek: () -> Unit, + selectedWeek: String, + onClickNextWeek: () -> Unit, +) { Row( modifier = Modifier .fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { IconButton( modifier = Modifier.size(24.dp), - onClick = onClickPreviousWeek + onClick = onClickPreviousWeek, ) { Icon( imageVector = Icons.Default.KeyboardDoubleArrowLeft, - contentDescription = "Previous Week" + contentDescription = "Previous Week", ) } Text( text = selectedWeek, style = MaterialTheme.typography.titleLarge.copy( - fontWeight = FontWeight.SemiBold - ) + fontWeight = FontWeight.SemiBold, + ), ) IconButton( modifier = Modifier.size(24.dp), @@ -361,7 +366,7 @@ private fun WeeksController(onClickPreviousWeek: () -> Unit, selectedWeek: Strin if (selectedWeek != "This Week") { onClickNextWeek() } - } + }, ) { Icon( imageVector = Icons.Default.KeyboardDoubleArrowRight, @@ -370,7 +375,7 @@ private fun WeeksController(onClickPreviousWeek: () -> Unit, selectedWeek: Strin MaterialTheme.colorScheme.onBackground } else { MaterialTheme.colorScheme.onBackground.copy(alpha = 0.3f) - } + }, ) } } @@ -378,15 +383,26 @@ private fun WeeksController(onClickPreviousWeek: () -> Unit, selectedWeek: Strin @OptIn(ExperimentalResourceApi::class) @Composable -fun HistoryCard(task: Task, modifier: Modifier = Modifier, hourFormat: Int, sessionTime: Int, shortBreakTime: Int, longBreakTime: Int, onClickCancel: (task: Task) -> Unit, onClickDelete: (task: Task) -> Unit, showTaskOption: (task: Task) -> Boolean, onShowTaskOption: (task: Task) -> Unit) { +fun HistoryCard( + task: Task, + modifier: Modifier = Modifier, + hourFormat: Int, + sessionTime: Int, + shortBreakTime: Int, + longBreakTime: Int, + onClickCancel: (task: Task) -> Unit, + onClickDelete: (task: Task) -> Unit, + showTaskOption: (task: Task) -> Boolean, + onShowTaskOption: (task: Task) -> Unit, +) { Column { Card( - modifier = modifier + modifier = modifier, ) { Row( modifier = Modifier.padding(12.dp), horizontalArrangement = Arrangement.spacedBy(16.dp), - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Box( modifier = Modifier @@ -394,9 +410,9 @@ fun HistoryCard(task: Task, modifier: Modifier = Modifier, hourFormat: Int, sess .clip(MaterialTheme.shapes.large) .background( color = Color(task.type.taskColor()), - shape = MaterialTheme.shapes.medium + shape = MaterialTheme.shapes.medium, ), - contentAlignment = Alignment.Center + contentAlignment = Alignment.Center, ) { Icon( modifier = Modifier @@ -404,27 +420,27 @@ fun HistoryCard(task: Task, modifier: Modifier = Modifier, hourFormat: Int, sess .size(24.dp), painter = painterResource(task.type.taskIcon()), contentDescription = "Task Icon", - tint = MaterialTheme.colorScheme.onPrimary + tint = MaterialTheme.colorScheme.onPrimary, ) } Column( modifier = Modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(4.dp) + verticalArrangement = Arrangement.spacedBy(4.dp), ) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Text( modifier = Modifier.fillMaxWidth(.8f), text = task.name, style = MaterialTheme.typography.titleSmall.copy( fontWeight = FontWeight.SemiBold, - fontSize = 14.sp + fontSize = 14.sp, ), maxLines = 2, - overflow = TextOverflow.Ellipsis + overflow = TextOverflow.Ellipsis, ) Icon( modifier = Modifier @@ -433,7 +449,7 @@ fun HistoryCard(task: Task, modifier: Modifier = Modifier, hourFormat: Int, sess onShowTaskOption(task) }, imageVector = Icons.Default.MoreVert, - contentDescription = "More Options" + contentDescription = "More Options", ) } if (task.description != null) { @@ -441,13 +457,13 @@ fun HistoryCard(task: Task, modifier: Modifier = Modifier, hourFormat: Int, sess text = task.description, style = MaterialTheme.typography.bodyMedium, maxLines = 3, - overflow = TextOverflow.Ellipsis + overflow = TextOverflow.Ellipsis, ) } Spacer(modifier = Modifier.height(8.dp)) Row( modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween + horizontalArrangement = Arrangement.SpaceBetween, ) { Text( text = "${ @@ -455,13 +471,13 @@ fun HistoryCard(task: Task, modifier: Modifier = Modifier, hourFormat: Int, sess focusSessions = task.focusSessions, sessionTime = sessionTime, shortBreakTime = shortBreakTime, - longBreakTime = longBreakTime + longBreakTime = longBreakTime, ) } minutes", style = MaterialTheme.typography.displaySmall.copy( fontSize = 12.sp, - fontWeight = FontWeight.SemiBold - ) + fontWeight = FontWeight.SemiBold, + ), ) Text( prettyTimeDifference( @@ -470,14 +486,14 @@ fun HistoryCard(task: Task, modifier: Modifier = Modifier, hourFormat: Int, sess focusSessions = task.focusSessions, sessionTime = sessionTime, shortBreakTime = shortBreakTime, - longBreakTime = longBreakTime + longBreakTime = longBreakTime, ), - timeFormat = hourFormat + timeFormat = hourFormat, ), style = MaterialTheme.typography.displaySmall.copy( fontSize = 12.sp, - fontWeight = FontWeight.SemiBold - ) + fontWeight = FontWeight.SemiBold, + ), ) } } @@ -489,7 +505,7 @@ fun HistoryCard(task: Task, modifier: Modifier = Modifier, hourFormat: Int, sess modifier = Modifier .fillMaxWidth(), horizontalArrangement = Arrangement.End, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { TextButton(onClick = { onClickCancel(task) @@ -497,8 +513,8 @@ fun HistoryCard(task: Task, modifier: Modifier = Modifier, hourFormat: Int, sess Text( text = "Cancel", style = MaterialTheme.typography.labelLarge.copy( - fontWeight = FontWeight.SemiBold - ) + fontWeight = FontWeight.SemiBold, + ), ) } Spacer(modifier = Modifier.width(16.dp)) @@ -509,8 +525,8 @@ fun HistoryCard(task: Task, modifier: Modifier = Modifier, hourFormat: Int, sess text = "Delete", color = MaterialTheme.colorScheme.error, style = MaterialTheme.typography.labelLarge.copy( - fontWeight = FontWeight.ExtraBold - ) + fontWeight = FontWeight.ExtraBold, + ), ) } } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/statistics/StatisticsScreenModel.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/statistics/StatisticsScreenModel.kt index 74dde16..a3cfe65 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/statistics/StatisticsScreenModel.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/statistics/StatisticsScreenModel.kt @@ -17,7 +17,7 @@ package com.joelkanyi.focusbloom.feature.statistics import androidx.compose.runtime.mutableStateListOf import cafe.adriel.voyager.core.model.ScreenModel -import cafe.adriel.voyager.core.model.coroutineScope +import cafe.adriel.voyager.core.model.screenModelScope import com.joelkanyi.focusbloom.core.domain.model.Task import com.joelkanyi.focusbloom.core.domain.repository.settings.SettingsRepository import com.joelkanyi.focusbloom.core.domain.repository.tasks.TasksRepository @@ -28,16 +28,16 @@ import kotlinx.coroutines.launch class StatisticsScreenModel( private val tasksRepository: TasksRepository, - settingsRepository: SettingsRepository + settingsRepository: SettingsRepository, ) : ScreenModel { val hourFormat = settingsRepository.getHourFormat() .map { it } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = null + initialValue = null, ) val sessionTime = settingsRepository.getSessionTime() @@ -45,23 +45,23 @@ class StatisticsScreenModel( it } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = null + initialValue = null, ) val shortBreakTime = settingsRepository.getShortBreakTime() .map { it } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = null + initialValue = null, ) val longBreakTime = settingsRepository.getLongBreakTime() .map { it } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = null + initialValue = null, ) val tasks = tasksRepository.getTasks() @@ -72,13 +72,13 @@ class StatisticsScreenModel( } } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = emptyList() + initialValue = emptyList(), ) fun deleteTask(task: Task) { - coroutineScope.launch { + screenModelScope.launch { tasksRepository.deleteTask(task.id) } } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/statistics/component/StatisticsChart.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/statistics/component/StatisticsChart.kt index 7d1c9f8..db40395 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/statistics/component/StatisticsChart.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/statistics/component/StatisticsChart.kt @@ -54,7 +54,7 @@ fun AxisTitle(title: String, modifier: Modifier = Modifier) { title, color = MaterialTheme.colorScheme.onBackground, style = MaterialTheme.typography.titleMedium, - modifier = modifier + modifier = modifier, ) } @@ -66,7 +66,7 @@ fun AxisLabel(label: String, modifier: Modifier = Modifier) { style = MaterialTheme.typography.bodySmall, modifier = modifier, overflow = TextOverflow.Ellipsis, - maxLines = 1 + maxLines = 1, ) } @@ -77,8 +77,8 @@ fun barChartEntries(fibonacci: List): List> { DefaultBarChartEntry( xValue = (index + 1).toFloat(), yMin = 0f, - yMax = fl - ) + yMax = fl, + ), ) } } @@ -91,7 +91,7 @@ fun BarChart(tickPositionState: TickPositionState, entries: List) { val xAxisRange = 0.5f..7.5f ChartLayout( modifier = paddingMod, - title = { } + title = { }, ) { XYChart( xAxisModel = LinearAxisModel( @@ -99,16 +99,16 @@ fun BarChart(tickPositionState: TickPositionState, entries: List) { minimumMajorTickIncrement = 1f, minimumMajorTickSpacing = 10.dp, zoomRangeLimit = 3f, - minorTickCount = 0 + minorTickCount = 0, ), yAxisModel = LinearAxisModel( yAxisRange, minimumMajorTickIncrement = 1f, - minorTickCount = 0 + minorTickCount = 0, ), xAxisStyle = rememberAxisStyle( tickPosition = tickPositionState.horizontalAxis, - color = Color.LightGray + color = Color.LightGray, ), xAxisLabels = { AxisLabel( @@ -122,13 +122,13 @@ fun BarChart(tickPositionState: TickPositionState, entries: List) { 7f -> "Sun" else -> "" }, - Modifier.padding(top = 2.dp) + Modifier.padding(top = 2.dp), ) }, xAxisTitle = { AxisTitle( "Day of the Week", - modifier = Modifier.padding(top = 8.dp) + modifier = Modifier.padding(top = 8.dp), ) }, yAxisStyle = rememberAxisStyle(tickPosition = tickPositionState.verticalAxis), @@ -139,25 +139,25 @@ fun BarChart(tickPositionState: TickPositionState, entries: List) { AxisTitle( "Tasks Completed", modifier = Modifier.rotateVertically(VerticalRotation.COUNTER_CLOCKWISE) - .padding(bottom = padding) + .padding(bottom = padding), ) }, - verticalMajorGridLineStyle = null + verticalMajorGridLineStyle = null, ) { VerticalBarChart( series = listOf( barChartEntries( - fibonacci = entries - ) + fibonacci = entries, + ), ), bar = { _, _, value -> DefaultVerticalBar( brush = SolidColor(MaterialTheme.colorScheme.primary), - modifier = Modifier.fillMaxWidth(BarWidth) + modifier = Modifier.fillMaxWidth(BarWidth), ) { HoverSurface { Text(value.yMax.toString()) } } - } + }, ) } } @@ -165,7 +165,7 @@ fun BarChart(tickPositionState: TickPositionState, entries: List) { data class TickPositionState( val verticalAxis: TickPosition, - val horizontalAxis: TickPosition + val horizontalAxis: TickPosition, ) @Composable @@ -174,7 +174,7 @@ fun HoverSurface(modifier: Modifier = Modifier, content: @Composable () -> Unit) shadowElevation = 2.dp, shape = MaterialTheme.shapes.medium, color = Color.LightGray, - modifier = modifier.padding(padding) + modifier = modifier.padding(padding), ) { Box(modifier = Modifier.padding(padding)) { content() diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/taskprogress/TaskProgressScreen.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/taskprogress/TaskProgressScreen.kt index 08b42eb..5f2381c 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/taskprogress/TaskProgressScreen.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/taskprogress/TaskProgressScreen.kt @@ -76,6 +76,8 @@ import com.joelkanyi.focusbloom.core.utils.toMinutes import com.joelkanyi.focusbloom.core.utils.toPercentage import com.joelkanyi.focusbloom.core.utils.toTimer import com.joelkanyi.focusbloom.platform.StatusBarColors +import focusbloom.shared.generated.resources.Res +import focusbloom.shared.generated.resources.ic_complete import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.jetbrains.compose.resources.ExperimentalResourceApi @@ -84,7 +86,7 @@ import org.koin.core.component.KoinComponent import org.koin.core.component.get data class TaskProgressScreen( - val taskId: Int + val taskId: Int, ) : Screen, KoinComponent { @Composable @@ -108,7 +110,7 @@ data class TaskProgressScreen( Color(SessionColor) } else { Color( - focusColor + focusColor, ) } } @@ -118,7 +120,7 @@ data class TaskProgressScreen( Color(LongBreakColor) } else { Color( - longBreakColor + longBreakColor, ) } } @@ -128,14 +130,14 @@ data class TaskProgressScreen( Color(ShortBreakColor) } else { Color( - shortBreakColor + shortBreakColor, ) } } } StatusBarColors( statusBarColor = containerColor, - navBarColor = containerColor + navBarColor = containerColor, ) LaunchedEffect(key1 = Unit) { screenModel.getRemindersStatus() @@ -160,7 +162,7 @@ data class TaskProgressScreen( onConfirm = { Timer.reset() navigator.pop() - } + }, ) } @@ -203,7 +205,7 @@ data class TaskProgressScreen( }, executeTasks = { screenModel.executeTasks() - } + }, ) screenModel.resetAllTasksToInactive() screenModel.updateActiveTask(taskId, true) @@ -213,7 +215,7 @@ data class TaskProgressScreen( // screenModel.setTime(task?.focusTime ?: 20) } } - } + }, ) } } @@ -232,7 +234,7 @@ fun FocusTimeScreenContent( onClickNavigateBack: () -> Unit, onClickAction: (state: TimerState) -> Unit, onClickNext: () -> Unit, - onClickReset: () -> Unit + onClickReset: () -> Unit, ) { Scaffold( containerColor = containerColor, @@ -247,48 +249,48 @@ fun FocusTimeScreenContent( Icon( imageVector = Icons.Outlined.ArrowBack, contentDescription = "Add Task Back Button", - tint = MaterialTheme.colorScheme.onPrimary + tint = MaterialTheme.colorScheme.onPrimary, ) } }, colors = TopAppBarDefaults.topAppBarColors( - containerColor = containerColor - ) + containerColor = containerColor, + ), ) - } + }, ) { paddingValues -> Box( - modifier = Modifier.padding(paddingValues).fillMaxSize() + modifier = Modifier.padding(paddingValues).fillMaxSize(), ) { if (task == null) { Text( modifier = Modifier.align(Alignment.Center), - text = "Task not found" + text = "Task not found", ) } else { LazyColumn( - modifier = Modifier.padding(PaddingValues(horizontal = 16.dp)) + modifier = Modifier.padding(PaddingValues(horizontal = 16.dp)), ) { item { Card( - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) { Column( modifier = Modifier .fillMaxWidth() - .padding(12.dp) + .padding(12.dp), ) { Row( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween + horizontalArrangement = Arrangement.SpaceBetween, ) { Text( modifier = Modifier.fillMaxWidth(.85f), text = task.name, style = MaterialTheme.typography.titleSmall, maxLines = 3, - overflow = TextOverflow.Ellipsis + overflow = TextOverflow.Ellipsis, ) Text( @@ -296,19 +298,19 @@ fun FocusTimeScreenContent( withStyle( style = SpanStyle( fontWeight = FontWeight.SemiBold, - fontSize = 18.sp - ) + fontSize = 18.sp, + ), ) { append("${task.currentCycle}") } append("/${task.focusSessions}") - } + }, ) } Row( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween + horizontalArrangement = Arrangement.SpaceBetween, ) { Text( text = "Total: ${ @@ -316,17 +318,17 @@ fun FocusTimeScreenContent( sessionTime = focusTime.toMinutes(), shortBreakTime = shortBreakTime.toMinutes(), longBreakTime = longBreakTime.toMinutes(), - focusSessions = task.focusSessions + focusSessions = task.focusSessions, ) } minutes", - style = MaterialTheme.typography.bodySmall + style = MaterialTheme.typography.bodySmall, ) Text( text = when (task.current.sessionType()) { SessionType.Focus -> "${focusTime.toMinutes()} min" SessionType.ShortBreak -> "${shortBreakTime.toMinutes()} min" SessionType.LongBreak -> "${longBreakTime.toMinutes()} min" - } + }, ) } } @@ -337,7 +339,7 @@ fun FocusTimeScreenContent( Spacer(modifier = Modifier.height(32.dp)) Row( modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.Center + horizontalArrangement = Arrangement.Center, ) { TaskProgress( percentage = timerValue.toPercentage( @@ -345,12 +347,12 @@ fun FocusTimeScreenContent( SessionType.Focus -> focusTime SessionType.ShortBreak -> shortBreakTime SessionType.LongBreak -> longBreakTime - } + }, ), radius = 40.dp, content = timerValue.toTimer(), mainColor = MaterialTheme.colorScheme.primary, - counterColor = MaterialTheme.colorScheme.onPrimary + counterColor = MaterialTheme.colorScheme.onPrimary, ) } } @@ -365,9 +367,9 @@ fun FocusTimeScreenContent( SessionType.LongBreak -> "Long Break" }, style = MaterialTheme.typography.displaySmall.copy( - color = MaterialTheme.colorScheme.onPrimary + color = MaterialTheme.colorScheme.onPrimary, ), - textAlign = TextAlign.Center + textAlign = TextAlign.Center, ) } @@ -378,7 +380,7 @@ fun FocusTimeScreenContent( state = timerState, onClickReset = onClickReset, onClickNext = onClickNext, - onClickAction = onClickAction + onClickAction = onClickAction, ) } } @@ -389,15 +391,21 @@ fun FocusTimeScreenContent( @OptIn(ExperimentalResourceApi::class) @Composable -fun SuccessfulCompletionOfTask(modifier: Modifier = Modifier, title: String, message: String, onConfirm: () -> Unit) { +fun SuccessfulCompletionOfTask( + modifier: Modifier = Modifier, + title: String, + message: String, + onConfirm: () -> Unit, +) { AlertDialog( modifier = modifier.fillMaxWidth(), shape = MaterialTheme.shapes.large, icon = { Image( modifier = Modifier.size(48.dp), - painter = painterResource("ic_complete.xml"), - contentDescription = "Task Completed" + painter = painterResource( + Res.drawable.ic_complete), + contentDescription = "Task Completed", ) }, containerColor = MaterialTheme.colorScheme.background, @@ -407,8 +415,8 @@ fun SuccessfulCompletionOfTask(modifier: Modifier = Modifier, title: String, mes modifier = Modifier.fillMaxWidth(), text = title, style = MaterialTheme.typography.titleMedium.copy( - textAlign = TextAlign.Center - ) + textAlign = TextAlign.Center, + ), ) }, text = { @@ -416,21 +424,21 @@ fun SuccessfulCompletionOfTask(modifier: Modifier = Modifier, title: String, mes modifier = Modifier.fillMaxWidth(), text = message, style = MaterialTheme.typography.bodyMedium.copy( - textAlign = TextAlign.Center - ) + textAlign = TextAlign.Center, + ), ) }, dismissButton = {}, confirmButton = { Button( modifier = Modifier.fillMaxWidth(), - onClick = onConfirm + onClick = onConfirm, ) { Text( text = "OK", - style = MaterialTheme.typography.titleSmall + style = MaterialTheme.typography.titleSmall, ) } - } + }, ) } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/taskprogress/TaskProgressScreenModel.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/taskprogress/TaskProgressScreenModel.kt index eab87b9..9598ecf 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/taskprogress/TaskProgressScreenModel.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/feature/taskprogress/TaskProgressScreenModel.kt @@ -16,7 +16,7 @@ package com.joelkanyi.focusbloom.feature.taskprogress import cafe.adriel.voyager.core.model.ScreenModel -import cafe.adriel.voyager.core.model.coroutineScope +import cafe.adriel.voyager.core.model.screenModelScope import com.joelkanyi.focusbloom.core.domain.model.SessionType import com.joelkanyi.focusbloom.core.domain.model.Task import com.joelkanyi.focusbloom.core.domain.repository.settings.SettingsRepository @@ -36,30 +36,30 @@ import kotlinx.coroutines.launch class TaskProgressScreenModel( private val settingsRepository: SettingsRepository, private val tasksRepository: TasksRepository, - private val notificationManager: NotificationsManager + private val notificationManager: NotificationsManager, ) : ScreenModel { val shortBreakColor = settingsRepository.shortBreakColor() .map { it } .stateIn( - coroutineScope, + screenModelScope, started = SharingStarted.WhileSubscribed(), - initialValue = null + initialValue = null, ) val longBreakColor = settingsRepository.longBreakColor() .map { it } .stateIn( - coroutineScope, + screenModelScope, started = SharingStarted.WhileSubscribed(), - initialValue = null + initialValue = null, ) val focusColor = settingsRepository.focusColor() .map { it } .stateIn( - coroutineScope, + screenModelScope, started = SharingStarted.WhileSubscribed(), - initialValue = null + initialValue = null, ) val focusTime = settingsRepository.getSessionTime() @@ -67,31 +67,31 @@ class TaskProgressScreenModel( it?.toMillis() ?: (25).toMillis() } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = null + initialValue = null, ) val shortBreakTime = settingsRepository.getShortBreakTime() .map { it?.toMillis() ?: (5).toMillis() } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = null + initialValue = null, ) val longBreakTime = settingsRepository.getLongBreakTime() .map { it?.toMillis() ?: (15).toMillis() } .stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = null + initialValue = null, ) private val _remindersOn = MutableStateFlow(null) - val remindersOn = _remindersOn.asStateFlow() + private val remindersOn = _remindersOn.asStateFlow() fun getRemindersStatus() { - coroutineScope.launch { + screenModelScope.launch { settingsRepository.remindersOn().collectLatest { _remindersOn.value = it == 1 } @@ -101,7 +101,7 @@ class TaskProgressScreenModel( private val _task = MutableStateFlow(null) val task = _task.asStateFlow() fun getTask(taskId: Int) { - coroutineScope.launch { + screenModelScope.launch { tasksRepository.getTask(taskId).collectLatest { _task.value = it } @@ -114,7 +114,7 @@ class TaskProgressScreenModel( * @param consumedTime the consumed time of the focus */ private fun updateConsumedFocusTime(taskId: Int, consumedTime: Long) { - coroutineScope.launch { + screenModelScope.launch { tasksRepository.updateConsumedFocusTime(taskId, consumedTime) } } @@ -125,7 +125,7 @@ class TaskProgressScreenModel( * @param consumedTime the consumed time of the short break */ private fun updateConsumedShortBreakTime(taskId: Int, consumedTime: Long) { - coroutineScope.launch { + screenModelScope.launch { tasksRepository.updateConsumedShortBreakTime(taskId, consumedTime) } } @@ -136,7 +136,7 @@ class TaskProgressScreenModel( * @param consumedTime the consumed time of the long break */ private fun updateConsumedLongBreakTime(taskId: Int, consumedTime: Long) { - coroutineScope.launch { + screenModelScope.launch { tasksRepository.updateConsumedLongBreakTime(taskId, consumedTime) } } @@ -147,13 +147,13 @@ class TaskProgressScreenModel( * @param inProgressTask the in progress task */ private fun updateInProgressTask(taskId: Int, inProgressTask: Boolean) { - coroutineScope.launch { + screenModelScope.launch { tasksRepository.updateTaskInProgress(taskId, inProgressTask) } } fun updateActiveTask(taskId: Int, activeTask: Boolean) { - coroutineScope.launch { + screenModelScope.launch { tasksRepository.updateTaskActive(id = taskId, active = activeTask) } } @@ -164,13 +164,13 @@ class TaskProgressScreenModel( * @param completedTask the completed task */ private fun updateCompletedTask(taskId: Int, completedTask: Boolean) { - coroutineScope.launch { + screenModelScope.launch { tasksRepository.updateTaskCompleted(taskId, completedTask) } } fun resetAllTasksToInactive() { - coroutineScope.launch { + screenModelScope.launch { tasksRepository.updateAllTasksActiveStatusToInactive() } } @@ -181,7 +181,7 @@ class TaskProgressScreenModel( * @param currentCycle the current cycle of the task */ private fun updateCurrentCycle(taskId: Int, currentCycle: Int) { - coroutineScope.launch { + screenModelScope.launch { tasksRepository.updateTaskCycleNumber(taskId, currentCycle) } } @@ -192,7 +192,7 @@ class TaskProgressScreenModel( * @param currentSession the current session of the task */ private fun updateCurrentSession(taskId: Int, currentSession: String) { - coroutineScope.launch { + screenModelScope.launch { tasksRepository.updateCurrentSessionName(taskId, currentSession) } } @@ -201,23 +201,23 @@ class TaskProgressScreenModel( when (task.value?.current) { "Focus" -> updateConsumedFocusTime( task.value?.id ?: -1, - Timer.tickingTime.value + Timer.tickingTime.value, ) "ShortBreak" -> updateConsumedShortBreakTime( task.value?.id ?: -1, - Timer.tickingTime.value + Timer.tickingTime.value, ) "LongBreak" -> updateConsumedLongBreakTime( task.value?.id ?: -1, - Timer.tickingTime.value + Timer.tickingTime.value, ) } } fun executeTasks() { - coroutineScope.launch { + screenModelScope.launch { if (task.value?.currentCycle?.equals(0) == true) { println("executeTasks: first cycle") updateCurrentCycle(task.value?.id ?: 0, 1) @@ -230,7 +230,7 @@ class TaskProgressScreenModel( }, executeTasks = { executeTasks() - } + }, ) } else { when (task.value?.current) { @@ -241,7 +241,7 @@ class TaskProgressScreenModel( title = "[TASK] ${task.value?.name}", description = "${ task.value?.currentCycle?.formattedNumber() - } Focus Session Completed, going for a long break" + } Focus Session Completed, going for a long break", ) } @@ -254,13 +254,13 @@ class TaskProgressScreenModel( }, executeTasks = { executeTasks() - } + }, ) } else { if (remindersOn.value == true) { notificationManager.showNotification( title = "[TASK] ${task.value?.name}", - description = "${task.value?.currentCycle?.formattedNumber()} focus session completed, going for a short break" + description = "${task.value?.currentCycle?.formattedNumber()} focus session completed, going for a short break", ) } @@ -273,7 +273,7 @@ class TaskProgressScreenModel( }, executeTasks = { executeTasks() - } + }, ) } } @@ -284,16 +284,16 @@ class TaskProgressScreenModel( title = "[TASK] ${task.value?.name}", description = "Short Break Completed, going for the ${ task.value?.currentCycle?.plus( - 1 + 1, )?.formattedNumber() - } focus session" + } focus session", ) } updateCurrentSession(task.value?.id ?: 0, "Focus") updateCurrentCycle( task.value?.id ?: 0, - task.value?.currentCycle?.plus(1) ?: (0 + 1) + task.value?.currentCycle?.plus(1) ?: (0 + 1), ) updateInProgressTask(task.value?.id ?: 0, true) Timer.setTickingTime(focusTime.value ?: 0L) @@ -303,7 +303,7 @@ class TaskProgressScreenModel( }, executeTasks = { executeTasks() - } + }, ) } @@ -311,7 +311,7 @@ class TaskProgressScreenModel( if (remindersOn.value == true) { notificationManager.showNotification( title = "[TASK] ${task.value?.name}", - description = "Good Job, you have completed this task \uD83C\uDF89\uD83C\uDF89" + description = "Good Job, you have completed this task \uD83C\uDF89\uD83C\uDF89", ) } @@ -328,7 +328,7 @@ class TaskProgressScreenModel( } fun moveToNextSessionOfTheTask() { - coroutineScope.launch { + screenModelScope.launch { when (task.value?.current.sessionType()) { SessionType.Focus -> { if (task.value?.currentCycle == task.value?.focusSessions) { @@ -341,7 +341,7 @@ class TaskProgressScreenModel( }, executeTasks = { executeTasks() - } + }, ) } else { updateCurrentSession(task.value?.id ?: 0, "ShortBreak") @@ -353,7 +353,7 @@ class TaskProgressScreenModel( }, executeTasks = { executeTasks() - } + }, ) } } @@ -371,7 +371,7 @@ class TaskProgressScreenModel( updateCurrentSession(task.value?.id ?: 0, "Focus") updateCurrentCycle( task.value?.id ?: 0, - task.value?.currentCycle?.plus(1) ?: (0 + 1) + task.value?.currentCycle?.plus(1) ?: (0 + 1), ) updateInProgressTask(task.value?.id ?: 0, true) Timer.setTickingTime(focusTime.value ?: 0L) @@ -381,7 +381,7 @@ class TaskProgressScreenModel( }, executeTasks = { executeTasks() - } + }, ) } } @@ -389,7 +389,7 @@ class TaskProgressScreenModel( } fun resetCurrentSessionOfTheTask() { - coroutineScope.launch { + screenModelScope.launch { when (task.value?.current.sessionType()) { SessionType.Focus -> { updateCurrentSession(task.value?.id ?: 0, "Focus") @@ -401,7 +401,7 @@ class TaskProgressScreenModel( }, executeTasks = { executeTasks() - } + }, ) } @@ -424,15 +424,10 @@ class TaskProgressScreenModel( }, executeTasks = { executeTasks() - } + }, ) } } } } } - -sealed class ReminderState { - data object Loading : ReminderState() - data class Success(val reminderOn: Boolean) : ReminderState() -} diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/main/MainScreen.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/main/MainScreen.kt index 7b8ffb2..e6ed7c5 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/main/MainScreen.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/main/MainScreen.kt @@ -56,7 +56,7 @@ class MainScreen : Screen { val useNavRail = windowSizeClass.widthSizeClass > WindowWidthSizeClass.Compact TabNavigator( - BloomTab.HomeTab + BloomTab.HomeTab, ) { val tabNavigator = LocalTabNavigator.current @@ -69,8 +69,8 @@ class MainScreen : Screen { BloomTab.CalendarTab, BloomTab.AddTaskTab(), BloomTab.StatisticsTab, - BloomTab.SettingsTab - ) + BloomTab.SettingsTab, + ), ) CurrentScreen() } @@ -79,7 +79,7 @@ class MainScreen : Screen { content = { innerPadding -> Box( modifier = Modifier - .padding(innerPadding) + .padding(innerPadding), ) { CurrentScreen() } @@ -95,28 +95,28 @@ class MainScreen : Screen { tabNavigator.current = BloomTab.AddTaskTab() }, elevation = FloatingActionButtonDefaults.elevation( - defaultElevation = 0.dp + defaultElevation = 0.dp, ), - shape = CircleShape + shape = CircleShape, ) { Icon( imageVector = Icons.Filled.Add, contentDescription = "", tint = MaterialTheme.colorScheme.onPrimary, - modifier = Modifier.size(24.dp) + modifier = Modifier.size(24.dp), ) } }, bottomBar = { BottomNavigation( - backgroundColor = MaterialTheme.colorScheme.background + backgroundColor = MaterialTheme.colorScheme.background, ) { TabNavigationItem(BloomTab.HomeTab) TabNavigationItem(BloomTab.CalendarTab) TabNavigationItem(BloomTab.StatisticsTab) TabNavigationItem(BloomTab.SettingsTab) } - } + }, ) } } @@ -136,7 +136,7 @@ private fun RowScope.TabNavigationItem(tab: Tab) { (2u).toUShort() -> 24.dp (3u).toUShort() -> 0.dp else -> 0.dp - } + }, ), selected = tabNavigator.current == tab, onClick = { tabNavigator.current = tab }, @@ -153,9 +153,9 @@ private fun RowScope.TabNavigationItem(tab: Tab) { MaterialTheme.colorScheme.primary } else { MaterialTheme.colorScheme.onBackground - } + }, ) } - } + }, ) } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/main/MainViewModel.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/main/MainViewModel.kt index 187c22b..fcec337 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/main/MainViewModel.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/main/MainViewModel.kt @@ -16,7 +16,7 @@ package com.joelkanyi.focusbloom.main import cafe.adriel.voyager.core.model.ScreenModel -import cafe.adriel.voyager.core.model.coroutineScope +import cafe.adriel.voyager.core.model.screenModelScope import com.joelkanyi.focusbloom.core.domain.repository.settings.SettingsRepository import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -24,22 +24,22 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn class MainViewModel( - settingsRepository: SettingsRepository + settingsRepository: SettingsRepository, ) : ScreenModel { val appTheme: StateFlow = settingsRepository.getAppTheme().map { it }.stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(), - initialValue = null + initialValue = null, ) val onBoardingCompleted: StateFlow = settingsRepository.getUsername().map { OnBoardingState.Success(it.isNullOrEmpty().not()) }.stateIn( - scope = coroutineScope, + scope = screenModelScope, started = SharingStarted.WhileSubscribed(), - initialValue = OnBoardingState.Loading + initialValue = OnBoardingState.Loading, ) } diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/platform/Font.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/platform/Font.kt deleted file mode 100644 index 08037ec..0000000 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/platform/Font.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2023 Joel Kanyi. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.joelkanyi.focusbloom.platform - -import androidx.compose.runtime.Composable -import androidx.compose.ui.text.font.Font -import androidx.compose.ui.text.font.FontStyle -import androidx.compose.ui.text.font.FontWeight - -@Composable -expect fun font(name: String, res: String, weight: FontWeight, style: FontStyle): Font diff --git a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/platform/NotificationsManager.kt b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/platform/NotificationsManager.kt index 342665a..874fec7 100644 --- a/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/platform/NotificationsManager.kt +++ b/shared/src/commonMain/kotlin/com/joelkanyi/focusbloom/platform/NotificationsManager.kt @@ -18,6 +18,6 @@ package com.joelkanyi.focusbloom.platform expect class NotificationsManager { fun showNotification( title: String, - description: String + description: String, ) } diff --git a/shared/src/commonMain/sqldelight/database/task.sq b/shared/src/commonMain/sqldelight/database/task.sq index 9806c5a..2bd256f 100644 --- a/shared/src/commonMain/sqldelight/database/task.sq +++ b/shared/src/commonMain/sqldelight/database/task.sq @@ -5,7 +5,7 @@ import kotlin.String; -CREATE TABLE taskEntity ( +CREATE TABLE IF NOT EXISTS taskEntity ( id INTEGER AS Int NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, description TEXT, diff --git a/shared/src/iosMain/kotlin/com/joelkanyi/focusbloom/di/Module.kt b/shared/src/iosMain/kotlin/com/joelkanyi/focusbloom/di/Module.kt index 7aa7299..ab703a9 100644 --- a/shared/src/iosMain/kotlin/com/joelkanyi/focusbloom/di/Module.kt +++ b/shared/src/iosMain/kotlin/com/joelkanyi/focusbloom/di/Module.kt @@ -18,11 +18,9 @@ package com.joelkanyi.focusbloom.di import com.joelkanyi.focusbloom.platform.DatabaseDriverFactory import com.joelkanyi.focusbloom.platform.MultiplatformSettingsWrapper import com.joelkanyi.focusbloom.platform.NotificationsManager -import com.russhwolf.settings.ExperimentalSettingsApi import org.koin.core.module.Module import org.koin.dsl.module -@OptIn(ExperimentalSettingsApi::class) actual fun platformModule(): Module = module { single { MultiplatformSettingsWrapper().createSettings() } single { DatabaseDriverFactory() } diff --git a/shared/src/iosMain/kotlin/com/joelkanyi/focusbloom/platform/Font.ios.kt b/shared/src/iosMain/kotlin/com/joelkanyi/focusbloom/platform/Font.ios.kt deleted file mode 100644 index 4224e30..0000000 --- a/shared/src/iosMain/kotlin/com/joelkanyi/focusbloom/platform/Font.ios.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2023 Joel Kanyi. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.joelkanyi.focusbloom.platform - -import androidx.compose.runtime.Composable -import androidx.compose.ui.text.font.Font -import androidx.compose.ui.text.font.FontStyle -import androidx.compose.ui.text.font.FontWeight -import kotlinx.coroutines.runBlocking -import org.jetbrains.compose.resources.ExperimentalResourceApi -import org.jetbrains.compose.resources.resource - -private val cache: MutableMap = mutableMapOf() - -@OptIn(ExperimentalResourceApi::class) -@Composable -actual fun font(name: String, res: String, weight: FontWeight, style: FontStyle): Font { - return cache.getOrPut(res) { - val byteArray = runBlocking { resource("font/$res.ttf").readBytes() } - androidx.compose.ui.text.platform.Font(res, byteArray, weight, style) - } -} diff --git a/shared/src/iosMain/kotlin/com/joelkanyi/focusbloom/platform/NotificationsManager.ios.kt b/shared/src/iosMain/kotlin/com/joelkanyi/focusbloom/platform/NotificationsManager.ios.kt index 7110d60..1d91cfc 100644 --- a/shared/src/iosMain/kotlin/com/joelkanyi/focusbloom/platform/NotificationsManager.ios.kt +++ b/shared/src/iosMain/kotlin/com/joelkanyi/focusbloom/platform/NotificationsManager.ios.kt @@ -46,7 +46,7 @@ actual class NotificationsManager { override fun userNotificationCenter( center: UNUserNotificationCenter, didReceiveNotificationResponse: UNNotificationResponse, - withCompletionHandler: () -> Unit + withCompletionHandler: () -> Unit, ) { withCompletionHandler() } @@ -54,7 +54,7 @@ actual class NotificationsManager { override fun userNotificationCenter( center: UNUserNotificationCenter, willPresentNotification: UNNotification, - withCompletionHandler: (UNNotificationPresentationOptions) -> Unit + withCompletionHandler: (UNNotificationPresentationOptions) -> Unit, ) { withCompletionHandler(UNNotificationPresentationOptionAlert) } diff --git a/shared/src/iosMain/kotlin/com/joelkanyi/focusbloom/platform/StatusBarColors.ios.kt b/shared/src/iosMain/kotlin/com/joelkanyi/focusbloom/platform/StatusBarColors.ios.kt index 98f0f3c..7f23bd2 100644 --- a/shared/src/iosMain/kotlin/com/joelkanyi/focusbloom/platform/StatusBarColors.ios.kt +++ b/shared/src/iosMain/kotlin/com/joelkanyi/focusbloom/platform/StatusBarColors.ios.kt @@ -37,9 +37,7 @@ private fun statusBarView() = remember { val tag = 3848245L // https://stackoverflow.com/questions/56651245/how-to-change-the-status-bar-background-color-and-text-color-on-ios-13 - keyWindow?.viewWithTag(tag)?.let { - it - } ?: run { + keyWindow?.viewWithTag(tag) ?: run { val height = keyWindow?.windowScene?.statusBarManager?.statusBarFrame ?: zeroValue() val statusBarView = UIView(frame = height) @@ -63,5 +61,5 @@ private fun Color.toUIColor(): UIColor = UIColor( red = this.red.toDouble(), green = this.green.toDouble(), blue = this.blue.toDouble(), - alpha = this.alpha.toDouble() + alpha = this.alpha.toDouble(), ) diff --git a/shared/src/jvmMain/kotlin/com/joelkanyi/focusbloom/di/Module.kt b/shared/src/jvmMain/kotlin/com/joelkanyi/focusbloom/di/Module.kt index 7aa7299..ab703a9 100644 --- a/shared/src/jvmMain/kotlin/com/joelkanyi/focusbloom/di/Module.kt +++ b/shared/src/jvmMain/kotlin/com/joelkanyi/focusbloom/di/Module.kt @@ -18,11 +18,9 @@ package com.joelkanyi.focusbloom.di import com.joelkanyi.focusbloom.platform.DatabaseDriverFactory import com.joelkanyi.focusbloom.platform.MultiplatformSettingsWrapper import com.joelkanyi.focusbloom.platform.NotificationsManager -import com.russhwolf.settings.ExperimentalSettingsApi import org.koin.core.module.Module import org.koin.dsl.module -@OptIn(ExperimentalSettingsApi::class) actual fun platformModule(): Module = module { single { MultiplatformSettingsWrapper().createSettings() } single { DatabaseDriverFactory() } diff --git a/shared/src/jvmMain/kotlin/com/joelkanyi/focusbloom/notification/linux/LinuxNotificationProvider.kt b/shared/src/jvmMain/kotlin/com/joelkanyi/focusbloom/notification/linux/LinuxNotificationProvider.kt index 931c94c..65fa65c 100644 --- a/shared/src/jvmMain/kotlin/com/joelkanyi/focusbloom/notification/linux/LinuxNotificationProvider.kt +++ b/shared/src/jvmMain/kotlin/com/joelkanyi/focusbloom/notification/linux/LinuxNotificationProvider.kt @@ -224,7 +224,7 @@ object LinuxNotificationProvider : NotificationProvider { @Suppress("FunctionName") private fun LibNotify.notify_notification_set_desktop_entry( notification: Pointer, - desktopEntry: String + desktopEntry: String, ) { val string = g_variant_new_string(desktopEntry) notify_notification_set_hint(notification, "desktop-entry", string) // NON-NLS @@ -233,7 +233,7 @@ object LinuxNotificationProvider : NotificationProvider { @Suppress("FunctionName") private fun LibNotify.notify_notification_set_suppress_sound( notification: Pointer, - suppressSound: Boolean + suppressSound: Boolean, ) { val bool = g_variant_new_boolean(suppressSound) notify_notification_set_hint(notification, "suppress-sound", bool) // NON-NLS diff --git a/shared/src/jvmMain/kotlin/com/joelkanyi/focusbloom/notification/windows/Toast4jNotificationProvider.kt b/shared/src/jvmMain/kotlin/com/joelkanyi/focusbloom/notification/windows/Toast4jNotificationProvider.kt index 1cfdf72..06a29cf 100644 --- a/shared/src/jvmMain/kotlin/com/joelkanyi/focusbloom/notification/windows/Toast4jNotificationProvider.kt +++ b/shared/src/jvmMain/kotlin/com/joelkanyi/focusbloom/notification/windows/Toast4jNotificationProvider.kt @@ -57,7 +57,7 @@ object Toast4jNotificationProvider : NotificationProvider { ToastBuilder(WinToastTemplate.WinToastTemplateType.ToastText01).setSilent() .setLine1(title) .setLine2(description) - .build() + .build(), ) } } diff --git a/shared/src/jvmMain/kotlin/com/joelkanyi/focusbloom/platform/DatabaseDriverFactory.jvm.kt b/shared/src/jvmMain/kotlin/com/joelkanyi/focusbloom/platform/DatabaseDriverFactory.jvm.kt index 68738db..7f6bccb 100644 --- a/shared/src/jvmMain/kotlin/com/joelkanyi/focusbloom/platform/DatabaseDriverFactory.jvm.kt +++ b/shared/src/jvmMain/kotlin/com/joelkanyi/focusbloom/platform/DatabaseDriverFactory.jvm.kt @@ -18,10 +18,45 @@ package com.joelkanyi.focusbloom.platform import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver import com.joelkanyi.focusbloom.database.BloomDatabase +import java.io.File actual class DatabaseDriverFactory { actual fun createDriver(): SqlDriver { - return JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY) - .also { BloomDatabase.Schema.create(it) } + return JdbcSqliteDriver( + url = "jdbc:sqlite:${databaseFile.absolutePath}", + ).also { db -> + BloomDatabase.Schema.create(db) + } } + + private val databaseFile: File + get() = File(appDir.also { if (!it.exists()) it.mkdirs() }, "bloom.db") + + private val appDir: File + get() { + val os = System.getProperty("os.name").lowercase() + return when { + os.contains("win") -> { + File( + System.getenv("AppData"), + "bloom/db" + ) // "C:\Users\AppData\Roaming\bloom\db" + } + + os.contains("nix") || os.contains("nux") || os.contains("aix") -> { + File( + System.getProperty("user.home"), ".bloom" + ) // "/home//.bloom" + } + + os.contains("mac") -> { + File( + System.getProperty("user.home"), + "Library/Application Support/bloom" + ) // "/Users//Library/Application Support/bloom" + } + + else -> error("Unsupported operating system") + } + } } diff --git a/shared/src/jvmMain/kotlin/com/joelkanyi/focusbloom/platform/Font.jvm.kt b/shared/src/jvmMain/kotlin/com/joelkanyi/focusbloom/platform/Font.jvm.kt deleted file mode 100644 index da929a3..0000000 --- a/shared/src/jvmMain/kotlin/com/joelkanyi/focusbloom/platform/Font.jvm.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2023 Joel Kanyi. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.joelkanyi.focusbloom.platform - -import androidx.compose.runtime.Composable -import androidx.compose.ui.text.font.Font -import androidx.compose.ui.text.font.FontStyle -import androidx.compose.ui.text.font.FontWeight - -@Composable -actual fun font(name: String, res: String, weight: FontWeight, style: FontStyle): Font = androidx.compose.ui.text.platform.Font("font/$res.ttf", weight, style) diff --git a/shared/src/jvmMain/kotlin/com/joelkanyi/focusbloom/platform/NotificationsManager.jvm.kt b/shared/src/jvmMain/kotlin/com/joelkanyi/focusbloom/platform/NotificationsManager.jvm.kt index 88a9355..ad14c6f 100644 --- a/shared/src/jvmMain/kotlin/com/joelkanyi/focusbloom/platform/NotificationsManager.jvm.kt +++ b/shared/src/jvmMain/kotlin/com/joelkanyi/focusbloom/platform/NotificationsManager.jvm.kt @@ -28,21 +28,21 @@ actual class NotificationsManager { hostOs.startsWith("Windows") -> { Toast4jNotificationProvider.sendNotification( title = title, - description = description + description = description, ) } hostOs.startsWith("Mac") -> { MacOsNotificationProvider.sendNotification( title = title, - description = description + description = description, ) } hostOs.startsWith("Linux") -> { LinuxNotificationProvider.sendNotification( title = title, - description = description + description = description, ) } diff --git a/shared/src/jvmMain/kotlin/com/joelkanyi/focusbloom/utils/AudioUtils.kt b/shared/src/jvmMain/kotlin/com/joelkanyi/focusbloom/utils/AudioUtils.kt index 14c0f36..759f6bd 100644 --- a/shared/src/jvmMain/kotlin/com/joelkanyi/focusbloom/utils/AudioUtils.kt +++ b/shared/src/jvmMain/kotlin/com/joelkanyi/focusbloom/utils/AudioUtils.kt @@ -24,7 +24,8 @@ object AudioUtils { fun loadAudioFromResource(name: String): Clip? { val resourceStream = getResourceAsStream(name) ?: return null - val bufferedStream = BufferedInputStream(resourceStream) // add buffer for mark/reset support + val bufferedStream = + BufferedInputStream(resourceStream) // add buffer for mark/reset support val audioInputStream = AudioSystem.getAudioInputStream(bufferedStream) val sound = AudioSystem.getClip() sound.open(audioInputStream)