diff --git a/.github/workflows/checkStyle.yml b/.github/workflows/checkStyle.yml
new file mode 100644
index 0000000..2cdf5c6
--- /dev/null
+++ b/.github/workflows/checkStyle.yml
@@ -0,0 +1,39 @@
+name: Check Style
+
+on:
+ pull_request:
+
+env:
+ GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx3g"
+
+jobs:
+ checkStyle:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v1
+
+ - name: Setup JDK 17
+ uses: actions/setup-java@v3
+ with:
+ java-version: '17'
+ distribution: 'adopt'
+
+ - name: Setup Gradle
+ uses: gradle/gradle-build-action@v2
+
+ - name: Validate Gradle Wrapper
+ uses: gradle/wrapper-validation-action@v1
+
+ - name: Grant execute permission for gradlew
+ run: chmod +x gradlew
+
+ - name: Detekt
+ run: ./gradlew detekt
+
+ - name: Upload detekt SARIF files
+ if: always()
+ uses: github/codeql-action/upload-sarif@v2
+ with:
+ sarif_file: 'build/reports/detekt/merge.sarif'
+ category: detekt
\ No newline at end of file
diff --git a/.github/workflows/publishAndroid.yml b/.github/workflows/publishAndroid.yml
index 12c6228..e0b5e3b 100644
--- a/.github/workflows/publishAndroid.yml
+++ b/.github/workflows/publishAndroid.yml
@@ -1,4 +1,7 @@
name: Publish Android
+concurrency: # Cancel currently running releases when a new one is started
+ group: publish-android
+ cancel-in-progress: true
on:
push:
branches:
@@ -19,10 +22,10 @@ jobs:
- name: Checkout repo
uses: actions/checkout@v3
- - name: set up JDK 11
+ - name: set up JDK 17
uses: actions/setup-java@v3
with:
- java-version: '11'
+ java-version: '17'
distribution: 'adopt'
- name: Setup Gradle
@@ -55,11 +58,10 @@ jobs:
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx3g"
- name: Create version tag
- if: ${{ github.ref_name == 'main' }}
run: |
git tag android-$(cat androidApp/build/version.tag)
- git push --tags
+ git push origin android-$(cat androidApp/build/version.tag)
# TODO auto increase androidPatch in version.properties (on prod build only) and push (to dev branch, republish must be prevented then)?
- # TODO add discord or other notification
\ No newline at end of file
+ # TODO add discord or other notification
diff --git a/.github/workflows/publishShared.yml b/.github/workflows/publishShared.yml
index c208bd5..5bb8d4f 100644
--- a/.github/workflows/publishShared.yml
+++ b/.github/workflows/publishShared.yml
@@ -1,4 +1,7 @@
name: Publish Shared Swift Package
+concurrency: # Cancel currently running releases when a new one is started
+ group: publish-shared
+ cancel-in-progress: true
on:
workflow_dispatch: # allow manually running the workflow for the dev and main branch
branches:
@@ -17,8 +20,8 @@ on:
jobs:
call-kmmbridge-publish:
- uses: touchlab/KMMBridgeGithubWorkflow/.github/workflows/faktorybuildbranches.yml@v0.8
+ uses: touchlab/KMMBridgeGithubWorkflow/.github/workflows/faktorybuildbranches.yml@2e121ace461e0004eb079926c1f6e74afaee3e3d
with:
- jvmVersion: 11
+ jvmVersion: 17
secrets:
gradle_params: "-PGITHUB_BRANCH=${{ github.ref_name }}"
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 1afc827..38a9d88 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -15,10 +15,10 @@ jobs:
- name: Checkout repo
uses: actions/checkout@v3
- - name: set up JDK 11
+ - name: set up JDK 17
uses: actions/setup-java@v3
with:
- java-version: '11'
+ java-version: '17'
distribution: 'adopt'
- name: Setup Gradle
diff --git a/README.md b/README.md
index 596440f..dcf22ee 100644
--- a/README.md
+++ b/README.md
@@ -97,7 +97,7 @@ and [build.gradle.kts (shared)](shared/build.gradle.kts) kmmbridge config)
# Further Resources and References
- [Kotlin/kmm-sample](https://github.com/Kotlin/kmm-sample)
-- [KaMPKit](https://github.com/touchlab/KaMPKit) Collection of code and tools for getting stated
+- [KaMPKit](https://github.com/touchlab/KaMPKit) Collection of code and tools for getting started
with KMP/KMM
- [KMMBridge SPM sample (android + shared)](https://github.com/touchlab/KMMBridgeSampleKotlin)
- [KMMBridge SPM sample (iOS)](https://github.com/touchlab/KMMBridgeSampleSpm)
diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts
index 815cd5e..7ddcdc7 100644
--- a/androidApp/build.gradle.kts
+++ b/androidApp/build.gradle.kts
@@ -6,9 +6,9 @@ import java.util.Properties
plugins {
id("com.android.application")
- id("com.github.triplet.play") version "3.6.0"
+ id("com.github.triplet.play") version "3.8.4"
kotlin("android")
- id("com.google.devtools.ksp") version "1.8.10-1.0.9"
+ id("com.google.devtools.ksp") version "1.9.0-1.0.13"
}
private val versionProperty by lazy {
@@ -23,6 +23,10 @@ android {
namespace = "org.datepollsystems.waiterrobot.android"
compileSdk = Versions.androidCompileSdk
+ androidResources {
+ generateLocaleConfig = true
+ }
+
defaultConfig {
applicationId = "org.datepollsystems.waiterrobot.android"
@@ -73,21 +77,22 @@ android {
buildFeatures {
compose = true
+ buildConfig = true
}
composeOptions {
kotlinCompilerExtensionVersion = Versions.composeCompiler
}
- packagingOptions {
+ packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
compileOptions {
- sourceCompatibility = JavaVersion.VERSION_1_8
- targetCompatibility = JavaVersion.VERSION_1_8
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
isCoreLibraryDesugaringEnabled = true
}
@@ -97,7 +102,6 @@ android {
create("lava") {
dimension = "environment"
applicationIdSuffix = ".lava"
- resValue("string", "app_name", "WaiterRobot Lava")
buildConfigField("String", "API_BASE", "\"https://lava.kellner.team/api\"")
manifestPlaceholders["host"] = "lava.kellner.team"
@@ -107,13 +111,12 @@ android {
// -> use epochMinutes (overflow would be in 5962).
// (conversion to int is save as java int is bigger as the max versionCode allowed by google play)
val epochMinutes = (Date().toInstant().epochSecond / 60).toInt()
- versionNameSuffix = "-lava-${epochMinutes}"
+ versionNameSuffix = "-lava-$epochMinutes"
versionCode = epochMinutes
}
create("prod") {
dimension = "environment"
- resValue("string", "app_name", "WaiterRobot")
buildConfigField("String", "API_BASE", "\"https://my.kellner.team/api\"")
manifestPlaceholders["host"] = "my.kellner.team"
}
@@ -158,11 +161,11 @@ dependencies {
implementation("androidx.lifecycle:lifecycle-runtime-ktx:${Versions.androidxLifecycle}")
implementation("androidx.appcompat:appcompat:1.6.1")
- coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.2")
+ coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.3")
// Compose
runtimeOnly("androidx.compose.compiler:compiler:${Versions.composeCompiler}")
- implementation("androidx.activity:activity-compose:1.6.1")
+ implementation("androidx.activity:activity-compose:1.7.2")
implementation("androidx.compose.foundation:foundation:${Versions.compose}")
implementation("androidx.compose.foundation:foundation-layout:${Versions.compose}")
implementation("androidx.compose.ui:ui-graphics:${Versions.compose}")
@@ -174,13 +177,13 @@ dependencies {
implementation("androidx.compose.material:material-icons-extended:${Versions.compose}")
// Compose helpers
- implementation("com.google.accompanist:accompanist-permissions:0.28.0")
+ implementation("com.google.accompanist:accompanist-permissions:0.32.0")
// Architecture (MVI)
implementation("org.orbit-mvi:orbit-compose:${Versions.orbitMvi}")
// Dependency injection
- implementation("io.insert-koin:koin-androidx-compose:3.4.2") // Not aligned with other koin version
+ implementation("io.insert-koin:koin-androidx-compose:3.4.6") // Not aligned with other koin version
// SafeCompose Navigation Args
implementation("io.github.raamcosta.compose-destinations:core:${Versions.composeDestinations}")
@@ -192,7 +195,7 @@ dependencies {
implementation("androidx.camera:camera-lifecycle:${Versions.camera}")
// QrCode Scanning
- implementation("com.google.mlkit:barcode-scanning:17.0.3")
+ implementation("com.google.mlkit:barcode-scanning:17.2.0")
// In-App-Update support
implementation("com.google.android.play:app-update:2.1.0")
diff --git a/androidApp/src/lava/res/values-de/strings.xml b/androidApp/src/lava/res/values-de/strings.xml
new file mode 100644
index 0000000..e8eb84b
--- /dev/null
+++ b/androidApp/src/lava/res/values-de/strings.xml
@@ -0,0 +1,4 @@
+
+
+ lava kellner.team
+
\ No newline at end of file
diff --git a/androidApp/src/lava/res/values/strings.xml b/androidApp/src/lava/res/values/strings.xml
new file mode 100644
index 0000000..b4d129a
--- /dev/null
+++ b/androidApp/src/lava/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+
+ lava waiters.team
+
diff --git a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/MainActivity.kt b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/MainActivity.kt
index c973eb1..30623f6 100644
--- a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/MainActivity.kt
+++ b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/MainActivity.kt
@@ -19,6 +19,7 @@ import kotlinx.datetime.Instant
import kotlinx.datetime.until
import org.datepollsystems.waiterrobot.android.ui.RootView
import org.datepollsystems.waiterrobot.shared.core.CommonApp
+import org.datepollsystems.waiterrobot.shared.core.CommonApp.MIN_UPDATE_INFO_HOURS
import org.datepollsystems.waiterrobot.shared.features.settings.models.AppTheme
import org.datepollsystems.waiterrobot.shared.root.RootViewModel
import org.datepollsystems.waiterrobot.shared.utils.extensions.defaultOnNull
@@ -74,11 +75,11 @@ class MainActivity : AppCompatActivity() {
val appUpdateInfoTask = appUpdateManager.appUpdateInfo
appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
if (
- appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
- && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)
- && CommonApp.settings.lastUpdateAvailableNote // Show max once a day
+ appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE &&
+ appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) &&
+ CommonApp.settings.lastUpdateAvailableNote // Show max once a day
.defaultOnNull(Instant.DISTANT_PAST)
- .until(Clock.System.now(), DateTimeUnit.HOUR) > 24
+ .until(Clock.System.now(), DateTimeUnit.HOUR) > MIN_UPDATE_INFO_HOURS
) {
CommonApp.settings.lastUpdateAvailableNote = Clock.System.now()
appUpdateManager.startUpdateFlow(
diff --git a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/billing/BillingScreen.kt b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/billing/BillingScreen.kt
index 94aabf9..74d8d17 100644
--- a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/billing/BillingScreen.kt
+++ b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/billing/BillingScreen.kt
@@ -28,8 +28,8 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.navigation.NavController
import com.ramcosta.composedestinations.annotation.Destination
+import org.datepollsystems.waiterrobot.android.ui.common.CenteredText
import org.datepollsystems.waiterrobot.android.ui.common.FloatingActionButton
-import org.datepollsystems.waiterrobot.android.ui.core.CenteredText
import org.datepollsystems.waiterrobot.android.ui.core.handleSideEffects
import org.datepollsystems.waiterrobot.android.ui.core.view.ScaffoldView
import org.datepollsystems.waiterrobot.shared.core.viewmodel.ViewState
diff --git a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/billing/PayBillDialog.kt b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/billing/PayBillDialog.kt
index 3f9b1fe..bf54e20 100644
--- a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/billing/PayBillDialog.kt
+++ b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/billing/PayBillDialog.kt
@@ -1,8 +1,16 @@
package org.datepollsystems.waiterrobot.android.ui.billing
-import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardOptions
-import androidx.compose.material.*
+import androidx.compose.material.Button
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.OutlinedTextField
+import androidx.compose.material.Surface
+import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -15,8 +23,8 @@ import org.datepollsystems.waiterrobot.shared.generated.localization.change
import org.datepollsystems.waiterrobot.shared.generated.localization.pay
import org.datepollsystems.waiterrobot.shared.generated.localization.total
-@Composable
// TODO own Screen?
+@Composable
fun PayBillDialog(
priceSum: String,
changeText: String,
diff --git a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/common/CenteredText.kt b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/common/CenteredText.kt
index e7d96ec..90adaa8 100644
--- a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/common/CenteredText.kt
+++ b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/common/CenteredText.kt
@@ -1,4 +1,4 @@
-package org.datepollsystems.waiterrobot.android.ui.core
+package org.datepollsystems.waiterrobot.android.ui.common
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
diff --git a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/common/FloatingActionButton.kt b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/common/FloatingActionButton.kt
index b58710e..8227a51 100644
--- a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/common/FloatingActionButton.kt
+++ b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/common/FloatingActionButton.kt
@@ -2,7 +2,11 @@ package org.datepollsystems.waiterrobot.android.ui.common
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.shape.CornerSize
-import androidx.compose.material.*
+import androidx.compose.material.ContentAlpha
+import androidx.compose.material.FloatingActionButtonDefaults
+import androidx.compose.material.FloatingActionButtonElevation
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.contentColorFor
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
@@ -10,8 +14,8 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.compositeOver
-@Composable
/* Wrapper for FloatingActionButton which allows to disable the button */
+@Composable
fun FloatingActionButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
@@ -23,7 +27,9 @@ fun FloatingActionButton(
elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
content: @Composable () -> Unit
) {
- val realOnclick = if (enabled) onClick else {
+ val realOnclick = if (enabled) {
+ onClick
+ } else {
{}
}
val realBackgroundColor = if (enabled) {
diff --git a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/common/LinkText.kt b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/common/LinkText.kt
index cdbcd18..0266fb9 100644
--- a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/common/LinkText.kt
+++ b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/common/LinkText.kt
@@ -43,4 +43,4 @@ fun LinkText(modifier: Modifier = Modifier, text: String, url: String) {
}
}
)
-}
\ No newline at end of file
+}
diff --git a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/common/SingleSelectDialog.kt b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/common/SingleSelectDialog.kt
index 1029e33..d218b68 100644
--- a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/common/SingleSelectDialog.kt
+++ b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/common/SingleSelectDialog.kt
@@ -1,6 +1,11 @@
package org.datepollsystems.waiterrobot.android.ui.common
-import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.selection.selectable
@@ -77,4 +82,4 @@ private fun RadioButtonRow(
modifier = Modifier.padding(start = 16.dp)
)
}
-}
\ No newline at end of file
+}
diff --git a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/common/SwipeableListItem.kt b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/common/SwipeableListItem.kt
index 7a777bf..db6bf9f 100644
--- a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/common/SwipeableListItem.kt
+++ b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/common/SwipeableListItem.kt
@@ -6,11 +6,24 @@ import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
-import androidx.compose.foundation.layout.*
-import androidx.compose.material.*
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.DismissDirection
+import androidx.compose.material.DismissValue
+import androidx.compose.material.ExperimentalMaterialApi
+import androidx.compose.material.FractionalThreshold
+import androidx.compose.material.Icon
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.SwipeToDismiss
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Remove
+import androidx.compose.material.rememberDismissState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
@@ -120,4 +133,4 @@ fun SwipeableListItem(
content()
}
}
-}
\ No newline at end of file
+}
diff --git a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/core/Navigation.kt b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/core/Navigation.kt
index 872030e..371a862 100644
--- a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/core/Navigation.kt
+++ b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/core/Navigation.kt
@@ -16,7 +16,7 @@ import org.datepollsystems.waiterrobot.shared.core.navigation.Screen
import org.datepollsystems.waiterrobot.shared.core.viewmodel.AbstractViewModel
import org.datepollsystems.waiterrobot.shared.core.viewmodel.ViewModelEffect
import org.datepollsystems.waiterrobot.shared.core.viewmodel.ViewModelState
-import org.koin.androidx.compose.get
+import org.koin.compose.koinInject
import org.koin.core.parameter.parametersOf
import org.orbitmvi.orbit.compose.collectSideEffect
@@ -26,7 +26,7 @@ fun AbstractViewModel.handleSide
navigator: NavController,
handler: (suspend (E) -> Unit)? = null
) {
- val logger: Logger = get { parametersOf("handleSideEffects") }
+ val logger: Logger = koinInject { parametersOf("handleSideEffects") }
collectSideEffect { navOrSideEffect ->
when (navOrSideEffect) {
is NavOrViewModelEffect.NavEffect -> navigator.handleNavAction(navOrSideEffect.action)
diff --git a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/core/theme/WaiterRobotTheme.kt b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/core/theme/WaiterRobotTheme.kt
index 1c75f71..6082229 100644
--- a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/core/theme/WaiterRobotTheme.kt
+++ b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/core/theme/WaiterRobotTheme.kt
@@ -15,9 +15,11 @@ fun WaiterRobotTheme(
useDarkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
- Box(modifier = Modifier
- .systemBarsPadding()
- .imePadding()) {
+ Box(
+ modifier = Modifier
+ .systemBarsPadding()
+ .imePadding()
+ ) {
MaterialTheme(
colors = if (useDarkTheme) darkColors else lightColors,
content = content
diff --git a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/core/view/View.kt b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/core/view/View.kt
index b6fb0b7..06172f3 100644
--- a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/core/view/View.kt
+++ b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/core/view/View.kt
@@ -15,7 +15,8 @@ import org.datepollsystems.waiterrobot.shared.core.viewmodel.ViewState
/**
* Handles displaying errors and loading state.
- * If [onRefresh] is provided a [RefreshableView] is used and [content] must therefore be scrollable ([RefreshableView]). Otherwise a [LoadableView].
+ * If [onRefresh] is provided a [RefreshableView] is used and [content] therefore
+ * must be scrollable ([RefreshableView]). Otherwise a [LoadableView] is used.
* @see ErrorDialog
* @see RefreshableView
* @see LoadableView
@@ -59,7 +60,6 @@ fun View(
content: @Composable () -> Unit
) = View(state, Modifier.padding(paddingValues), onRefresh, content)
-
@Composable
fun ScaffoldView(
state: ViewModelState,
diff --git a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/order/AddNoteDialog.kt b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/order/AddNoteDialog.kt
index d916982..863ec84 100644
--- a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/order/AddNoteDialog.kt
+++ b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/order/AddNoteDialog.kt
@@ -1,14 +1,36 @@
package org.datepollsystems.waiterrobot.android.ui.order
-import androidx.compose.foundation.layout.*
-import androidx.compose.material.*
-import androidx.compose.runtime.*
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.Button
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.OutlinedTextField
+import androidx.compose.material.ProvideTextStyle
+import androidx.compose.material.Surface
+import androidx.compose.material.Text
+import androidx.compose.material.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import org.datepollsystems.waiterrobot.shared.features.order.models.OrderItem
-import org.datepollsystems.waiterrobot.shared.generated.localization.*
+import org.datepollsystems.waiterrobot.shared.generated.localization.L
+import org.datepollsystems.waiterrobot.shared.generated.localization.cancel
+import org.datepollsystems.waiterrobot.shared.generated.localization.clear
+import org.datepollsystems.waiterrobot.shared.generated.localization.inputLabel
+import org.datepollsystems.waiterrobot.shared.generated.localization.inputPlaceholder
+import org.datepollsystems.waiterrobot.shared.generated.localization.save
+import org.datepollsystems.waiterrobot.shared.generated.localization.title
@Composable
fun AddNoteDialog(item: OrderItem, onDismiss: () -> Unit, onSave: (note: String?) -> Unit) {
@@ -33,7 +55,9 @@ fun AddNoteDialog(item: OrderItem, onDismiss: () -> Unit, onSave: (note: String?
label = { Text(text = L.order.addNoteDialog.inputLabel()) },
placeholder = { Text(text = L.order.addNoteDialog.inputPlaceholder()) },
value = note,
- onValueChange = { note = it.take(120) }
+ onValueChange = { note = it.take(120) },
+ minLines = 3,
+ maxLines = 3
)
Text(
modifier = Modifier.fillMaxWidth(),
diff --git a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/order/OrderListItem.kt b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/order/OrderListItem.kt
index ad0f9d9..f005d24 100644
--- a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/order/OrderListItem.kt
+++ b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/order/OrderListItem.kt
@@ -62,6 +62,7 @@ private fun OrderListItemPreview() {
amount = 10,
note = "test Note",
addAction = { _, _ -> },
- onLongClick = {})
+ onLongClick = {}
+ )
}
}
diff --git a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/order/OrderScreen.kt b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/order/OrderScreen.kt
index 1b9dae7..8e62f8a 100644
--- a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/order/OrderScreen.kt
+++ b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/order/OrderScreen.kt
@@ -18,8 +18,8 @@ import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.ramcosta.composedestinations.annotation.Destination
import kotlinx.coroutines.launch
+import org.datepollsystems.waiterrobot.android.ui.common.CenteredText
import org.datepollsystems.waiterrobot.android.ui.common.FloatingActionButton
-import org.datepollsystems.waiterrobot.android.ui.core.CenteredText
import org.datepollsystems.waiterrobot.android.ui.core.handleSideEffects
import org.datepollsystems.waiterrobot.android.ui.core.view.ScaffoldView
import org.datepollsystems.waiterrobot.shared.core.viewmodel.ViewState
@@ -49,7 +49,8 @@ fun OrderScreen(
var noteDialogItem: OrderItem? by remember { mutableStateOf(null) }
val bottomSheetState = rememberModalBottomSheetState(
- // When opening the order screen waiter most likely wants to add a new product -> show the product list immediately
+ // When opening the order screen waiter most likely wants to add a new product
+ // -> show the product list immediately
// But don't show it when the screen was opened with an initial item, this feels not nice
initialValue = if (initialItemId == null) ModalBottomSheetValue.Expanded else ModalBottomSheetValue.Hidden,
skipHalfExpanded = true
diff --git a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/order/Product.kt b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/order/Product.kt
index ffff64b..44fca31 100644
--- a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/order/Product.kt
+++ b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/order/Product.kt
@@ -40,13 +40,14 @@ fun Product(
textAlign = TextAlign.Center,
textDecoration = if (product.soldOut) TextDecoration.LineThrough else null
)
- Text(
- text = product.allergens.joinToString(", ") { it.shortName }
- .ifEmpty { "-" },
- style = MaterialTheme.typography.caption,
- textAlign = TextAlign.Center,
- color = Color.LightGray
- )
+ if (product.allergens.isNotEmpty()) {
+ Text(
+ text = product.allergens.joinToString(", ") { it.shortName },
+ style = MaterialTheme.typography.caption,
+ textAlign = TextAlign.Center,
+ color = Color.LightGray
+ )
+ }
Text(
text = product.price.toString(),
style = MaterialTheme.typography.body2,
diff --git a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/order/ProductSearch.kt b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/order/ProductSearch.kt
index 7f9ae4b..ea8cbbc 100644
--- a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/order/ProductSearch.kt
+++ b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/order/ProductSearch.kt
@@ -21,11 +21,10 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch
+import org.datepollsystems.waiterrobot.android.ui.common.CenteredText
import org.datepollsystems.waiterrobot.android.ui.common.sectionHeader
-import org.datepollsystems.waiterrobot.android.ui.core.CenteredText
import org.datepollsystems.waiterrobot.shared.features.order.models.Product
import org.datepollsystems.waiterrobot.shared.features.order.models.ProductGroup
-import org.datepollsystems.waiterrobot.shared.features.order.models.ProductGroupWithProducts
import org.datepollsystems.waiterrobot.shared.generated.localization.L
import org.datepollsystems.waiterrobot.shared.generated.localization.allGroups
import org.datepollsystems.waiterrobot.shared.generated.localization.noProductFound
@@ -35,7 +34,7 @@ import org.datepollsystems.waiterrobot.shared.generated.localization.title
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun ProductSearch(
- productGroups: List,
+ productGroups: List,
onSelect: (Product) -> Unit,
onFilter: (String) -> Unit,
close: () -> Unit
@@ -74,7 +73,8 @@ fun ProductSearch(
},
singleLine = true,
keyboardOptions = KeyboardOptions.Default.copy(
- keyboardType = KeyboardType.Text, imeAction = ImeAction.Search
+ keyboardType = KeyboardType.Text,
+ imeAction = ImeAction.Search
),
modifier = Modifier
.padding(start = 10.dp, end = 20.dp, top = 10.dp, bottom = 10.dp)
@@ -95,14 +95,17 @@ fun ProductSearch(
edgePadding = 0.dp,
divider = {} // Add divider externally as otherwise it does not span the whole width
) {
- Tab(selected = pagerState.currentPage == 0,
+ Tab(
+ selected = pagerState.currentPage == 0,
onClick = { coScope.launch { pagerState.scrollToPage(0) } },
- text = { Text(L.productSearch.allGroups()) })
- productGroups.forEachIndexed { index, productGroupWithProducts ->
+ text = { Text(L.productSearch.allGroups()) }
+ )
+ productGroups.forEachIndexed { index, productGroup ->
Tab(
selected = pagerState.currentPage == index + 1,
onClick = { coScope.launch { pagerState.scrollToPage(index + 1) } },
- text = { Text(productGroupWithProducts.group.name) })
+ text = { Text(productGroup.name) }
+ )
}
}
@@ -111,10 +114,13 @@ fun ProductSearch(
HorizontalPager(pagerState) { pageIndex ->
if (pageIndex == 0) {
ProductLazyVerticalGrid {
- productGroups.forEach { (group: ProductGroup, products: List) ->
- if (products.isNotEmpty()) {
- sectionHeader(key = "group-${group.id}", title = group.name)
- items(products, key = Product::id) { product ->
+ productGroups.forEach { productGroup ->
+ if (productGroup.products.isNotEmpty()) {
+ sectionHeader(
+ key = "group-${productGroup.id}",
+ title = productGroup.name
+ )
+ items(productGroup.products, key = Product::id) { product ->
Product(product = product, onSelect = { onSelect(product) })
}
}
diff --git a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/scanner/QrCodeScanner.kt b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/scanner/QrCodeScanner.kt
index 7fe7d9f..5385048 100644
--- a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/scanner/QrCodeScanner.kt
+++ b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/scanner/QrCodeScanner.kt
@@ -82,30 +82,36 @@ fun QrCodeScanner(onResult: (Barcode) -> Unit) {
val analysisUseCase = ImageAnalysis.Builder()
.setTargetResolution(Size(previewView.width, previewView.height))
- .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) // Do not process every frame only keep the latest
+ // Do not process every frame only keep the latest
+ .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build()
.apply {
setAnalyzer(
- Executors.newSingleThreadExecutor(), // Execute analysis on a single worker thread
+ // Execute analysis on a single worker thread
+ Executors.newSingleThreadExecutor(),
QrCodeAnalyzer {
- this.clearAnalyzer() // Stop after first detected code // TODO test scanning invalid code (no data and no url)
+ // Stop after first detected code
+ // TODO test scanning invalid code (no data and no url)
+ this.clearAnalyzer()
onResult(it.first())
}
)
}
coroutineScope.launch {
+ @Suppress("TooGenericExceptionCaught")
try {
val cameraProvider = context.getCameraProvider()
- // Make sure no use case is bound to the cameraProvider, when QrCodeScanner is "launched" twice
- // (e.g. when first scan at login fails and the QrCodeScanner is then opened a second time)
-
if (!cameraProvider.hasCamera(CameraSelector.DEFAULT_BACK_CAMERA)) {
errorMessage = L.qrScanner.noCameraFound()
return@launch
}
+ // Make sure no use case is bound to the cameraProvider,
+ // when QrCodeScanner is "launched" twice (e.g. when first scan at
+ // login fails and the QrCodeScanner is then opened a second time)
cameraProvider.unbindAll()
+
cam = cameraProvider.bindToLifecycle(
lifecycleOwner,
CameraSelector.DEFAULT_BACK_CAMERA,
diff --git a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/settings/SettingsScreen.kt b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/settings/SettingsScreen.kt
index f1acc66..eafac06 100644
--- a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/settings/SettingsScreen.kt
+++ b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/settings/SettingsScreen.kt
@@ -124,7 +124,11 @@ fun SettingsScreen(
settingsItem(
icon = { Icon(Icons.Filled.Logout, contentDescription = "Logout") },
title = { Text(L.settings.logout.action()) },
- subtitle = { Text("\"${CommonApp.settings.organisationName}\" / \"${CommonApp.settings.waiterName}\"") },
+ subtitle = {
+ Text(
+ "\"${CommonApp.settings.organisationName}\" / \"${CommonApp.settings.waiterName}\""
+ )
+ },
onClick = { showLogoutWarningDialog = true }
)
settingsItem(
diff --git a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/switchevent/SwitchEventScreen.kt b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/switchevent/SwitchEventScreen.kt
index 818caf4..c49fe0a 100644
--- a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/switchevent/SwitchEventScreen.kt
+++ b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/switchevent/SwitchEventScreen.kt
@@ -48,7 +48,8 @@ fun SwitchEventScreen(
Scaffold(scaffoldState = LocalScaffoldState.current) {
Column(modifier = Modifier.padding(it)) {
- // Surface wrapper container is needed as otherwise the PullRefreshIndicator would be on top of this part of the view
+ // Surface wrapper container is needed as otherwise the PullRefreshIndicator would be
+ // on top of this part of the view
Surface(modifier = Modifier.zIndex(1f)) {
Column {
Icon(
diff --git a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/tabledetail/TableDetailScreen.kt b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/tabledetail/TableDetailScreen.kt
index 4552138..4342f10 100644
--- a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/tabledetail/TableDetailScreen.kt
+++ b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/tabledetail/TableDetailScreen.kt
@@ -20,7 +20,7 @@ import androidx.compose.ui.draw.scale
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.ramcosta.composedestinations.annotation.Destination
-import org.datepollsystems.waiterrobot.android.ui.core.CenteredText
+import org.datepollsystems.waiterrobot.android.ui.common.CenteredText
import org.datepollsystems.waiterrobot.android.ui.core.handleSideEffects
import org.datepollsystems.waiterrobot.android.ui.core.view.ScaffoldView
import org.datepollsystems.waiterrobot.shared.features.table.models.OrderedItem
diff --git a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/tablelist/TableListScreen.kt b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/tablelist/TableListScreen.kt
index 697cb6b..670c88c 100644
--- a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/tablelist/TableListScreen.kt
+++ b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/ui/tablelist/TableListScreen.kt
@@ -15,8 +15,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
+import org.datepollsystems.waiterrobot.android.ui.common.CenteredText
import org.datepollsystems.waiterrobot.android.ui.common.sectionHeader
-import org.datepollsystems.waiterrobot.android.ui.core.CenteredText
import org.datepollsystems.waiterrobot.android.ui.core.handleSideEffects
import org.datepollsystems.waiterrobot.android.ui.core.view.ScaffoldView
import org.datepollsystems.waiterrobot.shared.core.CommonApp
diff --git a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/util/QrCodeAnalyzer.kt b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/util/QrCodeAnalyzer.kt
index 4dd7b1d..43e64eb 100644
--- a/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/util/QrCodeAnalyzer.kt
+++ b/androidApp/src/main/java/org/datepollsystems/waiterrobot/android/util/QrCodeAnalyzer.kt
@@ -10,13 +10,14 @@ import com.google.mlkit.vision.barcode.BarcodeScannerOptions
import com.google.mlkit.vision.barcode.BarcodeScanning
import com.google.mlkit.vision.barcode.common.Barcode
import com.google.mlkit.vision.common.InputImage
+import org.datepollsystems.waiterrobot.shared.core.di.injectLoggerForClass
import org.koin.core.component.KoinComponent
-import org.koin.core.component.inject
import java.util.concurrent.TimeUnit
-class QrCodeAnalyzer(private val onQrCode: (List) -> Unit) : ImageAnalysis.Analyzer,
- KoinComponent {
- private val logger: Logger by inject()
+class QrCodeAnalyzer(
+ private val onQrCode: (List) -> Unit
+) : ImageAnalysis.Analyzer, KoinComponent {
+ private val logger: Logger by injectLoggerForClass()
private var lastAnalyzedTimeStamp = 0L
diff --git a/androidApp/src/main/res/resources.properties b/androidApp/src/main/res/resources.properties
new file mode 100644
index 0000000..fccdea8
--- /dev/null
+++ b/androidApp/src/main/res/resources.properties
@@ -0,0 +1 @@
+unqualifiedResLocale=de
\ No newline at end of file
diff --git a/androidApp/src/main/res/values-de/strings.xml b/androidApp/src/main/res/values-de/strings.xml
new file mode 100644
index 0000000..b838b33
--- /dev/null
+++ b/androidApp/src/main/res/values-de/strings.xml
@@ -0,0 +1,4 @@
+
+
+ kellner.team
+
\ No newline at end of file
diff --git a/androidApp/src/main/res/values/strings.xml b/androidApp/src/main/res/values/strings.xml
new file mode 100644
index 0000000..a9361b7
--- /dev/null
+++ b/androidApp/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+
+ waiters.team
+
diff --git a/androidApp/version.properties b/androidApp/version.properties
index 98b424f..59dc3ad 100644
--- a/androidApp/version.properties
+++ b/androidApp/version.properties
@@ -1 +1 @@
-androidVersion=2.0.2
\ No newline at end of file
+androidVersion=2.0.3
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index 8c8e3ad..700a9b0 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,3 +1,6 @@
+import io.gitlab.arturbosch.detekt.Detekt
+import io.gitlab.arturbosch.detekt.report.ReportMergeTask
+
buildscript {
repositories {
gradlePluginPortal()
@@ -5,17 +8,50 @@ buildscript {
mavenCentral()
}
dependencies {
- val kotlinVersion = "1.8.10"
- classpath("com.android.tools.build:gradle:7.4.1")
+ val kotlinVersion = "1.9.0"
+ classpath("com.android.tools.build:gradle:8.1.1")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
classpath("org.jetbrains.kotlin:kotlin-serialization:${kotlinVersion}")
}
}
+plugins {
+ id("io.gitlab.arturbosch.detekt") version "1.23.1"
+}
+
+val detektReportMergeSarif by tasks.registering(ReportMergeTask::class) {
+ output = layout.buildDirectory.file("reports/detekt/merge.sarif")
+}
+
allprojects {
repositories {
gradlePluginPortal()
google()
mavenCentral()
}
+
+ apply(plugin = "io.gitlab.arturbosch.detekt")
+
+ detekt {
+ config.from(rootDir.resolve("detekt.yml"))
+ buildUponDefaultConfig = true
+ basePath = rootDir.path
+ // Autocorrection can only be done locally
+ autoCorrect = System.getenv("CI")?.lowercase() != true.toString()
+ }
+
+ dependencies {
+ detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.23.1")
+ }
+
+ tasks.withType().configureEach {
+ reports {
+ html.required = true
+ sarif.required = true
+ }
+ finalizedBy(detektReportMergeSarif)
+ }
+ detektReportMergeSarif {
+ input.from(tasks.withType().map { it.sarifReportFile })
+ }
}
diff --git a/buildSrc/src/main/java/Versions.kt b/buildSrc/src/main/java/Versions.kt
index 3e9366d..034e5da 100644
--- a/buildSrc/src/main/java/Versions.kt
+++ b/buildSrc/src/main/java/Versions.kt
@@ -1,22 +1,22 @@
object Versions {
// Shared
const val kermitLogger = "1.2.2"
- const val koinDi = "3.3.3"
+ const val koinDi = "3.4.3"
const val orbitMvi = "4.5.0"
- const val mokoMvvm = "0.15.0"
- const val ktor = "2.2.3"
+ const val mokoMvvm = "0.16.1"
+ const val ktor = "2.3.3"
const val settings = "1.0.0"
- const val realm = "1.6.1" // Also update belonging plugin in shared/build.gradle.kts
+ const val realm = "1.10.2" // Also update belonging plugin in shared/build.gradle.kts
// Android
const val androidMinSdk = 24
- const val androidTargetSdk = 31
- const val androidCompileSdk = 33
- const val androidBuildTools = "33.0.0"
+ const val androidTargetSdk = 33
+ const val androidCompileSdk = 34
+ const val androidBuildTools = "34.0.0"
- const val compose = "1.5.0-beta01"
- const val composeCompiler = "1.4.4"
- const val androidxLifecycle = "2.5.1"
- const val composeDestinations = "1.9.42-beta"
- const val camera = "1.2.1"
+ const val compose = "1.5.0"
+ const val composeCompiler = "1.5.1"
+ const val androidxLifecycle = "2.6.1"
+ const val composeDestinations = "1.9.52"
+ const val camera = "1.2.3"
}
diff --git a/detekt.yml b/detekt.yml
new file mode 100644
index 0000000..bb4bf5f
--- /dev/null
+++ b/detekt.yml
@@ -0,0 +1,38 @@
+naming:
+ FunctionNaming:
+ ignoreAnnotated:
+ - Composable
+
+complexity:
+ LongMethod:
+ ignoreAnnotated:
+ - Composable
+ LongParameterList:
+ ignoreDefaultParameters: true
+ ignoreAnnotated:
+ - Composable
+ CyclomaticComplexMethod:
+ ignoreSimpleWhenEntries: true
+
+style:
+ UnusedPrivateMember:
+ ignoreAnnotated:
+ - Preview
+ WildcardImport:
+ active: false
+ MagicNumber:
+ ignoreAnnotated:
+ - Composable
+
+# Formatting contains some overlapping rules with the standard rule set -> deactivate them so we do not get duplicate errors
+formatting:
+ NoWildcardImports: # style>WildcardImport
+ active: false
+ Filename: # naming>MatchingDeclarationName
+ active: false
+ FinalNewline: # style>NewLineAtEndOfFile
+ active: false
+ MaximumLineLength: # style>MaxLineLength
+ active: false
+ ModifierOrdering: # style>ModifierOrder
+ active: false
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index e708b1c..7f93135 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 376271f..ac72c34 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,7 @@
-#Tue Nov 22 09:55:58 CET 2022
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
distributionPath=wrapper/dists
-zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index 4f906e0..0adc8e1 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=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=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, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+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..6689b85 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -1,89 +1,92 @@
-@rem
-@rem Copyright 2015 the original author or authors.
-@rem
-@rem Licensed under the Apache License, Version 2.0 (the "License");
-@rem you may not use this file except in compliance with the License.
-@rem You may obtain a copy of the License at
-@rem
-@rem https://www.apache.org/licenses/LICENSE-2.0
-@rem
-@rem Unless required by applicable law or agreed to in writing, software
-@rem distributed under the License is distributed on an "AS IS" BASIS,
-@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-@rem See the License for the specific language governing permissions and
-@rem limitations under the License.
-@rem
-
-@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Resolve any "." and ".." in APP_HOME to make it shorter.
-for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "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.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-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.
-
-goto fail
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="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
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+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.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+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.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+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!
+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
+
+:omega
diff --git a/renovate.json5 b/renovate.json5
new file mode 100644
index 0000000..64811a2
--- /dev/null
+++ b/renovate.json5
@@ -0,0 +1,36 @@
+{
+ "baseBranches": [
+ "develop"
+ ],
+ "semanticPrefix": "renovate",
+ "labels": [
+ "dependencies"
+ ],
+ "schedule": [
+ "after 10pm every weekday",
+ "before 4am every weekday",
+ "every weekend"
+ ],
+ "pinVersions": false,
+ "semanticCommits": "enabled",
+ "separateMajorMinor": false,
+ "prHourlyLimit": 2,
+ "timezone": "Europe/Vienna",
+ "packageRules": [
+ {
+ "matchUpdateTypes": [
+ "minor",
+ "patch",
+ "pin",
+ "digest"
+ ],
+ "groupName": "minor-updates"
+ },
+ {
+ "matchUpdateTypes": [
+ "major"
+ ],
+ "groupName": "major-risky-update"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 8d40f2b..e51bf17 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -15,4 +15,34 @@ dependencyResolutionManagement {
rootProject.name = "WaiterRobot"
include(":androidApp")
-include(":shared")
\ No newline at end of file
+include(":shared")
+
+plugins {
+ id("org.danilopianini.gradle-pre-commit-git-hooks") version "1.1.9"
+}
+
+gitHooks {
+ hook("pre-push") {
+ from {
+ """
+ echo "Running detekt check..."
+ OUTPUT="/tmp/detekt-${'$'}(date +%s)"
+ ./gradlew detekt > ${'$'}OUTPUT
+ EXIT_CODE=${'$'}?
+ if [ ${'$'}EXIT_CODE -ne 0 ]; then
+ cat ${'$'}OUTPUT
+ rm ${'$'}OUTPUT
+ echo "**********************************************************************************************"
+ echo " detekt failed "
+ echo " Please fix the above issues before pushing. "
+ echo " Some of the issues might already be resolved automatically and only must be committed again. "
+ echo " Run './gradlew detekt' to to get an updated list of issues. "
+ echo "**********************************************************************************************"
+ exit ${'$'}EXIT_CODE
+ fi
+ rm ${'$'}OUTPUT
+ """.trimIndent()
+ }
+ }
+ createHooks(overwriteExisting = true)
+}
diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts
index d15fac2..2723a2e 100644
--- a/shared/build.gradle.kts
+++ b/shared/build.gradle.kts
@@ -14,8 +14,8 @@ plugins {
id("com.android.library")
id("co.touchlab.faktory.kmmbridge") version "0.3.7"
`maven-publish`
- id("dev.jamiecraane.plugins.kmmresources") version "1.0.0-alpha10" // Shared localization
- id("io.realm.kotlin") version "1.6.1"
+ id("dev.jamiecraane.plugins.kmmresources") version "1.0.0-alpha11" // Shared localization
+ id("io.realm.kotlin") version "1.10.2"
}
version = "1.0" // Shared package has only 2 digit version, patch is managed by kmmbridge.
@@ -69,7 +69,7 @@ kotlin {
// Helper
api("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
- api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1") // Also needed by android for ComposeDestination parameter serialization
+ api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1") // Also needed by android for ComposeDestination parameter serialization
}
}
val commonTest by getting {
@@ -90,7 +90,7 @@ kotlin {
implementation("io.ktor:ktor-client-cio:${Versions.ktor}")
}
}
- val androidTest by getting
+ val androidUnitTest by getting
val iosX64Main by getting
val iosArm64Main by getting
@@ -194,7 +194,7 @@ tasks {
copy {
from("$generatedLocalizationRoot/commonMain/resources/ios")
into(
- "${project.buildDir}/XCFrameworks/${buildType.toLowerCase()}/" +
+ "${project.buildDir}/XCFrameworks/${buildType.lowercase()}/" +
"$iosFrameworkName.xcframework/$arch/$iosFrameworkName.framework"
)
}
@@ -233,3 +233,11 @@ class CustomGitVersionManager(
}
}
}
+
+detekt {
+ source.from(
+ "src/androidMain/kotlin",
+ "src/commonMain/kotlin",
+ "src/iosMain/kotlin",
+ )
+}
diff --git a/shared/localization.yml b/shared/localization.yml
index 03745ad..d827f48 100644
--- a/shared/localization.yml
+++ b/shared/localization.yml
@@ -1,7 +1,7 @@
app:
name:
- en: WaiterRobot
- de: WaiterRobot
+ en: waiters.team
+ de: kellner.team
genericError:
message:
en: Something went wrong. Please try again.
@@ -233,7 +233,7 @@ settings:
darkMode:
title:
en: Dark Mode
- de: Dunklermodus
+ de: Dunkelmodus
useSystem:
en: Use system setting
de: System einstellung verwenden
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/AppInfo.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/AppInfo.kt
index 15ad183..c1431bd 100644
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/AppInfo.kt
+++ b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/AppInfo.kt
@@ -11,8 +11,7 @@ class AppInfo(
) {
val apiBaseUrl = apiBaseUrl.removeSuffix("/") + "/"
- val sessionName =
- "$os; $appVersion ($appBuild); $phoneModel".truncate(60)
+ val sessionName = "$os; $appVersion ($appBuild); $phoneModel".truncate(MAX_SESSION_LENGTH)
}
sealed class OS {
@@ -26,3 +25,5 @@ sealed class OS {
is Ios -> "iOS-${this.version}"
}
}
+
+private const val MAX_SESSION_LENGTH = 60
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/CommonApp.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/CommonApp.kt
index 0a79663..67a6874 100644
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/CommonApp.kt
+++ b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/CommonApp.kt
@@ -54,6 +54,7 @@ object CommonApp : KoinComponent {
internal fun logout() {
coroutineScope.launch {
+ @Suppress("TooGenericExceptionCaught")
try {
val tokens = settings.tokens ?: return@launch
getKoin().getOrNull()?.logout(tokens)
@@ -77,4 +78,6 @@ object CommonApp : KoinComponent {
?.forEach(BearerAuthProvider::clearToken)
}
}
+
+ const val MIN_UPDATE_INFO_HOURS = 24
}
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/api/AbstractApi.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/api/AbstractApi.kt
index fc3b406..7784c94 100644
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/api/AbstractApi.kt
+++ b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/api/AbstractApi.kt
@@ -39,10 +39,10 @@ internal abstract class AbstractApi(basePath: String, private val client: HttpCl
block?.invoke(this)
}
- protected suspend fun post(
+ protected suspend inline fun post(
endpoint: String = "",
- body: RequestBodyDto? = null,
- block: (HttpRequestBuilder.() -> Unit)? = null
+ body: B? = null,
+ noinline block: (HttpRequestBuilder.() -> Unit)? = null
): HttpResponse = client.post(endpoint.toFullUrl()) {
if (body != null) {
contentType(ContentType.Application.Json)
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/api/ApiExceptions.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/api/ApiException.kt
similarity index 100%
rename from shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/api/ApiExceptions.kt
rename to shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/api/ApiException.kt
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/api/AuthorizedClient.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/api/AuthorizedClient.kt
index 5028295..048f092 100644
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/api/AuthorizedClient.kt
+++ b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/api/AuthorizedClient.kt
@@ -33,6 +33,7 @@ internal fun createAuthorizedClient(
// Function to refresh a token (called when server response with 401 and a WWW-Authenticate header)
refreshTokens {
+ @Suppress("TooGenericExceptionCaught")
try {
authRepository.refreshTokens(scope).toBearerTokens()
} catch (e: Exception) {
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/api/BasicClient.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/api/BasicClient.kt
index ce4f554..fd92f14 100644
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/api/BasicClient.kt
+++ b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/api/BasicClient.kt
@@ -31,6 +31,7 @@ internal fun HttpClientConfig<*>.commonConfig(
}
install(HttpTimeout) {
+ @Suppress("MagicNumber")
requestTimeoutMillis = 10_000
}
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/db/AbstractDatabase.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/db/AbstractDatabase.kt
index f309cad..ad5179d 100644
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/db/AbstractDatabase.kt
+++ b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/db/AbstractDatabase.kt
@@ -2,10 +2,11 @@ package org.datepollsystems.waiterrobot.shared.core.db
import co.touchlab.kermit.Logger
import io.realm.kotlin.Realm
+import org.datepollsystems.waiterrobot.shared.core.di.injectLoggerForClass
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
internal abstract class AbstractDatabase : KoinComponent {
protected val realm: Realm by inject()
- protected val logger: Logger by inject()
+ protected val logger: Logger by injectLoggerForClass()
}
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/db/DatabaseFactory.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/db/DatabaseFactory.kt
index 8a37dc2..6832c4d 100644
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/db/DatabaseFactory.kt
+++ b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/db/DatabaseFactory.kt
@@ -3,21 +3,27 @@ package org.datepollsystems.waiterrobot.shared.core.db
import io.realm.kotlin.Realm
import io.realm.kotlin.RealmConfiguration
import io.realm.kotlin.types.RealmObject
+import org.datepollsystems.waiterrobot.shared.features.order.db.model.AllergenEntry
import org.datepollsystems.waiterrobot.shared.features.order.db.model.ProductEntry
+import org.datepollsystems.waiterrobot.shared.features.order.db.model.ProductGroupEntry
import org.datepollsystems.waiterrobot.shared.features.table.db.model.TableEntry
import kotlin.reflect.KClass
fun createRealmDB(): Realm {
val schema: Set> = setOf(
TableEntry::class,
+ ProductGroupEntry::class,
ProductEntry::class,
- ProductEntry.Allergen::class,
- ProductEntry.ProductGroup::class,
+ AllergenEntry::class,
)
+ @Suppress("MagicNumber")
val config = RealmConfiguration.Builder(schema)
- .deleteRealmIfMigrationNeeded() // Realm is only used as a persistent cache - so do not care about migrations
- .schemaVersion(2) // TODO increase with each version of the common code (automate - compute from version in buildScript or app version?)
+ // Realm is only used as a persistent cache - so do not care about migrations
+ .deleteRealmIfMigrationNeeded()
+ // TODO increase with each version of the common code
+ // (automate - compute from version in buildScript or app version?)
+ .schemaVersion(3)
.build()
return Realm.open(config)
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/di/KtorLogger.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/di/CustomKtorLogger.kt
similarity index 100%
rename from shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/di/KtorLogger.kt
rename to shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/di/CustomKtorLogger.kt
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/di/Koin.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/di/Koin.kt
index ffc2344..cecb7ea 100644
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/di/Koin.kt
+++ b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/di/Koin.kt
@@ -30,5 +30,5 @@ fun initKoin(appDeclaration: KoinAppDeclaration = { }) = startKoin {
}
fun KoinComponent.injectLogger(tag: String): Lazy = inject { parametersOf(tag) }
-internal fun KoinComponent.injectLoggerForClass(): Lazy =
+fun KoinComponent.injectLoggerForClass(): Lazy =
injectLogger(this::class.simpleName ?: "AnonymousClass")
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/di/KoinCoreModule.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/di/KoinCoreModule.kt
index 22b6604..c62e590 100644
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/di/KoinCoreModule.kt
+++ b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/di/KoinCoreModule.kt
@@ -53,4 +53,7 @@ internal val coreModule = module {
single { createRealmDB() }
}
-private fun createJson() = Json { ignoreUnknownKeys = true }
+private fun createJson() = Json {
+ ignoreUnknownKeys = true
+ coerceInputValues = true // Use default value for null (when not-nullable) and unknown values
+}
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/settings/SerializedStringSettings.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/settings/SerializedStringSettings.kt
index 117025d..8acb4e2 100644
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/settings/SerializedStringSettings.kt
+++ b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/settings/SerializedStringSettings.kt
@@ -31,8 +31,9 @@ internal fun Settings.jsonSerialized(
serializer: KSerializer
): ReadWriteProperty = JsonSerializedDelegate(this, key, defaultValue, serializer)
-internal inline fun Settings.nullableJsonSerialized(key: String? = null)
- : ReadWriteProperty = nullableJsonSerialized(key, serializer())
+internal inline fun Settings.nullableJsonSerialized(
+ key: String? = null
+): ReadWriteProperty = nullableJsonSerialized(key, serializer())
internal fun Settings.nullableJsonSerialized(
key: String? = null,
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/viewmodel/AbstractViewModel.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/viewmodel/AbstractViewModel.kt
index b7c7587..1def6de 100644
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/viewmodel/AbstractViewModel.kt
+++ b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/core/viewmodel/AbstractViewModel.kt
@@ -46,7 +46,10 @@ abstract class AbstractViewModel(initia
}
else -> {
- logger.w(exception) { "Unhandled exception in intent. Exceptions should be handled directly in the intent!" }
+ logger.w(exception) {
+ "Unhandled exception in intent. " +
+ "Exceptions should be handled directly in the intent!"
+ }
intent {
reduceError(L.app.genericError.title(), L.app.genericError.message())
}
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/auth/api/models/LogoutDto.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/auth/api/models/LogoutRequestDto.kt
similarity index 100%
rename from shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/auth/api/models/LogoutDto.kt
rename to shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/auth/api/models/LogoutRequestDto.kt
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/auth/repository/AuthRepository.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/auth/repository/AuthRepository.kt
index b881170..2be3cb7 100644
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/auth/repository/AuthRepository.kt
+++ b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/auth/repository/AuthRepository.kt
@@ -55,6 +55,7 @@ internal class AuthRepository(private val authApi: AuthApi) : AbstractRepository
}
private suspend fun autoSelectEvent() {
+ @Suppress("TooGenericExceptionCaught")
try {
// Auto select event when there is only one available
eventRepository.getEvents().singleOrNull()?.let {
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/auth/viewmodel/register/RegisterViewModel.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/auth/viewmodel/register/RegisterViewModel.kt
index 15f0a59..745919a 100644
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/auth/viewmodel/register/RegisterViewModel.kt
+++ b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/auth/viewmodel/register/RegisterViewModel.kt
@@ -21,7 +21,7 @@ class RegisterViewModel internal constructor(
authRepository.createWithToken(createToken, name)
navigator.popUpToRoot()
reduce { state.withViewState(ViewState.Idle) }
- } catch (e: ApiException.CredentialsIncorrect) {
+ } catch (_: ApiException.CredentialsIncorrect) {
reduceError(L.login.invalidCode.title(), L.login.invalidCode.desc())
}
}
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/auth/viewmodel/scanner/LoginScannerViewModel.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/auth/viewmodel/scanner/LoginScannerViewModel.kt
index 6da39d3..535a4fc 100644
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/auth/viewmodel/scanner/LoginScannerViewModel.kt
+++ b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/auth/viewmodel/scanner/LoginScannerViewModel.kt
@@ -25,7 +25,7 @@ class LoginScannerViewModel internal constructor(
navigator.push(Screen.RegisterScreen(deepLink.token))
}
}
- } catch (e: Exception) {
+ } catch (_: Exception) {
logger.d { "Error with scanned login code: $code" }
reduceError("Invalid code", "Scanned invalid code")
}
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/billing/viewmodel/BillingState.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/billing/viewmodel/BillingState.kt
index c8cd080..222337f 100644
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/billing/viewmodel/BillingState.kt
+++ b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/billing/viewmodel/BillingState.kt
@@ -10,6 +10,7 @@ data class BillingState(
val moneyGivenText: String = "",
val changeText: String = "0.00 €",
val showConfirmationDialog: Boolean = false,
+ @Suppress("ConstructorParameterNaming")
internal val _billItems: Map = emptyMap()
) : ViewModelState() {
val billItems: List by lazy { _billItems.values.toList() }
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/billing/viewmodel/BillingViewModel.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/billing/viewmodel/BillingViewModel.kt
index 40d016c..2af6d8e 100644
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/billing/viewmodel/BillingViewModel.kt
+++ b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/billing/viewmodel/BillingViewModel.kt
@@ -43,7 +43,7 @@ class BillingViewModel internal constructor(
try {
val given = givenText.euro
reduce { state.copy(changeText = (given - state.priceSum).toString()) }
- } catch (e: Exception) {
+ } catch (_: Exception) {
reduce { state.copy(changeText = "NaN") }
}
}
@@ -86,7 +86,8 @@ class BillingViewModel internal constructor(
}
fun abortBill() = intent {
- // Hide the confirmation dialog before navigation away, as otherwise on iOS it would be still shown on the new screen
+ // Hide the confirmation dialog before navigation away,
+ // as otherwise on iOS it would be still shown on the new screen
reduce { state.copy(showConfirmationDialog = false) }
navigator.pop()
}
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/api/OrderApi.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/api/OrderApi.kt
index 3bc34f7..f8f40df 100644
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/api/OrderApi.kt
+++ b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/api/OrderApi.kt
@@ -9,4 +9,3 @@ internal class OrderApi(client: AuthorizedClient) : AuthorizedApi("waiter/order"
post("/", order)
}
}
-
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/api/ProductApi.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/api/ProductApi.kt
index 122f1ab..4f1928c 100644
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/api/ProductApi.kt
+++ b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/api/ProductApi.kt
@@ -3,10 +3,10 @@ package org.datepollsystems.waiterrobot.shared.features.order.api
import io.ktor.client.call.body
import org.datepollsystems.waiterrobot.shared.core.api.AuthorizedApi
import org.datepollsystems.waiterrobot.shared.core.api.AuthorizedClient
-import org.datepollsystems.waiterrobot.shared.features.order.api.models.ProductGroupResponseDto
+import org.datepollsystems.waiterrobot.shared.features.order.api.models.ProductGroupDto
internal class ProductApi(client: AuthorizedClient) : AuthorizedApi("waiter/product", client) {
suspend fun getProducts(eventId: Long) =
- get("/", "eventId" to eventId.toString()).body>()
+ get("/", "eventId" to eventId.toString()).body>()
}
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/api/models/OrderDto.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/api/models/OrderRequestDto.kt
similarity index 100%
rename from shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/api/models/OrderDto.kt
rename to shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/api/models/OrderRequestDto.kt
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/api/models/ProductDto.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/api/models/ProductDto.kt
index 3bb2353..8a36ea8 100644
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/api/models/ProductDto.kt
+++ b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/api/models/ProductDto.kt
@@ -4,10 +4,11 @@ import kotlinx.serialization.Serializable
import org.datepollsystems.waiterrobot.shared.utils.Cents
@Serializable
-internal class ProductGroupResponseDto(
+internal class ProductGroupDto(
val id: Long,
val name: String,
- val products: List
+ val products: List,
+ val position: Int = Int.MAX_VALUE,
)
@Serializable
@@ -16,7 +17,8 @@ internal class ProductDto(
val name: String,
val soldOut: Boolean,
val price: Cents,
- val allergens: List
+ val allergens: List,
+ val position: Int = Int.MAX_VALUE,
)
@Serializable
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/db/ProductDatabase.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/db/ProductDatabase.kt
index 5dd07c7..a3d641e 100644
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/db/ProductDatabase.kt
+++ b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/db/ProductDatabase.kt
@@ -5,33 +5,34 @@ import io.realm.kotlin.ext.query
import io.realm.kotlin.query.RealmResults
import org.datepollsystems.waiterrobot.shared.core.db.AbstractDatabase
import org.datepollsystems.waiterrobot.shared.features.order.db.model.ProductEntry
-import org.datepollsystems.waiterrobot.shared.features.table.db.model.TableEntry
+import org.datepollsystems.waiterrobot.shared.features.order.db.model.ProductGroupEntry
import org.datepollsystems.waiterrobot.shared.utils.extensions.Now
import kotlin.time.Duration
internal class ProductDatabase : AbstractDatabase() {
- fun getForEvent(eventId: Long): RealmResults =
- realm.query("eventId == $0", eventId).find()
+ fun getForEvent(eventId: Long): RealmResults =
+ realm.query("eventId == $0", eventId).find()
- fun getById(id: Long): ProductEntry? = realm.query("id == $0", id).first().find()
+ fun getProductById(id: Long): ProductEntry? =
+ realm.query("id == $0", id).first().find()
- suspend fun insert(products: List) {
+ suspend fun insert(productGroups: List) {
realm.write {
- products.forEach { copyToRealm(it, UpdatePolicy.ALL) }
+ productGroups.forEach { copyToRealm(it, UpdatePolicy.ALL) }
}
}
suspend fun deleteForEvent(eventId: Long) {
realm.write {
- delete(query("eventId == $0", eventId).find())
+ delete(query("eventId == $0", eventId).find())
}
}
suspend fun deleteOlderThan(maxAge: Duration) {
val timestamp = Now().minus(maxAge).toEpochMilliseconds()
realm.write {
- delete(query("updatedAt <= $0", timestamp).find())
+ delete(query("updatedAt <= $0", timestamp).find())
}
}
}
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/db/model/ProductEntry.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/db/model/ProductEntry.kt
index edff0ce..d03088d 100644
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/db/model/ProductEntry.kt
+++ b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/db/model/ProductEntry.kt
@@ -1,5 +1,7 @@
package org.datepollsystems.waiterrobot.shared.features.order.db.model
+import io.realm.kotlin.ext.realmListOf
+import io.realm.kotlin.ext.toRealmList
import io.realm.kotlin.types.RealmList
import io.realm.kotlin.types.RealmObject
import io.realm.kotlin.types.annotations.PrimaryKey
@@ -7,59 +9,69 @@ import kotlinx.datetime.Instant
import org.datepollsystems.waiterrobot.shared.utils.Cents
import org.datepollsystems.waiterrobot.shared.utils.extensions.Now
-internal class ProductEntry() : RealmObject {
- @PrimaryKey
+internal class ProductGroupEntry constructor() : RealmObject {
var id: Long? = null
var name: String? = null
- var price: Cents? = null
- var soldOut: Boolean? = null
- var updatedAt: Long = 0L
var eventId: Long? = null
- var allergens: RealmList? = null
- var productGroup: ProductGroup? = null
+ var position: Int = Int.MAX_VALUE
+ var products: RealmList = realmListOf()
+ var updatedAt: Long = 0L
val updated: Instant
get() = Instant.fromEpochMilliseconds(updatedAt)
constructor(
id: Long,
- eventId: Long,
name: String,
- price: Cents,
- soldOut: Boolean,
- productGroup: ProductGroup,
- allergens: RealmList,
+ eventId: Long,
+ position: Int,
+ products: List,
updatedAt: Instant = Now()
) : this() {
this.id = id
- this.eventId = eventId
this.name = name
- this.price = price
- this.soldOut = soldOut
- this.allergens = allergens
- this.productGroup = productGroup
+ this.eventId = eventId
+ this.position = position
+ this.products = products.toRealmList()
this.updatedAt = updatedAt.toEpochMilliseconds()
}
+}
- internal class Allergen constructor() : RealmObject {
- var id: Long? = null
- var name: String? = null
- var shortName: String? = null
+internal class ProductEntry() : RealmObject {
+ @PrimaryKey
+ var id: Long? = null
+ var name: String? = null
+ var price: Cents? = null
+ var soldOut: Boolean? = null
+ var allergens: RealmList = realmListOf()
+ var position: Int = Int.MAX_VALUE
- constructor(id: Long, name: String, shortName: String) : this() {
- this.id = id
- this.name = name
- this.shortName = shortName
- }
+ @Suppress("LongParameterList")
+ constructor(
+ id: Long,
+ name: String,
+ price: Cents,
+ soldOut: Boolean,
+ allergens: List,
+ position: Int,
+ ) : this() {
+ this.id = id
+ this.name = name
+ this.price = price
+ this.soldOut = soldOut
+ this.allergens = allergens.toRealmList()
+ this.position = position
}
+}
- internal class ProductGroup constructor() : RealmObject {
- var id: Long? = null
- var name: String? = null
+internal class AllergenEntry constructor() : RealmObject {
+ var id: Long? = null
+ var name: String? = null
+ var shortName: String? = null
- constructor(id: Long, name: String) : this() {
- this.id = id
- this.name = name
- }
+ constructor(id: Long, name: String, shortName: String) : this() {
+ this.id = id
+ this.name = name
+ this.shortName = shortName
}
}
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/models/Product.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/models/Product.kt
index 013984c..4dd50aa 100644
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/models/Product.kt
+++ b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/models/Product.kt
@@ -8,4 +8,5 @@ data class Product(
val price: Money,
val soldOut: Boolean,
val allergens: List,
+ val position: Int,
)
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/models/ProductGroup.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/models/ProductGroup.kt
index 08f296f..31630dd 100644
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/models/ProductGroup.kt
+++ b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/models/ProductGroup.kt
@@ -2,5 +2,7 @@ package org.datepollsystems.waiterrobot.shared.features.order.models
data class ProductGroup(
val id: Long,
- val name: String
+ val name: String,
+ val position: Int,
+ val products: List
)
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/models/ProductGroupWithProducts.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/models/ProductGroupWithProducts.kt
deleted file mode 100644
index 3a6612c..0000000
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/models/ProductGroupWithProducts.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package org.datepollsystems.waiterrobot.shared.features.order.models
-
-data class ProductGroupWithProducts(
- val group: ProductGroup,
- val products: List
-)
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/repository/ProductRepository.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/repository/ProductRepository.kt
index 023f9bd..83a10ca 100644
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/repository/ProductRepository.kt
+++ b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/repository/ProductRepository.kt
@@ -1,6 +1,5 @@
package org.datepollsystems.waiterrobot.shared.features.order.repository
-import io.realm.kotlin.ext.toRealmList
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.datetime.Instant
@@ -8,12 +7,14 @@ import org.datepollsystems.waiterrobot.shared.core.CommonApp
import org.datepollsystems.waiterrobot.shared.core.repository.AbstractRepository
import org.datepollsystems.waiterrobot.shared.features.order.api.ProductApi
import org.datepollsystems.waiterrobot.shared.features.order.api.models.ProductDto
+import org.datepollsystems.waiterrobot.shared.features.order.api.models.ProductGroupDto
import org.datepollsystems.waiterrobot.shared.features.order.db.ProductDatabase
+import org.datepollsystems.waiterrobot.shared.features.order.db.model.AllergenEntry
import org.datepollsystems.waiterrobot.shared.features.order.db.model.ProductEntry
+import org.datepollsystems.waiterrobot.shared.features.order.db.model.ProductGroupEntry
import org.datepollsystems.waiterrobot.shared.features.order.models.Allergen
import org.datepollsystems.waiterrobot.shared.features.order.models.Product
import org.datepollsystems.waiterrobot.shared.features.order.models.ProductGroup
-import org.datepollsystems.waiterrobot.shared.features.order.models.ProductGroupWithProducts
import org.datepollsystems.waiterrobot.shared.utils.cent
import org.datepollsystems.waiterrobot.shared.utils.extensions.Now
import org.datepollsystems.waiterrobot.shared.utils.extensions.olderThan
@@ -34,29 +35,16 @@ internal class ProductRepository : AbstractRepository(), KoinComponent {
}
suspend fun getProductById(id: Long): Product? {
- return productDb.getById(id)?.toModel()
+ return productDb.getProductById(id)?.toModel()
?: getProductGroups(true) // TODO limit force update
- .flatMap(ProductGroupWithProducts::products)
+ .flatMap(ProductGroup::products)
.find { it.id == id }
}
- suspend fun getProductGroups(forceUpdate: Boolean = false): List {
+ suspend fun getProductGroups(forceUpdate: Boolean = false): List {
val eventId = CommonApp.settings.selectedEventId
- fun Map>.mapProductGroupWithProducts(
- mapper: (T) -> Product
- ): List {
- return this.map { (group, products) ->
- ProductGroupWithProducts(
- group = group,
- products = products.map(mapper)
- .sortedBy { it.name.lowercase() }
- .sortedBy(Product::soldOut)
- )
- }.sortedBy { it.group.name.lowercase() }
- }
-
- fun loadFromDb(): List? {
+ fun loadFromDb(): List? {
logger.i { "Fetching products from DB ..." }
val dbProducts = productDb.getForEvent(eventId)
logger.d { "Found ${dbProducts.count()} products in DB" }
@@ -64,40 +52,38 @@ internal class ProductRepository : AbstractRepository(), KoinComponent {
return if (dbProducts.isEmpty() || dbProducts.any { it.updated.olderThan(maxAge) }) {
null
} else {
- dbProducts.groupBy { it.productGroup!!.toModel() }
- .mapProductGroupWithProducts(ProductEntry::toModel)
+ dbProducts.map(ProductGroupEntry::toModel)
}
}
- suspend fun loadFromApiAndStore(): List {
+ suspend fun loadFromApiAndStore(): List {
logger.i { "Loading products from api ..." }
val timestamp = Now()
val apiProducts = productApi.getProducts(eventId)
logger.d { "Got ${apiProducts.sumOf { it.products.count() }} products from api" }
- val modelGroups = apiProducts.associate { it.id to ProductGroup(it.id, it.name) }
- val entryGroups =
- apiProducts.associate { it.id to ProductEntry.ProductGroup(it.id, it.name) }
+ val modelGroups = apiProducts.map(ProductGroupDto::toModel)
+ val entryGroups = apiProducts.map { it.toEntry(eventId, timestamp) }
logger.i { "Remove old products from DB ..." }
productDb.deleteForEvent(eventId)
logger.i { "Saving products to DB ..." }
- productDb.insert(apiProducts.flatMap { group ->
- group.products.map { it.toEntry(eventId, entryGroups[group.id]!!, timestamp) }
- })
+ productDb.insert(entryGroups)
- return apiProducts.associate { group ->
- modelGroups[group.id]!! to group.products
- }.mapProductGroupWithProducts(ProductDto::toModel)
+ return modelGroups
}
- return if (forceUpdate) {
- loadFromApiAndStore()
- } else {
- loadFromDb() ?: loadFromApiAndStore()
+ val result = when (forceUpdate) {
+ true -> loadFromApiAndStore()
+ false -> loadFromDb() ?: loadFromApiAndStore()
}
+
+ return result
+ .filter { it.products.isNotEmpty() } // Do not show groups that do not have products at all
+ .sortedBy { it.name.lowercase() } // Sort groups with same position by name
+ .sortedBy(ProductGroup::position)
}
companion object {
@@ -105,6 +91,16 @@ internal class ProductRepository : AbstractRepository(), KoinComponent {
}
}
+private fun ProductGroupDto.toModel() = ProductGroup(
+ id = this.id,
+ name = this.name,
+ position = this.position,
+ products = this.products
+ .map(ProductDto::toModel)
+ .sortedBy { it.name.lowercase() } // Sort products with same position by name
+ .sortedBy(Product::position)
+)
+
private fun ProductDto.toModel() = Product(
id = this.id,
name = this.name,
@@ -113,23 +109,27 @@ private fun ProductDto.toModel() = Product(
allergens = this.allergens.map { allergen ->
Allergen(allergen.id, allergen.name, allergen.shortName)
},
+ position = this.position,
)
-private fun ProductDto.toEntry(
- eventId: Long,
- group: ProductEntry.ProductGroup,
- timestamp: Instant
-) = ProductEntry(
+private fun ProductGroupDto.toEntry(eventId: Long, timestamp: Instant) = ProductGroupEntry(
id = this.id,
+ name = this.name,
eventId = eventId,
+ position = this.position,
+ products = this.products.map { it.toEntry() },
+ updatedAt = timestamp
+)
+
+private fun ProductDto.toEntry() = ProductEntry(
+ id = this.id,
name = this.name,
price = this.price,
soldOut = this.soldOut,
- productGroup = group,
allergens = this.allergens.map {
- ProductEntry.Allergen(id = it.id, name = it.name, shortName = it.shortName)
- }.toRealmList(),
- updatedAt = timestamp
+ AllergenEntry(id = it.id, name = it.name, shortName = it.shortName)
+ },
+ position = this.position,
)
private fun ProductEntry.toModel() = Product(
@@ -137,10 +137,13 @@ private fun ProductEntry.toModel() = Product(
name = this.name!!,
price = this.price!!.cent,
soldOut = this.soldOut!!,
- allergens = this.allergens!!.map { Allergen(it.id!!, it.name!!, it.shortName!!) },
+ allergens = this.allergens.map { Allergen(it.id!!, it.name!!, it.shortName!!) },
+ position = this.position
)
-private fun ProductEntry.ProductGroup.toModel() = ProductGroup(
+private fun ProductGroupEntry.toModel() = ProductGroup(
id = this.id!!,
- name = this.name!!
+ name = this.name!!,
+ position = this.position,
+ products = this.products.map(ProductEntry::toModel)
)
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/viewmodel/OrderState.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/viewmodel/OrderState.kt
index 0efe2b9..e73c555 100644
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/viewmodel/OrderState.kt
+++ b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/viewmodel/OrderState.kt
@@ -3,12 +3,13 @@ package org.datepollsystems.waiterrobot.shared.features.order.viewmodel
import org.datepollsystems.waiterrobot.shared.core.viewmodel.ViewModelState
import org.datepollsystems.waiterrobot.shared.core.viewmodel.ViewState
import org.datepollsystems.waiterrobot.shared.features.order.models.OrderItem
-import org.datepollsystems.waiterrobot.shared.features.order.models.ProductGroupWithProducts
+import org.datepollsystems.waiterrobot.shared.features.order.models.ProductGroup
data class OrderState(
override val viewState: ViewState = ViewState.Idle,
val showConfirmationDialog: Boolean = false,
- val productGroups: List = emptyList(),
+ val productGroups: List = emptyList(),
+ @Suppress("ConstructorParameterNaming")
internal val _currentOrder: Map = emptyMap() // Product ID to Order
) : ViewModelState() {
// Expose only as a list of OrderItems
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/viewmodel/OrderViewModel.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/viewmodel/OrderViewModel.kt
index 670982f..2a106db 100644
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/viewmodel/OrderViewModel.kt
+++ b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/order/viewmodel/OrderViewModel.kt
@@ -6,8 +6,6 @@ import org.datepollsystems.waiterrobot.shared.core.viewmodel.AbstractViewModel
import org.datepollsystems.waiterrobot.shared.core.viewmodel.ViewState
import org.datepollsystems.waiterrobot.shared.features.order.models.OrderItem
import org.datepollsystems.waiterrobot.shared.features.order.models.Product
-import org.datepollsystems.waiterrobot.shared.features.order.models.ProductGroup
-import org.datepollsystems.waiterrobot.shared.features.order.models.ProductGroupWithProducts
import org.datepollsystems.waiterrobot.shared.features.order.repository.OrderRepository
import org.datepollsystems.waiterrobot.shared.features.order.repository.ProductRepository
import org.datepollsystems.waiterrobot.shared.features.table.models.Table
@@ -17,6 +15,7 @@ import org.datepollsystems.waiterrobot.shared.utils.extensions.emptyToNull
import org.orbitmvi.orbit.syntax.simple.intent
import org.orbitmvi.orbit.syntax.simple.reduce
+@Suppress("TooManyFunctions")
class OrderViewModel internal constructor(
private val productRepository: ProductRepository,
private val orderRepository: OrderRepository,
@@ -40,7 +39,7 @@ class OrderViewModel internal constructor(
addItem(id, amount) { productRepository.getProductById(id) }
fun addItem(product: Product, amount: Int) =
- addItem(product.id, amount) { product }
+ addItem(product.id, amount) { productRepository.getProductById(product.id) }
fun addItemNote(item: OrderItem, note: String?) = intent {
@Suppress("NAME_SHADOWING")
@@ -76,6 +75,7 @@ class OrderViewModel internal constructor(
}
}
+ @Suppress("MemberVisibilityCanBePrivate") // used on iOS
fun removeAllOfProduct(productId: Long) = intent {
reduce { state.copy(_currentOrder = state._currentOrder.minus(productId)) }
}
@@ -89,7 +89,8 @@ class OrderViewModel internal constructor(
}
fun abortOrder() = intent {
- // Hide the confirmation dialog before navigation away, as otherwise on iOS it would be still shown on the new screen
+ // Hide the confirmation dialog before navigation away,
+ // as otherwise on iOS it would be still shown on the new screen
reduce { state.copy(showConfirmationDialog = false) }
navigator.pop()
}
@@ -140,13 +141,11 @@ class OrderViewModel internal constructor(
} else {
reduce {
state.copy(
- productGroups = allProducts.map { (group: ProductGroup, products: List) ->
- ProductGroupWithProducts(
- group = group,
- products = products.filter {
- it.name.contains(filter, ignoreCase = true)
- }
- )
+ productGroups = allProducts.map { group ->
+ val filteredProducts = group.products
+ .filter { it.name.contains(filter, ignoreCase = true) }
+ // Also add groups with no products so that the tabs do not change
+ group.copy(products = filteredProducts)
},
)
}
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/switchevent/api/models/EventDto.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/switchevent/api/models/EventResponseDto.kt
similarity index 100%
rename from shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/switchevent/api/models/EventDto.kt
rename to shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/switchevent/api/models/EventResponseDto.kt
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/table/api/models/TableDto.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/table/api/models/TableResponseDto.kt
similarity index 100%
rename from shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/table/api/models/TableDto.kt
rename to shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/features/table/api/models/TableResponseDto.kt
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/root/RootViewModel.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/root/RootViewModel.kt
index ad25fa8..7965a62 100644
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/root/RootViewModel.kt
+++ b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/root/RootViewModel.kt
@@ -62,7 +62,7 @@ class RootViewModel internal constructor(
}
}
reduce { state.withViewState(ViewState.Idle) }
- } catch (e: ApiException.CredentialsIncorrect) {
+ } catch (_: ApiException.CredentialsIncorrect) {
reduceError(L.root.invalidLoginLink.title(), L.root.invalidLoginLink.desc())
}
}
diff --git a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/utils/Money.kt b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/utils/Money.kt
index ea85691..8e4dd6a 100644
--- a/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/utils/Money.kt
+++ b/shared/src/commonMain/kotlin/org/datepollsystems/waiterrobot/shared/utils/Money.kt
@@ -1,3 +1,5 @@
+@file:Suppress("MagicNumber")
+
package org.datepollsystems.waiterrobot.shared.utils
import kotlin.math.abs
diff --git a/shared/src/iosMain/kotlin/org/datepollsystems/waiterrobot/shared/core/VersionChecker.kt b/shared/src/iosMain/kotlin/org/datepollsystems/waiterrobot/shared/core/VersionChecker.kt
index 76601c8..4a82b1e 100644
--- a/shared/src/iosMain/kotlin/org/datepollsystems/waiterrobot/shared/core/VersionChecker.kt
+++ b/shared/src/iosMain/kotlin/org/datepollsystems/waiterrobot/shared/core/VersionChecker.kt
@@ -14,6 +14,7 @@ import kotlinx.datetime.until
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
+import org.datepollsystems.waiterrobot.shared.core.CommonApp.MIN_UPDATE_INFO_HOURS
import org.datepollsystems.waiterrobot.shared.utils.extensions.defaultOnNull
import org.koin.core.component.KoinComponent
import org.koin.core.component.get
@@ -56,13 +57,14 @@ object VersionChecker : KoinComponent {
CommonApp.settings.lastUpdateAvailableNote
.defaultOnNull(Instant.DISTANT_PAST)
.until(Clock.System.now(), DateTimeUnit.HOUR)
+
logger.i(
"New app version is available, hoursSinceLastUpdateAvailableNote: " +
hoursSinceLastUpdateAvailableNote
)
// Show max once a day
- if (hoursSinceLastUpdateAvailableNote > 24) {
+ if (hoursSinceLastUpdateAvailableNote > MIN_UPDATE_INFO_HOURS) {
onNewVersionAvailable()
CommonApp.settings.lastUpdateAvailableNote = Clock.System.now()
}
diff --git a/shared/src/iosMain/kotlin/org/datepollsystems/waiterrobot/shared/core/di/Koin.kt b/shared/src/iosMain/kotlin/org/datepollsystems/waiterrobot/shared/core/di/Koin.kt
index ea49d97..8ef47f0 100644
--- a/shared/src/iosMain/kotlin/org/datepollsystems/waiterrobot/shared/core/di/Koin.kt
+++ b/shared/src/iosMain/kotlin/org/datepollsystems/waiterrobot/shared/core/di/Koin.kt
@@ -19,7 +19,7 @@ import org.koin.core.parameter.parametersOf
@Suppress("unused") // Only used by iOS
fun initKoinIos() = initKoin()
-@Suppress("unused") // Only used by iOS
+@Suppress("unused", "TooManyFunctions") // Only used by iOS
object IosKoinComponent : KoinComponent {
fun logger(tag: String): Logger = get { parametersOf(tag) }
fun rootVM() = get()
diff --git a/shared/src/iosMain/kotlin/org/datepollsystems/waiterrobot/shared/core/di/KoinSharedViewModel.kt b/shared/src/iosMain/kotlin/org/datepollsystems/waiterrobot/shared/core/di/KoinSharedViewModel.kt
index 051f482..362834b 100644
--- a/shared/src/iosMain/kotlin/org/datepollsystems/waiterrobot/shared/core/di/KoinSharedViewModel.kt
+++ b/shared/src/iosMain/kotlin/org/datepollsystems/waiterrobot/shared/core/di/KoinSharedViewModel.kt
@@ -6,7 +6,6 @@ import org.koin.core.definition.KoinDefinition
import org.koin.core.module.Module
import org.koin.core.qualifier.Qualifier
-
internal actual inline fun Module.sharedViewModel(
qualifier: Qualifier?,
noinline definition: Definition
diff --git a/shared/src/iosMain/kotlin/org/datepollsystems/waiterrobot/shared/utils/FlowUtils.kt b/shared/src/iosMain/kotlin/org/datepollsystems/waiterrobot/shared/utils/FlowUtils.kt
index 4a253ba..12e63aa 100644
--- a/shared/src/iosMain/kotlin/org/datepollsystems/waiterrobot/shared/utils/FlowUtils.kt
+++ b/shared/src/iosMain/kotlin/org/datepollsystems/waiterrobot/shared/utils/FlowUtils.kt
@@ -16,7 +16,8 @@ import kotlinx.coroutines.flow.*
* Collects the flow and calls the [onEach] function for each received item.
* KotlinFlow -> Callback base API
*
- * This is needed as from Swift no coroutines can be launched and to collect a flow a coroutine (or suspendable function) is needed.
+ * This is needed as from Swift no coroutines can be launched and to collect a flow a coroutine
+ * (or suspendable function) is needed.
*/
@Suppress("unused") // Used by iOS
fun Flow.subscribe(