Compare commits
25 Commits
e5fdd7fed2
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 6da10a0612 | |||
|
2e163f8354
|
|||
|
d10622c382
|
|||
|
e4ea44f766
|
|||
| 7650e6487d | |||
|
7fb8b195f0
|
|||
|
77f48c603d
|
|||
|
2d4214562a
|
|||
|
f7bd6643cb
|
|||
| 1be2cb425c | |||
|
571db144c4
|
|||
| 49c9a6dce1 | |||
|
c4e6d6b69f
|
|||
|
9ecfcc2a74
|
|||
|
f5db153ac2
|
|||
|
3c83f740d4
|
|||
|
1b4323c2bb
|
|||
|
|
dc4ba51896 | ||
|
|
2dd0ec3403 | ||
|
|
fd39b7ebe6 | ||
|
|
79e0ef62e5 | ||
|
|
459294cec7 | ||
|
|
e160580f69 | ||
|
|
c5bc5fad22 | ||
|
|
a471251ff3 |
91
.github/workflows/check.yml
vendored
91
.github/workflows/check.yml
vendored
@@ -1,91 +0,0 @@
|
||||
name: Check
|
||||
|
||||
on: [ pull_request ]
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: ashutoshgngwr/validate-fastlane-supply-metadata@v2
|
||||
with:
|
||||
fastlaneDir: ./fastlane/metadata/android
|
||||
usePlayStoreLocales: true
|
||||
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
|
||||
- name: Setup Android SDK
|
||||
uses: android-actions/setup-android@v3
|
||||
|
||||
- name: Checks
|
||||
run: ./gradlew check :app:koverXmlReportRelease :app:koverVerifyRelease
|
||||
|
||||
- name: SonarCloud
|
||||
env:
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
run: ./gradlew sonar
|
||||
|
||||
- name: Publish test reports
|
||||
uses: mikepenz/action-junit-report@v4
|
||||
if: always() # always run even if the previous step fails
|
||||
with:
|
||||
report_paths: './**/build/test-results/**/TEST-*.xml'
|
||||
|
||||
uiTests:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
api-level: [ 30 ]
|
||||
steps:
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@v1.3.1
|
||||
with:
|
||||
android: false
|
||||
large-packages: true
|
||||
tool-cache: true
|
||||
dotnet: true
|
||||
haskell: true
|
||||
docker-images: true
|
||||
swap-storage: true
|
||||
|
||||
- name: Enable KVM group perms
|
||||
run: |
|
||||
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger --name-match=kvm
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
name: Checkout the code
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-java@v4
|
||||
name: Setup JDK 17
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: 17
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
with:
|
||||
validate-wrappers: true
|
||||
gradle-home-cache-cleanup: true
|
||||
|
||||
- name: Run tests
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: ${{ matrix.api-level }}
|
||||
arch: x86_64
|
||||
disable-animations: true
|
||||
disk-size: 6000M
|
||||
heap-size: 600M
|
||||
script: ./gradlew :app:connectedCheck
|
||||
61
.github/workflows/sign.yml
vendored
61
.github/workflows/sign.yml
vendored
@@ -1,61 +0,0 @@
|
||||
name: Sign
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
sign:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v3
|
||||
|
||||
- name: Setup Android SDK
|
||||
uses: android-actions/setup-android@v3
|
||||
|
||||
- name: Restore keystore
|
||||
env:
|
||||
MEALIENT_KEY_STORE: ${{ secrets.MEALIENT_KEY_STORE }}
|
||||
MEALIENT_KEY_STORE_PASSWORD: ${{ secrets.MEALIENT_KEY_STORE_PASSWORD }}
|
||||
MEALIENT_KEY_ALIAS: ${{ secrets.MEALIENT_KEY_ALIAS }}
|
||||
MEALIENT_KEY_PASSWORD: ${{ secrets.MEALIENT_KEY_PASSWORD }}
|
||||
run: |
|
||||
echo "$MEALIENT_KEY_STORE" | base64 -d > app/keystore.jks
|
||||
echo "storeFile=keystore.jks" > keystore.properties
|
||||
echo "storePassword=$MEALIENT_KEY_STORE_PASSWORD" >> keystore.properties
|
||||
echo "keyAlias=$MEALIENT_KEY_ALIAS" >> keystore.properties
|
||||
echo "keyPassword=$MEALIENT_KEY_PASSWORD" >> keystore.properties
|
||||
|
||||
- name: APK
|
||||
run: |
|
||||
./gradlew build
|
||||
cp app/build/outputs/apk/release/*.apk mealient-release.apk
|
||||
|
||||
- name: Bundle
|
||||
run: |
|
||||
./gradlew bundle
|
||||
cp app/build/outputs/bundle/release/*.aab mealient-release.aab
|
||||
|
||||
- name: Upload release build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Release build
|
||||
path: |
|
||||
mealient-release.apk
|
||||
mealient-release.aab
|
||||
|
||||
- name: SonarCloud
|
||||
env:
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
run: ./gradlew sonar
|
||||
3
LICENSE
3
LICENSE
@@ -1,6 +1,7 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022, Kirill Kamakin
|
||||
Copyright (c) 2025, Atridad Lahiji
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -16,6 +17,6 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
LIABILITY, WHETHER IN AN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Welcome to the Mealient app for Android!
|
||||
|
||||
This is an open source Android app developed by Kirill Kamakin. The source code is available on
|
||||
This is an open source Android app developed by Kirill Kamakin and forked by Atridad Lahiji. The source code is available on
|
||||
GitHub under the MIT license; the app is also available on Google Play.
|
||||
|
||||
I hereby state, to the best of my knowledge and belief, that I have not programmed this app to
|
||||
@@ -10,7 +10,5 @@ collect any personally identifiable information. All data created by the you (th
|
||||
the Mealie server(s) that you connect to. It can be removed by the administrator(s) of this (these)
|
||||
server(s).
|
||||
|
||||
Yours sincerely,
|
||||
Kirill Kamakin.
|
||||
Stockholm, Sweden
|
||||
mealient@gmail.com
|
||||
Yours sincerely,
|
||||
Kirill Kamakin and Atridad Lahiji
|
||||
|
||||
54
README.md
54
README.md
@@ -1,46 +1,26 @@
|
||||
[](https://crowdin.com/project/mealient)
|
||||
|
||||
<a href='https://play.google.com/store/apps/details?id=gq.kirmanak.mealient&utm_source=github&utm_campaign=readme&pcampaignid=pcampaignidMKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img width="200" alt='Get it on Google Play' src='https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png'/></a>
|
||||
<a href="https://f-droid.org/packages/gq.kirmanak.mealient">
|
||||
<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
|
||||
alt="Get it on F-Droid"
|
||||
height="80">
|
||||
</a>
|
||||
|
||||
|
||||
# Mealient
|
||||
|
||||
## DISCLAIMER
|
||||
## USAGE REQUIREMENTS
|
||||
|
||||
This project is developed independently from the core Mealie project. It is NOT associated with the
|
||||
core Mealie developers. Any issues must be reported to the Mealient repository, NOT the Mealie
|
||||
repository.
|
||||
- Android 8.0 or higher
|
||||
- A Mealie server running v3 or higher
|
||||
|
||||
## DOWNLOAD
|
||||
|
||||
You have two options:
|
||||
|
||||
1. Download the latest APK from the Released page
|
||||
2. [<img src="https://github.com/ImranR98/Obtainium/blob/main/assets/graphics/badge_obtainium.png?raw=true" alt="Obtainium" height="41">](https://apps.obtainium.imranr.dev/redirect?r=obtainium://app/%7B%22id%22%3A%22com.atridad.mealient%22%2C%22url%22%3A%22https%3A%2F%2Fgit.atri.dad%2Fatridad%2FMealient%2Freleases%22%2C%22author%22%3A%22git.atri.dad%22%2C%22name%22%3A%22Mealient%22%2C%22preferredApkIndex%22%3A0%2C%22additionalSettings%22%3A%22%7B%5C%22intermediateLink%5C%22%3A%5B%5D%2C%5C%22customLinkFilterRegex%5C%22%3A%5C%22%5C%22%2C%5C%22filterByLinkText%5C%22%3Afalse%2C%5C%22skipSort%5C%22%3Afalse%2C%5C%22reverseSort%5C%22%3Afalse%2C%5C%22sortByLastLinkSegment%5C%22%3Afalse%2C%5C%22versionExtractWholePage%5C%22%3Afalse%2C%5C%22requestHeader%5C%22%3A%5B%7B%5C%22requestHeader%5C%22%3A%5C%22User-Agent%3A%20Mozilla%2F5.0%20(Linux%3B%20Android%2010%3B%20K)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20Chrome%2F114.0.0.0%20Mobile%20Safari%2F537.36%5C%22%7D%5D%2C%5C%22defaultPseudoVersioningMethod%5C%22%3A%5C%22partialAPKHash%5C%22%2C%5C%22trackOnly%5C%22%3Afalse%2C%5C%22versionExtractionRegEx%5C%22%3A%5C%22%5C%22%2C%5C%22matchGroupToUse%5C%22%3A%5C%22%5C%22%2C%5C%22versionDetection%5C%22%3Afalse%2C%5C%22useVersionCodeAsOSVersion%5C%22%3Afalse%2C%5C%22apkFilterRegEx%5C%22%3A%5C%22%5C%22%2C%5C%22invertAPKFilter%5C%22%3Afalse%2C%5C%22autoApkFilterByArch%5C%22%3Atrue%2C%5C%22appName%5C%22%3A%5C%22%5C%22%2C%5C%22appAuthor%5C%22%3A%5C%22%5C%22%2C%5C%22shizukuPretendToBeGooglePlay%5C%22%3Afalse%2C%5C%22allowInsecure%5C%22%3Afalse%2C%5C%22exemptFromBackgroundUpdates%5C%22%3Afalse%2C%5C%22skipUpdateNotifications%5C%22%3Afalse%2C%5C%22about%5C%22%3A%5C%22%5C%22%2C%5C%22refreshBeforeDownload%5C%22%3Afalse%7D%22%2C%22overrideSource%22%3Anull%7D)
|
||||
|
||||
## DISCLAIMERS
|
||||
|
||||
This project is developed independently from the core Mealie project. It is NOT associated with the core Mealie developers. **Any issues must be reported to the Mealient repository, NOT the Mealie repository.**
|
||||
|
||||
Also, this is a fork of the original Mealient project. All credit goes to Kirill Kamakin on GitHub for the original project.
|
||||
|
||||
## What is this?
|
||||
|
||||
An unofficial Android client for [Mealie](https://github.com/mealie-recipes/mealie/). It enables you
|
||||
An **unofficial** Android client for [Mealie](https://github.com/mealie-recipes/mealie/). It enables you
|
||||
to
|
||||
easily access your recipes using an Android device. The main advantage over website is that
|
||||
recipe data is stored locally and can be accessed without the Internet connection.
|
||||
|
||||
## Status
|
||||
|
||||
Current version is a very early alpha which supports a small subset of the Mealie capabilities.
|
||||
Displays the list of recipes, some information about each of the recipes, even recipe creation is
|
||||
available!
|
||||
The list of shopping lists is also available, each shopping list can be viewed and modified.
|
||||
|
||||
## Screenshots
|
||||
|
||||
<img src="https://github.com/kirmanak/Mealient/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png?raw=true" width="236" height="500" /> <img src="https://github.com/kirmanak/Mealient/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png?raw=true" width="236" height="500" /> <img src="https://github.com/kirmanak/Mealient/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png?raw=true" width="236" height="500" /> <img src="https://github.com/kirmanak/Mealient/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png?raw=true" width="236" height="500" /> <img src="https://github.com/kirmanak/Mealient/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png?raw=true" width="236" height="500" /> <img src="https://github.com/kirmanak/Mealient/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png?raw=true" width="236" height="500" />
|
||||
|
||||
## How to install
|
||||
|
||||
There are three ways:
|
||||
1. Install it with Google Play using the badge above.
|
||||
2. Install it with F-droid using the badge above.
|
||||
3. Download the latest apk from the releases page.
|
||||
|
||||
## Contribution
|
||||
|
||||
Any contribution is greatly appreciated: translations, bug reports, feature requests and any PR.
|
||||
|
||||
@@ -4,20 +4,19 @@ import java.io.FileInputStream
|
||||
import java.util.Properties
|
||||
|
||||
plugins {
|
||||
id("gq.kirmanak.mealient.application")
|
||||
id("com.atridad.mealient.application")
|
||||
id("dagger.hilt.android.plugin")
|
||||
alias(libs.plugins.ksp)
|
||||
id("gq.kirmanak.mealient.compose.app")
|
||||
id("com.atridad.mealient.compose.app")
|
||||
}
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
applicationId = "gq.kirmanak.mealient"
|
||||
versionCode = 37
|
||||
versionName = "0.4.8"
|
||||
testInstrumentationRunner = "gq.kirmanak.mealient.MealientTestRunner"
|
||||
applicationId = "com.atridad.mealient"
|
||||
versionCode = 39
|
||||
versionName = "1.0.0"
|
||||
testInstrumentationRunner = "com.atridad.mealient.MealientTestRunner"
|
||||
testInstrumentationRunnerArguments += mapOf("clearPackageData" to "true")
|
||||
resourceConfigurations += listOf("en", "es", "ru", "fr", "nl", "pt", "de")
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
@@ -50,7 +49,7 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
namespace = "gq.kirmanak.mealient"
|
||||
namespace = "com.atridad.mealient"
|
||||
|
||||
packaging {
|
||||
resources.excludes += "DebugProbesKt.bin"
|
||||
@@ -77,6 +76,7 @@ dependencies {
|
||||
implementation(project(":logging"))
|
||||
implementation(project(":ui"))
|
||||
implementation(project(":features:shopping_lists"))
|
||||
implementation(project(":features:user_managment"))
|
||||
implementation(project(":model_mapper"))
|
||||
implementation(libs.android.material.material)
|
||||
implementation(libs.androidx.coreKtx)
|
||||
@@ -103,6 +103,7 @@ dependencies {
|
||||
kover(project(":datasource"))
|
||||
kover(project(":datastore"))
|
||||
kover(project(":features:shopping_lists"))
|
||||
kover(project(":features:user_managment"))
|
||||
kover(project(":logging"))
|
||||
kover(project(":model_mapper"))
|
||||
kover(project(":ui"))
|
||||
@@ -145,25 +146,25 @@ kover {
|
||||
filters {
|
||||
excludes {
|
||||
classes(
|
||||
"gq.kirmanak.mealient.datastore.recipe.AddRecipeInput*", // generated by data store
|
||||
"com.atridad.mealient.datastore.recipe.AddRecipeInput*", // generated by data store
|
||||
"*ComposableSingletons*", // generated by Compose
|
||||
"gq.kirmanak.mealient.database.AppDb_Impl*", // generated by Room
|
||||
"com.atridad.mealient.database.AppDb_Impl*", // generated by Room
|
||||
"*Dao_Impl*", // generated by Room
|
||||
"*Hilt_*", // generated by Hilt
|
||||
)
|
||||
packages(
|
||||
"gq.kirmanak.mealient*.destinations", // generated by Compose destinations
|
||||
"com.atridad.mealient*.destinations", // generated by Compose destinations
|
||||
)
|
||||
annotatedBy(
|
||||
"androidx.compose.ui.tooling.preview.Preview",
|
||||
"gq.kirmanak.mealient.ui.preview.ColorSchemePreview",
|
||||
"com.atridad.mealient.ui.preview.ColorSchemePreview",
|
||||
"androidx.compose.runtime.Composable",
|
||||
"dagger.Module",
|
||||
"dagger.internal.DaggerGenerated",
|
||||
)
|
||||
}
|
||||
includes {
|
||||
packages("gq.kirmanak.mealient")
|
||||
packages("com.atridad.mealient")
|
||||
}
|
||||
}
|
||||
variant("release") {
|
||||
|
||||
BIN
app/release/baselineProfiles/0/app-release.dm
Normal file
BIN
app/release/baselineProfiles/0/app-release.dm
Normal file
Binary file not shown.
BIN
app/release/baselineProfiles/1/app-release.dm
Normal file
BIN
app/release/baselineProfiles/1/app-release.dm
Normal file
Binary file not shown.
BIN
app/release/com.atridad.mealient_0.5.1.apk
Normal file
BIN
app/release/com.atridad.mealient_0.5.1.apk
Normal file
Binary file not shown.
37
app/release/output-metadata.json
Normal file
37
app/release/output-metadata.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"version": 3,
|
||||
"artifactType": {
|
||||
"type": "APK",
|
||||
"kind": "Directory"
|
||||
},
|
||||
"applicationId": "com.atridad.mealient",
|
||||
"variantName": "release",
|
||||
"elements": [
|
||||
{
|
||||
"type": "SINGLE",
|
||||
"filters": [],
|
||||
"attributes": [],
|
||||
"versionCode": 38,
|
||||
"versionName": "0.5.1",
|
||||
"outputFile": "app-release.apk"
|
||||
}
|
||||
],
|
||||
"elementType": "File",
|
||||
"baselineProfiles": [
|
||||
{
|
||||
"minApi": 28,
|
||||
"maxApi": 30,
|
||||
"baselineProfiles": [
|
||||
"baselineProfiles/1/app-release.dm"
|
||||
]
|
||||
},
|
||||
{
|
||||
"minApi": 31,
|
||||
"maxApi": 2147483647,
|
||||
"baselineProfiles": [
|
||||
"baselineProfiles/0/app-release.dm"
|
||||
]
|
||||
}
|
||||
],
|
||||
"minSdkVersionForDexing": 26
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package gq.kirmanak.mealient
|
||||
package com.atridad.mealient
|
||||
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import com.kaspersky.components.composesupport.config.withComposeSupport
|
||||
import com.kaspersky.kaspresso.kaspresso.Kaspresso
|
||||
import com.kaspersky.kaspresso.testcases.api.testcase.TestCase
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import gq.kirmanak.mealient.ui.activity.MainActivity
|
||||
import com.atridad.mealient.ui.activity.MainActivity
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
@@ -1,11 +1,11 @@
|
||||
package gq.kirmanak.mealient
|
||||
package com.atridad.mealient
|
||||
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import gq.kirmanak.mealient.screen.AuthenticationScreen
|
||||
import gq.kirmanak.mealient.screen.BaseUrlScreen
|
||||
import gq.kirmanak.mealient.screen.DisclaimerScreen
|
||||
import gq.kirmanak.mealient.screen.RecipesListScreen
|
||||
import gq.kirmanak.mealient.ui.disclaimer.DisclaimerViewModel
|
||||
import com.atridad.mealient.screen.AuthenticationScreen
|
||||
import com.atridad.mealient.screen.BaseUrlScreen
|
||||
import com.atridad.mealient.screen.DisclaimerScreen
|
||||
import com.atridad.mealient.screen.RecipesListScreen
|
||||
import com.atridad.mealient.ui.disclaimer.DisclaimerViewModel
|
||||
import io.github.kakaocup.compose.node.element.ComposeScreen.Companion.onComposeScreen
|
||||
import io.github.kakaocup.kakao.common.utilities.getResourceString
|
||||
import org.junit.Before
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient
|
||||
package com.atridad.mealient
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient
|
||||
package com.atridad.mealient
|
||||
|
||||
import android.util.Log
|
||||
import okhttp3.mockwebserver.Dispatcher
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.screen
|
||||
package com.atridad.mealient.screen
|
||||
|
||||
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
|
||||
import io.github.kakaocup.compose.node.element.ComposeScreen
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.screen
|
||||
package com.atridad.mealient.screen
|
||||
|
||||
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
|
||||
import io.github.kakaocup.compose.node.element.ComposeScreen
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.screen
|
||||
package com.atridad.mealient.screen
|
||||
|
||||
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
|
||||
import io.github.kakaocup.compose.node.element.ComposeScreen
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.screen
|
||||
package com.atridad.mealient.screen
|
||||
|
||||
import io.github.kakaocup.compose.node.builder.ViewBuilder
|
||||
import io.github.kakaocup.compose.node.core.BaseNode
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.screen
|
||||
package com.atridad.mealient.screen
|
||||
|
||||
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
|
||||
import io.github.kakaocup.compose.node.element.ComposeScreen
|
||||
@@ -6,7 +6,7 @@
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
<application
|
||||
android:name="gq.kirmanak.mealient.App"
|
||||
android:name="com.atridad.mealient.App"
|
||||
android:allowBackup="false"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/full_backup_rules"
|
||||
@@ -53,4 +53,4 @@
|
||||
</provider>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
</manifest>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package gq.kirmanak.mealient
|
||||
package com.atridad.mealient
|
||||
|
||||
import android.app.Application
|
||||
import coil.Coil
|
||||
import coil.ImageLoader
|
||||
import com.google.android.material.color.DynamicColors
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
import gq.kirmanak.mealient.architecture.configuration.BuildConfiguration
|
||||
import gq.kirmanak.mealient.data.migration.MigrationDetector
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import com.atridad.mealient.architecture.configuration.BuildConfiguration
|
||||
import com.atridad.mealient.data.migration.MigrationDetector
|
||||
import com.atridad.mealient.logging.Logger
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.atridad.mealient.data.add
|
||||
|
||||
import com.atridad.mealient.datasource.models.AddRecipeInfo
|
||||
|
||||
interface AddRecipeDataSource {
|
||||
|
||||
suspend fun addRecipe(recipe: AddRecipeInfo): String
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package gq.kirmanak.mealient.data.add
|
||||
package com.atridad.mealient.data.add
|
||||
|
||||
import gq.kirmanak.mealient.datasource.models.AddRecipeInfo
|
||||
import com.atridad.mealient.datasource.models.AddRecipeInfo
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface AddRecipeRepo {
|
||||
@@ -1,11 +1,11 @@
|
||||
package gq.kirmanak.mealient.data.add.impl
|
||||
package com.atridad.mealient.data.add.impl
|
||||
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeDataSource
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeRepo
|
||||
import gq.kirmanak.mealient.datasource.models.AddRecipeInfo
|
||||
import gq.kirmanak.mealient.datastore.recipe.AddRecipeStorage
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import gq.kirmanak.mealient.model_mapper.ModelMapper
|
||||
import com.atridad.mealient.data.add.AddRecipeDataSource
|
||||
import com.atridad.mealient.data.add.AddRecipeRepo
|
||||
import com.atridad.mealient.datasource.models.AddRecipeInfo
|
||||
import com.atridad.mealient.datastore.recipe.AddRecipeStorage
|
||||
import com.atridad.mealient.logging.Logger
|
||||
import com.atridad.mealient.model_mapper.ModelMapper
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.data.auth
|
||||
package com.atridad.mealient.data.auth
|
||||
|
||||
interface AuthDataSource {
|
||||
/**
|
||||
@@ -1,6 +1,6 @@
|
||||
package gq.kirmanak.mealient.data.auth
|
||||
package com.atridad.mealient.data.auth
|
||||
|
||||
import gq.kirmanak.mealient.shopping_lists.repo.ShoppingListsAuthRepo
|
||||
import com.atridad.mealient.shopping_lists.repo.ShoppingListsAuthRepo
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface AuthRepo : ShoppingListsAuthRepo {
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.data.auth
|
||||
package com.atridad.mealient.data.auth
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package gq.kirmanak.mealient.data.auth.impl
|
||||
package com.atridad.mealient.data.auth.impl
|
||||
|
||||
import gq.kirmanak.mealient.data.auth.AuthDataSource
|
||||
import gq.kirmanak.mealient.datasource.MealieDataSource
|
||||
import gq.kirmanak.mealient.datasource.models.CreateApiTokenRequest
|
||||
import com.atridad.mealient.data.auth.AuthDataSource
|
||||
import com.atridad.mealient.datasource.MealieDataSource
|
||||
import com.atridad.mealient.datasource.models.CreateApiTokenRequest
|
||||
import javax.inject.Inject
|
||||
|
||||
class AuthDataSourceImpl @Inject constructor(
|
||||
@@ -1,10 +1,10 @@
|
||||
package gq.kirmanak.mealient.data.auth.impl
|
||||
package com.atridad.mealient.data.auth.impl
|
||||
|
||||
import gq.kirmanak.mealient.data.auth.AuthDataSource
|
||||
import gq.kirmanak.mealient.data.auth.AuthRepo
|
||||
import gq.kirmanak.mealient.data.auth.AuthStorage
|
||||
import gq.kirmanak.mealient.datasource.AuthenticationProvider
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import com.atridad.mealient.data.auth.AuthDataSource
|
||||
import com.atridad.mealient.data.auth.AuthRepo
|
||||
import com.atridad.mealient.data.auth.AuthStorage
|
||||
import com.atridad.mealient.datasource.AuthenticationProvider
|
||||
import com.atridad.mealient.logging.Logger
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import javax.inject.Inject
|
||||
@@ -1,13 +1,13 @@
|
||||
package gq.kirmanak.mealient.data.auth.impl
|
||||
package com.atridad.mealient.data.auth.impl
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.core.content.edit
|
||||
import gq.kirmanak.mealient.data.auth.AuthStorage
|
||||
import gq.kirmanak.mealient.datasource.TokenChangeListener
|
||||
import gq.kirmanak.mealient.datastore.DataStoreModule.Companion.ENCRYPTED
|
||||
import gq.kirmanak.mealient.extensions.prefsChangeFlow
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import com.atridad.mealient.data.auth.AuthStorage
|
||||
import com.atridad.mealient.datasource.TokenChangeListener
|
||||
import com.atridad.mealient.datastore.DataStoreModule.Companion.ENCRYPTED
|
||||
import com.atridad.mealient.extensions.prefsChangeFlow
|
||||
import com.atridad.mealient.logging.Logger
|
||||
import kotlinx.coroutines.asCoroutineDispatcher
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
@@ -1,6 +1,6 @@
|
||||
package gq.kirmanak.mealient.data.auth.impl
|
||||
package com.atridad.mealient.data.auth.impl
|
||||
|
||||
import gq.kirmanak.mealient.logging.LogRedactor
|
||||
import com.atridad.mealient.logging.LogRedactor
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import java.net.URLEncoder
|
||||
import javax.inject.Inject
|
||||
@@ -1,6 +1,6 @@
|
||||
package gq.kirmanak.mealient.data.baseurl
|
||||
package com.atridad.mealient.data.baseurl
|
||||
|
||||
import gq.kirmanak.mealient.datasource.models.VersionResponse
|
||||
import com.atridad.mealient.datasource.models.VersionResponse
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface ServerInfoRepo {
|
||||
@@ -1,8 +1,8 @@
|
||||
package gq.kirmanak.mealient.data.baseurl
|
||||
package com.atridad.mealient.data.baseurl
|
||||
|
||||
import gq.kirmanak.mealient.datasource.ServerUrlProvider
|
||||
import gq.kirmanak.mealient.datasource.models.VersionResponse
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import com.atridad.mealient.datasource.ServerUrlProvider
|
||||
import com.atridad.mealient.datasource.models.VersionResponse
|
||||
import com.atridad.mealient.logging.Logger
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.data.baseurl
|
||||
package com.atridad.mealient.data.baseurl
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.atridad.mealient.data.baseurl
|
||||
|
||||
import com.atridad.mealient.datasource.models.VersionResponse
|
||||
|
||||
interface VersionDataSource {
|
||||
|
||||
suspend fun requestVersion(baseURL: String): VersionResponse
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package gq.kirmanak.mealient.data.baseurl
|
||||
package com.atridad.mealient.data.baseurl
|
||||
|
||||
import gq.kirmanak.mealient.datasource.MealieDataSource
|
||||
import gq.kirmanak.mealient.datasource.models.VersionResponse
|
||||
import com.atridad.mealient.datasource.MealieDataSource
|
||||
import com.atridad.mealient.datasource.models.VersionResponse
|
||||
import javax.inject.Inject
|
||||
|
||||
class VersionDataSourceImpl @Inject constructor(
|
||||
@@ -1,9 +1,9 @@
|
||||
package gq.kirmanak.mealient.data.baseurl.impl
|
||||
package com.atridad.mealient.data.baseurl.impl
|
||||
|
||||
import androidx.core.net.toUri
|
||||
import gq.kirmanak.mealient.architecture.configuration.AppDispatchers
|
||||
import gq.kirmanak.mealient.data.storage.PreferencesStorage
|
||||
import gq.kirmanak.mealient.logging.LogRedactor
|
||||
import com.atridad.mealient.architecture.configuration.AppDispatchers
|
||||
import com.atridad.mealient.data.storage.PreferencesStorage
|
||||
import com.atridad.mealient.logging.LogRedactor
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -1,8 +1,8 @@
|
||||
package gq.kirmanak.mealient.data.baseurl.impl
|
||||
package com.atridad.mealient.data.baseurl.impl
|
||||
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import gq.kirmanak.mealient.data.baseurl.ServerInfoStorage
|
||||
import gq.kirmanak.mealient.data.storage.PreferencesStorage
|
||||
import com.atridad.mealient.data.baseurl.ServerInfoStorage
|
||||
import com.atridad.mealient.data.storage.PreferencesStorage
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package gq.kirmanak.mealient.data.configuration
|
||||
package com.atridad.mealient.data.configuration
|
||||
|
||||
import gq.kirmanak.mealient.BuildConfig
|
||||
import gq.kirmanak.mealient.architecture.configuration.BuildConfiguration
|
||||
import com.atridad.mealient.BuildConfig
|
||||
import com.atridad.mealient.architecture.configuration.BuildConfiguration
|
||||
import javax.inject.Inject
|
||||
|
||||
class BuildConfigurationImpl @Inject constructor() : BuildConfiguration {
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.data.disclaimer
|
||||
package com.atridad.mealient.data.disclaimer
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package gq.kirmanak.mealient.data.disclaimer
|
||||
package com.atridad.mealient.data.disclaimer
|
||||
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import gq.kirmanak.mealient.data.storage.PreferencesStorage
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import com.atridad.mealient.data.storage.PreferencesStorage
|
||||
import com.atridad.mealient.logging.Logger
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import javax.inject.Inject
|
||||
@@ -1,11 +1,11 @@
|
||||
package gq.kirmanak.mealient.data.migration
|
||||
package com.atridad.mealient.data.migration
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
import gq.kirmanak.mealient.data.auth.AuthRepo
|
||||
import gq.kirmanak.mealient.datasource.runCatchingExceptCancel
|
||||
import gq.kirmanak.mealient.datastore.DataStoreModule.Companion.ENCRYPTED
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import com.atridad.mealient.data.auth.AuthRepo
|
||||
import com.atridad.mealient.datasource.runCatchingExceptCancel
|
||||
import com.atridad.mealient.datastore.DataStoreModule.Companion.ENCRYPTED
|
||||
import com.atridad.mealient.logging.Logger
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.data.migration
|
||||
package com.atridad.mealient.data.migration
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
@@ -6,7 +6,7 @@ import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
import gq.kirmanak.mealient.datastore.DataStoreModule
|
||||
import com.atridad.mealient.datastore.DataStoreModule
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.data.migration
|
||||
package com.atridad.mealient.data.migration
|
||||
|
||||
interface MigrationDetector {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package gq.kirmanak.mealient.data.migration
|
||||
package com.atridad.mealient.data.migration
|
||||
|
||||
import gq.kirmanak.mealient.architecture.configuration.BuildConfiguration
|
||||
import gq.kirmanak.mealient.data.storage.PreferencesStorage
|
||||
import gq.kirmanak.mealient.datasource.runCatchingExceptCancel
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import com.atridad.mealient.architecture.configuration.BuildConfiguration
|
||||
import com.atridad.mealient.data.storage.PreferencesStorage
|
||||
import com.atridad.mealient.datasource.runCatchingExceptCancel
|
||||
import com.atridad.mealient.logging.Logger
|
||||
import javax.inject.Inject
|
||||
|
||||
class MigrationDetectorImpl @Inject constructor(
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.data.migration
|
||||
package com.atridad.mealient.data.migration
|
||||
|
||||
interface MigrationExecutor {
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.atridad.mealient.data.network
|
||||
|
||||
import com.atridad.mealient.data.add.AddRecipeDataSource
|
||||
import com.atridad.mealient.data.recipes.network.RecipeDataSource
|
||||
import com.atridad.mealient.data.share.ParseRecipeDataSource
|
||||
import com.atridad.mealient.datasource.MealieDataSource
|
||||
import com.atridad.mealient.datasource.models.AddRecipeInfo
|
||||
import com.atridad.mealient.datasource.models.GetRecipeResponse
|
||||
import com.atridad.mealient.datasource.models.GetRecipeSummaryResponse
|
||||
import com.atridad.mealient.datasource.models.ParseRecipeURLRequest
|
||||
import com.atridad.mealient.model_mapper.ModelMapper
|
||||
import javax.inject.Inject
|
||||
|
||||
class MealieDataSourceWrapper
|
||||
@Inject
|
||||
constructor(
|
||||
private val dataSource: MealieDataSource,
|
||||
private val modelMapper: ModelMapper,
|
||||
) : AddRecipeDataSource, RecipeDataSource, ParseRecipeDataSource {
|
||||
|
||||
override suspend fun addRecipe(recipe: AddRecipeInfo): String {
|
||||
val slug = dataSource.createRecipe(modelMapper.toCreateRequest(recipe))
|
||||
dataSource.updateRecipe(slug, modelMapper.toUpdateRequest(recipe))
|
||||
return slug
|
||||
}
|
||||
|
||||
override suspend fun requestRecipes(
|
||||
start: Int,
|
||||
limit: Int,
|
||||
): List<GetRecipeSummaryResponse> {
|
||||
// Imagine start is 30 and limit is 15. It means that we already have page 1 and 2, now we
|
||||
// need page 3
|
||||
val page = start / limit + 1
|
||||
return dataSource.requestRecipes(page, limit)
|
||||
}
|
||||
|
||||
override suspend fun requestRecipe(slug: String): GetRecipeResponse {
|
||||
return dataSource.requestRecipeInfo(slug)
|
||||
}
|
||||
|
||||
override suspend fun parseRecipeFromURL(parseRecipeURLInfo: ParseRecipeURLRequest): String {
|
||||
return dataSource.parseRecipeFromURL(parseRecipeURLInfo)
|
||||
}
|
||||
|
||||
override suspend fun getFavoriteRecipes(): List<String> {
|
||||
val userInfo = dataSource.requestUserInfo()
|
||||
|
||||
// Use the correct favorites endpoint that actually works
|
||||
return try {
|
||||
val favoritesResponse = dataSource.getUserFavoritesAlternative(userInfo.id)
|
||||
val favoriteRecipeIds =
|
||||
favoritesResponse.ratings.filter { it.isFavorite }.map { it.recipeId }
|
||||
|
||||
// Get all recipes to create UUID-to-slug mapping
|
||||
val allRecipes = dataSource.requestRecipes(1, -1) // Get all recipes
|
||||
val uuidToSlugMap = allRecipes.associate { it.remoteId to it.slug }
|
||||
|
||||
// Map favorite UUIDs to slugs
|
||||
val favoriteSlugs = favoriteRecipeIds.mapNotNull { uuid -> uuidToSlugMap[uuid] }
|
||||
|
||||
favoriteSlugs
|
||||
} catch (e: Exception) {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun updateIsRecipeFavorite(recipeSlug: String, isFavorite: Boolean) {
|
||||
val userInfo = dataSource.requestUserInfo()
|
||||
val userId = userInfo.id
|
||||
|
||||
if (isFavorite) {
|
||||
dataSource.addFavoriteRecipe(userId, recipeSlug)
|
||||
} else {
|
||||
dataSource.removeFavoriteRecipe(userId, recipeSlug)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun deleteRecipe(recipeSlug: String) {
|
||||
dataSource.deleteRecipe(recipeSlug)
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package gq.kirmanak.mealient.data.recipes
|
||||
package com.atridad.mealient.data.recipes
|
||||
|
||||
import androidx.paging.Pager
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeWithSummaryAndIngredientsAndInstructions
|
||||
import com.atridad.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
import com.atridad.mealient.database.recipe.entity.RecipeWithSummaryAndIngredientsAndInstructions
|
||||
|
||||
interface RecipeRepo {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.data.recipes.impl
|
||||
package com.atridad.mealient.data.recipes.impl
|
||||
|
||||
interface RecipeImageUrlProvider {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package gq.kirmanak.mealient.data.recipes.impl
|
||||
package com.atridad.mealient.data.recipes.impl
|
||||
|
||||
import android.net.Uri
|
||||
import gq.kirmanak.mealient.data.baseurl.ServerInfoRepo
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import com.atridad.mealient.data.baseurl.ServerInfoRepo
|
||||
import com.atridad.mealient.logging.Logger
|
||||
import javax.inject.Inject
|
||||
|
||||
class RecipeImageUrlProviderImpl @Inject constructor(
|
||||
@@ -1,7 +1,7 @@
|
||||
package gq.kirmanak.mealient.data.recipes.impl
|
||||
package com.atridad.mealient.data.recipes.impl
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
import com.atridad.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
|
||||
interface RecipePagingSourceFactory : () -> PagingSource<Int, RecipeSummaryEntity> {
|
||||
fun setQuery(newQuery: String?)
|
||||
@@ -1,8 +1,8 @@
|
||||
package gq.kirmanak.mealient.data.recipes.impl
|
||||
package com.atridad.mealient.data.recipes.impl
|
||||
|
||||
import androidx.paging.InvalidatingPagingSourceFactory
|
||||
import gq.kirmanak.mealient.database.recipe.RecipeStorage
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import com.atridad.mealient.database.recipe.RecipeStorage
|
||||
import com.atridad.mealient.logging.Logger
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
@@ -1,17 +1,17 @@
|
||||
package gq.kirmanak.mealient.data.recipes.impl
|
||||
package com.atridad.mealient.data.recipes.impl
|
||||
|
||||
import androidx.paging.ExperimentalPagingApi
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import gq.kirmanak.mealient.data.recipes.RecipeRepo
|
||||
import gq.kirmanak.mealient.data.recipes.network.RecipeDataSource
|
||||
import gq.kirmanak.mealient.database.recipe.RecipeStorage
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeIngredientToInstructionEntity
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeWithSummaryAndIngredientsAndInstructions
|
||||
import gq.kirmanak.mealient.datasource.runCatchingExceptCancel
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import gq.kirmanak.mealient.model_mapper.ModelMapper
|
||||
import com.atridad.mealient.data.recipes.RecipeRepo
|
||||
import com.atridad.mealient.data.recipes.network.RecipeDataSource
|
||||
import com.atridad.mealient.database.recipe.RecipeStorage
|
||||
import com.atridad.mealient.database.recipe.entity.RecipeIngredientToInstructionEntity
|
||||
import com.atridad.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
import com.atridad.mealient.database.recipe.entity.RecipeWithSummaryAndIngredientsAndInstructions
|
||||
import com.atridad.mealient.datasource.runCatchingExceptCancel
|
||||
import com.atridad.mealient.logging.Logger
|
||||
import com.atridad.mealient.model_mapper.ModelMapper
|
||||
import javax.inject.Inject
|
||||
|
||||
@OptIn(ExperimentalPagingApi::class)
|
||||
@@ -0,0 +1,96 @@
|
||||
package com.atridad.mealient.data.recipes.impl
|
||||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.paging.*
|
||||
import androidx.paging.LoadType.PREPEND
|
||||
import androidx.paging.LoadType.REFRESH
|
||||
import com.atridad.mealient.architecture.configuration.AppDispatchers
|
||||
import com.atridad.mealient.data.recipes.network.RecipeDataSource
|
||||
import com.atridad.mealient.database.recipe.RecipeStorage
|
||||
import com.atridad.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
import com.atridad.mealient.datasource.runCatchingExceptCancel
|
||||
import com.atridad.mealient.logging.Logger
|
||||
import com.atridad.mealient.model_mapper.ModelMapper
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@OptIn(ExperimentalPagingApi::class)
|
||||
@Singleton
|
||||
class RecipesRemoteMediator
|
||||
@Inject
|
||||
constructor(
|
||||
private val storage: RecipeStorage,
|
||||
private val network: RecipeDataSource,
|
||||
private val pagingSourceFactory: RecipePagingSourceFactory,
|
||||
private val logger: Logger,
|
||||
private val modelMapper: ModelMapper,
|
||||
private val dispatchers: AppDispatchers,
|
||||
) : RemoteMediator<Int, RecipeSummaryEntity>() {
|
||||
|
||||
@VisibleForTesting var lastRequestEnd: Int = 0
|
||||
|
||||
override suspend fun load(
|
||||
loadType: LoadType,
|
||||
state: PagingState<Int, RecipeSummaryEntity>
|
||||
): MediatorResult {
|
||||
logger.v {
|
||||
"load() called with: lastRequestEnd = $lastRequestEnd, loadType = $loadType, state = $state"
|
||||
}
|
||||
|
||||
if (loadType == PREPEND) {
|
||||
logger.i { "load: early exit, PREPEND isn't supported" }
|
||||
return MediatorResult.Success(endOfPaginationReached = true)
|
||||
}
|
||||
|
||||
val start = if (loadType == REFRESH) 0 else lastRequestEnd
|
||||
val limit = if (loadType == REFRESH) state.config.initialLoadSize else state.config.pageSize
|
||||
|
||||
val count: Int =
|
||||
runCatchingExceptCancel { updateRecipes(start, limit, loadType) }.getOrElse {
|
||||
logger.e(it) { "load: can't load recipes" }
|
||||
return MediatorResult.Error(it)
|
||||
}
|
||||
|
||||
// After something is inserted into DB the paging sources have to be invalidated
|
||||
// But for some reason Room/Paging library don't do it automatically
|
||||
// Here we invalidate them manually.
|
||||
// Read that trick here
|
||||
// https://github.com/android/architecture-components-samples/issues/889#issuecomment-880847858
|
||||
pagingSourceFactory.invalidate()
|
||||
|
||||
logger.d { "load: expectedCount = $limit, received $count" }
|
||||
lastRequestEnd = start + count
|
||||
return MediatorResult.Success(endOfPaginationReached = count < limit)
|
||||
}
|
||||
|
||||
suspend fun updateRecipes(
|
||||
start: Int,
|
||||
limit: Int,
|
||||
loadType: LoadType = REFRESH,
|
||||
): Int = coroutineScope {
|
||||
logger.v {
|
||||
"updateRecipes() called with: start = $start, limit = $limit, loadType = $loadType"
|
||||
}
|
||||
val deferredRecipes = async { network.requestRecipes(start, limit) }
|
||||
val favorites =
|
||||
runCatchingExceptCancel { network.getFavoriteRecipes() }
|
||||
.getOrDefault(emptyList())
|
||||
.toHashSet()
|
||||
|
||||
val recipes = deferredRecipes.await()
|
||||
|
||||
val entities =
|
||||
withContext(dispatchers.default) {
|
||||
recipes.map { recipe ->
|
||||
val isFavorite = favorites.contains(recipe.slug)
|
||||
modelMapper.toRecipeSummaryEntity(recipe, isFavorite)
|
||||
}
|
||||
}
|
||||
|
||||
if (loadType == REFRESH) storage.refreshAll(entities) else storage.saveRecipes(entities)
|
||||
recipes.size
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package gq.kirmanak.mealient.data.recipes.network
|
||||
package com.atridad.mealient.data.recipes.network
|
||||
|
||||
import gq.kirmanak.mealient.datasource.models.GetRecipeResponse
|
||||
import gq.kirmanak.mealient.datasource.models.GetRecipeSummaryResponse
|
||||
import com.atridad.mealient.datasource.models.GetRecipeResponse
|
||||
import com.atridad.mealient.datasource.models.GetRecipeSummaryResponse
|
||||
|
||||
interface RecipeDataSource {
|
||||
suspend fun requestRecipes(start: Int, limit: Int): List<GetRecipeSummaryResponse>
|
||||
@@ -1,6 +1,6 @@
|
||||
package gq.kirmanak.mealient.data.share
|
||||
package com.atridad.mealient.data.share
|
||||
|
||||
import gq.kirmanak.mealient.datasource.models.ParseRecipeURLRequest
|
||||
import com.atridad.mealient.datasource.models.ParseRecipeURLRequest
|
||||
|
||||
interface ParseRecipeDataSource {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.data.share
|
||||
package com.atridad.mealient.data.share
|
||||
|
||||
interface ShareRecipeRepo {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package gq.kirmanak.mealient.data.share
|
||||
package com.atridad.mealient.data.share
|
||||
|
||||
import androidx.core.util.PatternsCompat
|
||||
import gq.kirmanak.mealient.datasource.models.ParseRecipeURLRequest
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import com.atridad.mealient.datasource.models.ParseRecipeURLRequest
|
||||
import com.atridad.mealient.logging.Logger
|
||||
import javax.inject.Inject
|
||||
|
||||
class ShareRecipeRepoImpl @Inject constructor(
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.data.storage
|
||||
package com.atridad.mealient.data.storage
|
||||
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
@@ -11,6 +11,8 @@ interface PreferencesStorage {
|
||||
|
||||
val lastExecutedMigrationVersionKey: Preferences.Key<Int>
|
||||
|
||||
val themeModeKey: Preferences.Key<String>
|
||||
|
||||
suspend fun <T> getValue(key: Preferences.Key<T>): T?
|
||||
|
||||
suspend fun <T> requireValue(key: Preferences.Key<T>): T
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.data.storage
|
||||
package com.atridad.mealient.data.storage
|
||||
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
@@ -6,7 +6,7 @@ import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.intPreferencesKey
|
||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import com.atridad.mealient.logging.Logger
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.first
|
||||
@@ -29,6 +29,9 @@ class PreferencesStorageImpl @Inject constructor(
|
||||
override val lastExecutedMigrationVersionKey: Preferences.Key<Int> =
|
||||
intPreferencesKey("lastExecutedMigrationVersion")
|
||||
|
||||
override val themeModeKey: Preferences.Key<String> =
|
||||
stringPreferencesKey("themeMode")
|
||||
|
||||
override suspend fun <T> getValue(key: Preferences.Key<T>): T? {
|
||||
val value = dataStore.data.first()[key]
|
||||
logger.v { "getValue() returned: $value for $key" }
|
||||
@@ -1,15 +1,15 @@
|
||||
package gq.kirmanak.mealient.di
|
||||
package com.atridad.mealient.di
|
||||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeDataSource
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeRepo
|
||||
import gq.kirmanak.mealient.data.add.impl.AddRecipeRepoImpl
|
||||
import gq.kirmanak.mealient.data.network.MealieDataSourceWrapper
|
||||
import gq.kirmanak.mealient.datastore.recipe.AddRecipeStorage
|
||||
import gq.kirmanak.mealient.datastore.recipe.AddRecipeStorageImpl
|
||||
import com.atridad.mealient.data.add.AddRecipeDataSource
|
||||
import com.atridad.mealient.data.add.AddRecipeRepo
|
||||
import com.atridad.mealient.data.add.impl.AddRecipeRepoImpl
|
||||
import com.atridad.mealient.data.network.MealieDataSourceWrapper
|
||||
import com.atridad.mealient.datastore.recipe.AddRecipeStorage
|
||||
import com.atridad.mealient.datastore.recipe.AddRecipeStorageImpl
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.di
|
||||
package com.atridad.mealient.di
|
||||
|
||||
import android.content.Context
|
||||
import androidx.datastore.core.DataStore
|
||||
@@ -12,8 +12,8 @@ import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import gq.kirmanak.mealient.data.storage.PreferencesStorage
|
||||
import gq.kirmanak.mealient.data.storage.PreferencesStorageImpl
|
||||
import com.atridad.mealient.data.storage.PreferencesStorage
|
||||
import com.atridad.mealient.data.storage.PreferencesStorageImpl
|
||||
import okhttp3.OkHttpClient
|
||||
import javax.inject.Singleton
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package gq.kirmanak.mealient.di
|
||||
package com.atridad.mealient.di
|
||||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import gq.kirmanak.mealient.architecture.configuration.BuildConfiguration
|
||||
import gq.kirmanak.mealient.data.configuration.BuildConfigurationImpl
|
||||
import com.atridad.mealient.architecture.configuration.BuildConfiguration
|
||||
import com.atridad.mealient.data.configuration.BuildConfigurationImpl
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
@@ -1,20 +1,20 @@
|
||||
package gq.kirmanak.mealient.di
|
||||
package com.atridad.mealient.di
|
||||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import dagger.multibindings.IntoSet
|
||||
import gq.kirmanak.mealient.data.auth.AuthDataSource
|
||||
import gq.kirmanak.mealient.data.auth.AuthRepo
|
||||
import gq.kirmanak.mealient.data.auth.AuthStorage
|
||||
import gq.kirmanak.mealient.data.auth.impl.AuthDataSourceImpl
|
||||
import gq.kirmanak.mealient.data.auth.impl.AuthRepoImpl
|
||||
import gq.kirmanak.mealient.data.auth.impl.AuthStorageImpl
|
||||
import gq.kirmanak.mealient.data.auth.impl.CredentialsLogRedactor
|
||||
import gq.kirmanak.mealient.datasource.AuthenticationProvider
|
||||
import gq.kirmanak.mealient.logging.LogRedactor
|
||||
import gq.kirmanak.mealient.shopping_lists.repo.ShoppingListsAuthRepo
|
||||
import com.atridad.mealient.data.auth.AuthDataSource
|
||||
import com.atridad.mealient.data.auth.AuthRepo
|
||||
import com.atridad.mealient.data.auth.AuthStorage
|
||||
import com.atridad.mealient.data.auth.impl.AuthDataSourceImpl
|
||||
import com.atridad.mealient.data.auth.impl.AuthRepoImpl
|
||||
import com.atridad.mealient.data.auth.impl.AuthStorageImpl
|
||||
import com.atridad.mealient.data.auth.impl.CredentialsLogRedactor
|
||||
import com.atridad.mealient.datasource.AuthenticationProvider
|
||||
import com.atridad.mealient.logging.LogRedactor
|
||||
import com.atridad.mealient.shopping_lists.repo.ShoppingListsAuthRepo
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
@@ -1,15 +1,15 @@
|
||||
package gq.kirmanak.mealient.di
|
||||
package com.atridad.mealient.di
|
||||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import dagger.multibindings.IntoSet
|
||||
import gq.kirmanak.mealient.data.baseurl.*
|
||||
import gq.kirmanak.mealient.data.baseurl.impl.BaseUrlLogRedactor
|
||||
import gq.kirmanak.mealient.data.baseurl.impl.ServerInfoStorageImpl
|
||||
import gq.kirmanak.mealient.datasource.ServerUrlProvider
|
||||
import gq.kirmanak.mealient.logging.LogRedactor
|
||||
import com.atridad.mealient.data.baseurl.*
|
||||
import com.atridad.mealient.data.baseurl.impl.BaseUrlLogRedactor
|
||||
import com.atridad.mealient.data.baseurl.impl.ServerInfoStorageImpl
|
||||
import com.atridad.mealient.datasource.ServerUrlProvider
|
||||
import com.atridad.mealient.logging.LogRedactor
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
@@ -1,11 +1,11 @@
|
||||
package gq.kirmanak.mealient.di
|
||||
package com.atridad.mealient.di
|
||||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import gq.kirmanak.mealient.data.disclaimer.DisclaimerStorage
|
||||
import gq.kirmanak.mealient.data.disclaimer.DisclaimerStorageImpl
|
||||
import com.atridad.mealient.data.disclaimer.DisclaimerStorage
|
||||
import com.atridad.mealient.data.disclaimer.DisclaimerStorageImpl
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
@@ -1,15 +1,15 @@
|
||||
package gq.kirmanak.mealient.di
|
||||
package com.atridad.mealient.di
|
||||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import dagger.multibindings.IntoSet
|
||||
import gq.kirmanak.mealient.data.migration.From24AuthMigrationExecutor
|
||||
import gq.kirmanak.mealient.data.migration.From30MigrationExecutor
|
||||
import gq.kirmanak.mealient.data.migration.MigrationDetector
|
||||
import gq.kirmanak.mealient.data.migration.MigrationDetectorImpl
|
||||
import gq.kirmanak.mealient.data.migration.MigrationExecutor
|
||||
import com.atridad.mealient.data.migration.From24AuthMigrationExecutor
|
||||
import com.atridad.mealient.data.migration.From30MigrationExecutor
|
||||
import com.atridad.mealient.data.migration.MigrationDetector
|
||||
import com.atridad.mealient.data.migration.MigrationDetectorImpl
|
||||
import com.atridad.mealient.data.migration.MigrationExecutor
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
@@ -1,13 +1,13 @@
|
||||
package gq.kirmanak.mealient.di
|
||||
package com.atridad.mealient.di
|
||||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import gq.kirmanak.mealient.data.network.MealieDataSourceWrapper
|
||||
import gq.kirmanak.mealient.data.recipes.RecipeRepo
|
||||
import gq.kirmanak.mealient.data.recipes.impl.*
|
||||
import gq.kirmanak.mealient.data.recipes.network.RecipeDataSource
|
||||
import com.atridad.mealient.data.network.MealieDataSourceWrapper
|
||||
import com.atridad.mealient.data.recipes.RecipeRepo
|
||||
import com.atridad.mealient.data.recipes.impl.*
|
||||
import com.atridad.mealient.data.recipes.network.RecipeDataSource
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
@@ -1,13 +1,13 @@
|
||||
package gq.kirmanak.mealient.di
|
||||
package com.atridad.mealient.di
|
||||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import gq.kirmanak.mealient.data.network.MealieDataSourceWrapper
|
||||
import gq.kirmanak.mealient.data.share.ParseRecipeDataSource
|
||||
import gq.kirmanak.mealient.data.share.ShareRecipeRepo
|
||||
import gq.kirmanak.mealient.data.share.ShareRecipeRepoImpl
|
||||
import com.atridad.mealient.data.network.MealieDataSourceWrapper
|
||||
import com.atridad.mealient.data.share.ParseRecipeDataSource
|
||||
import com.atridad.mealient.data.share.ShareRecipeRepo
|
||||
import com.atridad.mealient.data.share.ShareRecipeRepoImpl
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.extensions
|
||||
package com.atridad.mealient.extensions
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.extensions
|
||||
package com.atridad.mealient.extensions
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
@@ -7,7 +7,7 @@ import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
import android.os.Build
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.StringRes
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import com.atridad.mealient.logging.Logger
|
||||
import kotlinx.coroutines.channels.ChannelResult
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.channels.onClosed
|
||||
@@ -1,16 +1,18 @@
|
||||
package gq.kirmanak.mealient.ui
|
||||
package com.atridad.mealient.ui
|
||||
|
||||
import com.ramcosta.composedestinations.spec.DestinationSpec
|
||||
import com.ramcosta.composedestinations.spec.NavGraphSpec
|
||||
import com.ramcosta.composedestinations.spec.Route
|
||||
import gq.kirmanak.mealient.shopping_lists.ui.destinations.ShoppingListScreenDestination
|
||||
import gq.kirmanak.mealient.shopping_lists.ui.destinations.ShoppingListsScreenDestination
|
||||
import gq.kirmanak.mealient.ui.destinations.AddRecipeScreenDestination
|
||||
import gq.kirmanak.mealient.ui.destinations.AuthenticationScreenDestination
|
||||
import gq.kirmanak.mealient.ui.destinations.BaseURLScreenDestination
|
||||
import gq.kirmanak.mealient.ui.destinations.DisclaimerScreenDestination
|
||||
import gq.kirmanak.mealient.ui.destinations.RecipeScreenDestination
|
||||
import gq.kirmanak.mealient.ui.destinations.RecipesListDestination
|
||||
import com.atridad.mealient.shopping_lists.ui.destinations.ShoppingListScreenDestination
|
||||
import com.atridad.mealient.shopping_lists.ui.destinations.ShoppingListsScreenDestination
|
||||
import com.atridad.mealient.ui.destinations.AddRecipeScreenDestination
|
||||
import com.atridad.mealient.ui.destinations.AuthenticationScreenDestination
|
||||
import com.atridad.mealient.ui.destinations.BaseURLScreenDestination
|
||||
import com.atridad.mealient.ui.destinations.DisclaimerScreenDestination
|
||||
import com.atridad.mealient.ui.destinations.RecipeScreenDestination
|
||||
import com.atridad.mealient.ui.destinations.RecipesListDestination
|
||||
import com.atridad.mealient.ui.destinations.SettingsScreenDestination
|
||||
import com.mealient.user_management.ui.profile.destinations.UserProfileScreenDestination
|
||||
|
||||
internal object NavGraphs {
|
||||
|
||||
@@ -40,6 +42,8 @@ internal object NavGraphs {
|
||||
DisclaimerScreenDestination,
|
||||
BaseURLScreenDestination,
|
||||
AuthenticationScreenDestination,
|
||||
SettingsScreenDestination,
|
||||
UserProfileScreenDestination,
|
||||
),
|
||||
nestedNavGraphs = listOf(
|
||||
recipes,
|
||||
@@ -1,11 +1,12 @@
|
||||
package gq.kirmanak.mealient.ui.activity
|
||||
package com.atridad.mealient.ui.activity
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.Email
|
||||
import androidx.compose.material.icons.filled.List
|
||||
import androidx.compose.material.icons.filled.Logout
|
||||
import androidx.compose.material.icons.automirrored.filled.List
|
||||
import androidx.compose.material.icons.automirrored.filled.Logout
|
||||
import androidx.compose.material.icons.filled.Person
|
||||
import androidx.compose.material.icons.filled.ShoppingCart
|
||||
import androidx.compose.material.icons.filled.SyncAlt
|
||||
import androidx.compose.material3.DrawerState
|
||||
@@ -22,12 +23,13 @@ import com.ramcosta.composedestinations.spec.Direction
|
||||
import com.ramcosta.composedestinations.spec.NavGraphSpec
|
||||
import com.ramcosta.composedestinations.utils.contains
|
||||
import com.ramcosta.composedestinations.utils.currentDestinationAsState
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.ui.NavGraphs
|
||||
import gq.kirmanak.mealient.ui.components.DrawerItem
|
||||
import gq.kirmanak.mealient.ui.destinations.AddRecipeScreenDestination
|
||||
import gq.kirmanak.mealient.ui.destinations.BaseURLScreenDestination
|
||||
import gq.kirmanak.mealient.ui.destinations.RecipesListDestination
|
||||
import com.atridad.mealient.R
|
||||
import com.atridad.mealient.ui.NavGraphs
|
||||
import com.atridad.mealient.ui.components.DrawerItem
|
||||
import com.atridad.mealient.ui.destinations.AddRecipeScreenDestination
|
||||
import com.atridad.mealient.ui.destinations.BaseURLScreenDestination
|
||||
import com.atridad.mealient.ui.destinations.RecipesListDestination
|
||||
import com.mealient.user_management.ui.profile.destinations.UserProfileScreenDestination
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
@@ -78,7 +80,7 @@ internal fun createDrawerItems(
|
||||
return listOf(
|
||||
createNavigationItem(
|
||||
nameRes = R.string.menu_navigation_drawer_recipes_list,
|
||||
icon = Icons.Default.List,
|
||||
icon = Icons.AutoMirrored.Filled.List,
|
||||
direction = RecipesListDestination,
|
||||
),
|
||||
createNavigationItem(
|
||||
@@ -91,6 +93,11 @@ internal fun createDrawerItems(
|
||||
icon = Icons.Default.ShoppingCart,
|
||||
direction = NavGraphs.shoppingLists,
|
||||
),
|
||||
createNavigationItem(
|
||||
nameRes = R.string.menu_navigation_drawer_profile,
|
||||
icon = Icons.Default.Person,
|
||||
direction = UserProfileScreenDestination,
|
||||
),
|
||||
createNavigationItem(
|
||||
nameRes = R.string.menu_navigation_drawer_change_url,
|
||||
icon = Icons.Default.SyncAlt,
|
||||
@@ -98,7 +105,7 @@ internal fun createDrawerItems(
|
||||
),
|
||||
createActionItem(
|
||||
nameRes = R.string.menu_navigation_drawer_logout,
|
||||
icon = Icons.Default.Logout,
|
||||
icon = Icons.AutoMirrored.Filled.Logout,
|
||||
appEvent = AppEvent.Logout,
|
||||
),
|
||||
createActionItem(
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.atridad.mealient.ui.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import com.atridad.mealient.extensions.isDarkThemeOn
|
||||
import com.atridad.mealient.logging.Logger
|
||||
import com.atridad.mealient.ui.theme.MealientTheme
|
||||
import com.atridad.mealient.data.storage.PreferencesStorage
|
||||
import com.atridad.mealient.ui.settings.ThemeMode
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
@Inject
|
||||
lateinit var logger: Logger
|
||||
|
||||
private val viewModel by viewModels<MainActivityViewModel>()
|
||||
|
||||
@Inject lateinit var prefs: PreferencesStorage
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
val splashScreen = installSplashScreen()
|
||||
super.onCreate(savedInstanceState)
|
||||
logger.v { "onCreate() called with: savedInstanceState = $savedInstanceState" }
|
||||
// Status bar appearance is now handled by the Material 3 theme
|
||||
// Navigation bar appearance can still be set here if needed
|
||||
with(WindowInsetsControllerCompat(window, window.decorView)) {
|
||||
val isAppearanceLightBars = !isDarkThemeOn()
|
||||
isAppearanceLightNavigationBars = isAppearanceLightBars
|
||||
}
|
||||
splashScreen.setKeepOnScreenCondition {
|
||||
viewModel.appState.value.forcedRoute == ForcedDestination.Undefined
|
||||
}
|
||||
setContent {
|
||||
// Observe theme changes live from preferences
|
||||
val initialMode = runBlocking { prefs.getValue(prefs.themeModeKey) } ?: ThemeMode.DEVICE.name
|
||||
val themeName = prefs.valueUpdates(prefs.themeModeKey)
|
||||
.collectAsState(initial = initialMode).value ?: initialMode
|
||||
val selectedMode = runCatching { ThemeMode.valueOf(themeName) }.getOrDefault(ThemeMode.DEVICE)
|
||||
val dark = when (selectedMode) {
|
||||
ThemeMode.DEVICE -> androidx.compose.foundation.isSystemInDarkTheme()
|
||||
ThemeMode.LIGHT -> false
|
||||
ThemeMode.DARK -> true
|
||||
}
|
||||
MealientTheme(darkTheme = dark) {
|
||||
MealientApp(viewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.ui.activity
|
||||
package com.atridad.mealient.ui.activity
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Intent
|
||||
@@ -7,16 +7,16 @@ import androidx.core.content.FileProvider
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.data.auth.AuthRepo
|
||||
import gq.kirmanak.mealient.data.baseurl.ServerInfoRepo
|
||||
import gq.kirmanak.mealient.data.disclaimer.DisclaimerStorage
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import gq.kirmanak.mealient.logging.getLogFile
|
||||
import gq.kirmanak.mealient.ui.destinations.AuthenticationScreenDestination
|
||||
import gq.kirmanak.mealient.ui.destinations.BaseURLScreenDestination
|
||||
import gq.kirmanak.mealient.ui.destinations.DirectionDestination
|
||||
import gq.kirmanak.mealient.ui.destinations.DisclaimerScreenDestination
|
||||
import com.atridad.mealient.R
|
||||
import com.atridad.mealient.data.auth.AuthRepo
|
||||
import com.atridad.mealient.data.baseurl.ServerInfoRepo
|
||||
import com.atridad.mealient.data.disclaimer.DisclaimerStorage
|
||||
import com.atridad.mealient.logging.Logger
|
||||
import com.atridad.mealient.logging.getLogFile
|
||||
import com.atridad.mealient.ui.destinations.AuthenticationScreenDestination
|
||||
import com.atridad.mealient.ui.destinations.BaseURLScreenDestination
|
||||
import com.atridad.mealient.ui.destinations.DirectionDestination
|
||||
import com.atridad.mealient.ui.destinations.DisclaimerScreenDestination
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
@@ -0,0 +1,299 @@
|
||||
package com.atridad.mealient.ui.activity
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.List
|
||||
import androidx.compose.material.icons.filled.ShoppingCart
|
||||
import androidx.compose.material.icons.filled.Person
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material.icons.filled.ShoppingCart
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.BottomAppBar
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.NavigationBarItem
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.surfaceColorAtElevation
|
||||
import androidx.compose.ui.graphics.luminance
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.navigation.NavHostController
|
||||
import com.ramcosta.composedestinations.DestinationsNavHost
|
||||
import com.ramcosta.composedestinations.animations.defaults.RootNavGraphDefaultAnimations
|
||||
import com.ramcosta.composedestinations.navigation.dependency
|
||||
import com.ramcosta.composedestinations.navigation.navigate
|
||||
import com.ramcosta.composedestinations.navigation.popUpTo
|
||||
import com.ramcosta.composedestinations.rememberNavHostEngine
|
||||
import com.ramcosta.composedestinations.spec.DestinationSpec
|
||||
import com.ramcosta.composedestinations.spec.NavHostEngine
|
||||
import com.ramcosta.composedestinations.spec.Route
|
||||
import com.ramcosta.composedestinations.utils.currentDestinationAsState
|
||||
import com.atridad.mealient.R
|
||||
import com.atridad.mealient.ui.NavGraphs
|
||||
import com.atridad.mealient.ui.destinations.RecipesListDestination
|
||||
import com.atridad.mealient.ui.destinations.AddRecipeScreenDestination
|
||||
import com.atridad.mealient.ui.destinations.SettingsScreenDestination
|
||||
import com.atridad.mealient.shopping_lists.ui.destinations.ShoppingListsScreenDestination
|
||||
import com.mealient.user_management.ui.profile.destinations.UserProfileScreenDestination
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
|
||||
|
||||
@Composable
|
||||
internal fun MealientApp(
|
||||
viewModel: MainActivityViewModel,
|
||||
) {
|
||||
val state by viewModel.appState.collectAsState()
|
||||
|
||||
MealientApp(
|
||||
state = state,
|
||||
onEvent = viewModel::onEvent,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MealientApp(
|
||||
state: MealientAppState,
|
||||
onEvent: (AppEvent) -> Unit,
|
||||
) {
|
||||
val engine = rememberNavHostEngine(
|
||||
rootDefaultAnimations = RootNavGraphDefaultAnimations.ACCOMPANIST_FADING,
|
||||
)
|
||||
val controller = engine.rememberNavController()
|
||||
|
||||
val currentDestinationState = controller.currentDestinationAsState()
|
||||
val currentDestination = currentDestinationState.value
|
||||
|
||||
// Ensure system bars match app colors
|
||||
val view = LocalView.current
|
||||
val barsColor = androidx.compose.material3.MaterialTheme.colorScheme.surface
|
||||
// Match Android navigation bar to the BottomAppBar's elevated container color
|
||||
val bottomBarColor = androidx.compose.material3.MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp)
|
||||
// Decide icon appearance from actual nav bar color brightness to match app-selected theme
|
||||
val lightBars = bottomBarColor.luminance() > 0.5f
|
||||
androidx.compose.runtime.SideEffect {
|
||||
val window = (view.context as android.app.Activity).window
|
||||
window.navigationBarColor = bottomBarColor.toArgb()
|
||||
window.statusBarColor = barsColor.toArgb()
|
||||
val controller = WindowCompat.getInsetsController(window, view)
|
||||
controller.isAppearanceLightNavigationBars = lightBars
|
||||
controller.isAppearanceLightStatusBars = lightBars
|
||||
}
|
||||
|
||||
ForceNavigationEffect(
|
||||
currentDestination = currentDestination,
|
||||
controller = controller,
|
||||
forcedDestination = state.forcedRoute
|
||||
)
|
||||
|
||||
IntentLaunchEffect(
|
||||
intent = state.intentToLaunch,
|
||||
onEvent = onEvent,
|
||||
)
|
||||
|
||||
if (state.dialogState != null) {
|
||||
MealientDialog(
|
||||
dialogState = state.dialogState,
|
||||
onEvent = onEvent,
|
||||
)
|
||||
}
|
||||
|
||||
AppContent(
|
||||
engine = engine,
|
||||
controller = controller,
|
||||
startRoute = (state.forcedRoute as? ForcedDestination.Destination)?.route,
|
||||
onEvent = onEvent,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun IntentLaunchEffect(
|
||||
intent: Intent?,
|
||||
onEvent: (AppEvent) -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
LaunchedEffect(intent) {
|
||||
if (intent != null) {
|
||||
context.startActivity(intent)
|
||||
onEvent(AppEvent.LaunchedIntent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MealientDialog(
|
||||
dialogState: DialogState,
|
||||
onEvent: (AppEvent) -> Unit,
|
||||
) {
|
||||
androidx.compose.material3.AlertDialog(
|
||||
onDismissRequest = {
|
||||
onEvent(dialogState.onDismiss)
|
||||
},
|
||||
confirmButton = {
|
||||
androidx.compose.material3.TextButton(
|
||||
onClick = { onEvent(dialogState.onPositiveClick) },
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = dialogState.positiveButton),
|
||||
style = androidx.compose.material3.MaterialTheme.typography.labelLarge
|
||||
)
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
androidx.compose.material3.TextButton(
|
||||
onClick = { onEvent(dialogState.onNegativeClick) },
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = dialogState.negativeButton),
|
||||
style = androidx.compose.material3.MaterialTheme.typography.labelLarge
|
||||
)
|
||||
}
|
||||
},
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(id = dialogState.title),
|
||||
style = androidx.compose.material3.MaterialTheme.typography.headlineSmall,
|
||||
color = androidx.compose.material3.MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(id = dialogState.message),
|
||||
style = androidx.compose.material3.MaterialTheme.typography.bodyMedium,
|
||||
color = androidx.compose.material3.MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
},
|
||||
containerColor = androidx.compose.material3.MaterialTheme.colorScheme.surface,
|
||||
titleContentColor = androidx.compose.material3.MaterialTheme.colorScheme.onSurface,
|
||||
textContentColor = androidx.compose.material3.MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
shape = androidx.compose.foundation.shape.RoundedCornerShape(28.dp)
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ForceNavigationEffect(
|
||||
currentDestination: DestinationSpec<*>?,
|
||||
controller: NavHostController,
|
||||
forcedDestination: ForcedDestination,
|
||||
) {
|
||||
LaunchedEffect(currentDestination, forcedDestination) {
|
||||
if (
|
||||
forcedDestination is ForcedDestination.Destination &&
|
||||
currentDestination != null &&
|
||||
currentDestination != forcedDestination.route
|
||||
) {
|
||||
controller.navigate(forcedDestination.route) {
|
||||
popUpTo(currentDestination) {
|
||||
inclusive = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AppContent(
|
||||
engine: NavHostEngine,
|
||||
controller: NavHostController,
|
||||
startRoute: Route?,
|
||||
onEvent: (AppEvent) -> Unit,
|
||||
) {
|
||||
val currentDestination by controller.currentDestinationAsState()
|
||||
val view = LocalView.current
|
||||
val bottomBarColor = androidx.compose.material3.MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp)
|
||||
val lightNavIcons = bottomBarColor.luminance() > 0.5f
|
||||
androidx.compose.runtime.SideEffect {
|
||||
val window = (view.context as android.app.Activity).window
|
||||
window.navigationBarColor = bottomBarColor.toArgb()
|
||||
val controllerInsets = WindowCompat.getInsetsController(window, view)
|
||||
controllerInsets.isAppearanceLightNavigationBars = lightNavIcons
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
contentWindowInsets = WindowInsets(0.dp),
|
||||
bottomBar = {
|
||||
BottomAppBar(
|
||||
windowInsets = WindowInsets(0.dp),
|
||||
containerColor = bottomBarColor,
|
||||
actions = {
|
||||
NavigationBarItem(
|
||||
icon = { Icon(Icons.AutoMirrored.Filled.List, contentDescription = "Recipes") },
|
||||
label = { Text(stringResource(R.string.menu_navigation_drawer_recipes_list)) },
|
||||
selected = currentDestination?.route == RecipesListDestination.route,
|
||||
onClick = {
|
||||
controller.navigate(RecipesListDestination) {
|
||||
popUpTo(controller.graph.startDestinationId) {
|
||||
inclusive = false
|
||||
}
|
||||
launchSingleTop = true
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
NavigationBarItem(
|
||||
icon = { Icon(Icons.Default.ShoppingCart, contentDescription = "Shopping Lists") },
|
||||
label = { Text(stringResource(R.string.menu_navigation_drawer_shopping_lists)) },
|
||||
selected = currentDestination?.route == ShoppingListsScreenDestination.route,
|
||||
onClick = {
|
||||
controller.navigate(ShoppingListsScreenDestination) {
|
||||
popUpTo(controller.graph.startDestinationId) {
|
||||
inclusive = false
|
||||
}
|
||||
launchSingleTop = true
|
||||
}
|
||||
}
|
||||
)
|
||||
NavigationBarItem(
|
||||
icon = { Icon(Icons.Default.Settings, contentDescription = "Settings") },
|
||||
label = { Text("Settings") },
|
||||
selected = currentDestination?.route == SettingsScreenDestination.route,
|
||||
onClick = {
|
||||
controller.navigate(SettingsScreenDestination) {
|
||||
popUpTo(controller.graph.startDestinationId) {
|
||||
inclusive = false
|
||||
}
|
||||
launchSingleTop = true
|
||||
}
|
||||
}
|
||||
)
|
||||
NavigationBarItem(
|
||||
icon = { Icon(Icons.Default.Person, contentDescription = "Profile") },
|
||||
label = { Text(stringResource(R.string.menu_navigation_drawer_profile)) },
|
||||
selected = currentDestination?.route == UserProfileScreenDestination.route,
|
||||
onClick = {
|
||||
controller.navigate(UserProfileScreenDestination) {
|
||||
popUpTo(controller.graph.startDestinationId) {
|
||||
inclusive = false
|
||||
}
|
||||
launchSingleTop = true
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
DestinationsNavHost(
|
||||
navGraph = NavGraphs.root,
|
||||
engine = engine,
|
||||
navController = controller,
|
||||
startRoute = startRoute ?: NavGraphs.root.startRoute,
|
||||
modifier = Modifier.padding(paddingValues)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package gq.kirmanak.mealient.ui.add
|
||||
package com.atridad.mealient.ui.add
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
@@ -29,25 +30,20 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.ui.AppTheme
|
||||
import gq.kirmanak.mealient.ui.Dimens
|
||||
import gq.kirmanak.mealient.ui.components.BaseScreenState
|
||||
import gq.kirmanak.mealient.ui.components.BaseScreenWithNavigation
|
||||
import gq.kirmanak.mealient.ui.components.TopProgressIndicator
|
||||
import gq.kirmanak.mealient.ui.components.previewBaseScreenState
|
||||
import gq.kirmanak.mealient.ui.preview.ColorSchemePreview
|
||||
import com.atridad.mealient.R
|
||||
import com.atridad.mealient.ui.AppTheme
|
||||
import com.atridad.mealient.ui.Dimens
|
||||
import com.atridad.mealient.ui.components.TopProgressIndicator
|
||||
import com.atridad.mealient.ui.preview.ColorSchemePreview
|
||||
|
||||
@Destination
|
||||
@Composable
|
||||
internal fun AddRecipeScreen(
|
||||
baseScreenState: BaseScreenState,
|
||||
viewModel: AddRecipeViewModel = hiltViewModel()
|
||||
) {
|
||||
val screenState by viewModel.screenState.collectAsState()
|
||||
|
||||
AddRecipeScreen(
|
||||
baseScreenState = baseScreenState,
|
||||
state = screenState,
|
||||
onEvent = viewModel::onEvent,
|
||||
)
|
||||
@@ -55,7 +51,6 @@ internal fun AddRecipeScreen(
|
||||
|
||||
@Composable
|
||||
internal fun AddRecipeScreen(
|
||||
baseScreenState: BaseScreenState,
|
||||
state: AddRecipeScreenState,
|
||||
onEvent: (AddRecipeScreenEvent) -> Unit,
|
||||
) {
|
||||
@@ -74,19 +69,14 @@ internal fun AddRecipeScreen(
|
||||
snackbarHostState.currentSnackbarData?.dismiss()
|
||||
}
|
||||
|
||||
BaseScreenWithNavigation(
|
||||
baseScreenState = baseScreenState,
|
||||
snackbarHostState = snackbarHostState,
|
||||
) { modifier ->
|
||||
TopProgressIndicator(
|
||||
modifier = modifier,
|
||||
isLoading = state.isLoading,
|
||||
) {
|
||||
AddRecipeScreenContent(
|
||||
state = state,
|
||||
onEvent = onEvent,
|
||||
)
|
||||
}
|
||||
TopProgressIndicator(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
isLoading = state.isLoading,
|
||||
) {
|
||||
AddRecipeScreenContent(
|
||||
state = state,
|
||||
onEvent = onEvent,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,7 +294,6 @@ private fun AddRecipeInputField(
|
||||
private fun AddRecipeScreenPreview() {
|
||||
AppTheme {
|
||||
AddRecipeScreen(
|
||||
baseScreenState = previewBaseScreenState(),
|
||||
state = AddRecipeScreenState(),
|
||||
onEvent = {},
|
||||
)
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.ui.add
|
||||
package com.atridad.mealient.ui.add
|
||||
|
||||
internal sealed interface AddRecipeScreenEvent {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.ui.add
|
||||
package com.atridad.mealient.ui.add
|
||||
|
||||
internal data class AddRecipeScreenState(
|
||||
val snackbarMessage: AddRecipeSnackbarMessage? = null,
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.ui.add
|
||||
package com.atridad.mealient.ui.add
|
||||
|
||||
internal sealed interface AddRecipeSnackbarMessage {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
package gq.kirmanak.mealient.ui.add
|
||||
package com.atridad.mealient.ui.add
|
||||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeRepo
|
||||
import gq.kirmanak.mealient.datasource.models.AddRecipeInfo
|
||||
import gq.kirmanak.mealient.datasource.models.AddRecipeIngredientInfo
|
||||
import gq.kirmanak.mealient.datasource.models.AddRecipeInstructionInfo
|
||||
import gq.kirmanak.mealient.datasource.models.AddRecipeSettingsInfo
|
||||
import gq.kirmanak.mealient.datasource.runCatchingExceptCancel
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import com.atridad.mealient.data.add.AddRecipeRepo
|
||||
import com.atridad.mealient.datasource.models.AddRecipeInfo
|
||||
import com.atridad.mealient.datasource.models.AddRecipeIngredientInfo
|
||||
import com.atridad.mealient.datasource.models.AddRecipeInstructionInfo
|
||||
import com.atridad.mealient.datasource.models.AddRecipeSettingsInfo
|
||||
import com.atridad.mealient.datasource.runCatchingExceptCancel
|
||||
import com.atridad.mealient.logging.Logger
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.ui.auth
|
||||
package com.atridad.mealient.ui.auth
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -25,12 +25,12 @@ import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.navigation.NavController
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.ui.AppTheme
|
||||
import gq.kirmanak.mealient.ui.Dimens
|
||||
import gq.kirmanak.mealient.ui.components.BaseScreen
|
||||
import gq.kirmanak.mealient.ui.components.TopProgressIndicator
|
||||
import gq.kirmanak.mealient.ui.preview.ColorSchemePreview
|
||||
import com.atridad.mealient.R
|
||||
import com.atridad.mealient.ui.AppTheme
|
||||
import com.atridad.mealient.ui.Dimens
|
||||
import com.atridad.mealient.ui.components.BaseScreen
|
||||
import com.atridad.mealient.ui.components.TopProgressIndicator
|
||||
import com.atridad.mealient.ui.preview.ColorSchemePreview
|
||||
|
||||
@Destination
|
||||
@Composable
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.ui.auth
|
||||
package com.atridad.mealient.ui.auth
|
||||
|
||||
internal sealed interface AuthenticationScreenEvent {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.ui.auth
|
||||
package com.atridad.mealient.ui.auth
|
||||
|
||||
internal data class AuthenticationScreenState(
|
||||
val isLoading: Boolean = false,
|
||||
@@ -1,14 +1,14 @@
|
||||
package gq.kirmanak.mealient.ui.auth
|
||||
package com.atridad.mealient.ui.auth
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.data.auth.AuthRepo
|
||||
import gq.kirmanak.mealient.datasource.NetworkError
|
||||
import gq.kirmanak.mealient.datasource.runCatchingExceptCancel
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import com.atridad.mealient.R
|
||||
import com.atridad.mealient.data.auth.AuthRepo
|
||||
import com.atridad.mealient.datasource.NetworkError
|
||||
import com.atridad.mealient.datasource.runCatchingExceptCancel
|
||||
import com.atridad.mealient.logging.Logger
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.ui.auth
|
||||
package com.atridad.mealient.ui.auth
|
||||
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.ui.auth
|
||||
package com.atridad.mealient.ui.auth
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
@@ -21,7 +21,7 @@ import androidx.compose.ui.semantics.testTag
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import gq.kirmanak.mealient.R
|
||||
import com.atridad.mealient.R
|
||||
|
||||
@OptIn(ExperimentalComposeUiApi::class)
|
||||
@Composable
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.ui.baseurl
|
||||
package com.atridad.mealient.ui.baseurl
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -27,21 +27,16 @@ import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.navigation.NavController
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.ui.AppTheme
|
||||
import gq.kirmanak.mealient.ui.Dimens
|
||||
import gq.kirmanak.mealient.ui.components.BaseScreen
|
||||
import gq.kirmanak.mealient.ui.components.BaseScreenState
|
||||
import gq.kirmanak.mealient.ui.components.BaseScreenWithNavigation
|
||||
import gq.kirmanak.mealient.ui.components.TopProgressIndicator
|
||||
import gq.kirmanak.mealient.ui.components.previewBaseScreenState
|
||||
import gq.kirmanak.mealient.ui.preview.ColorSchemePreview
|
||||
import com.atridad.mealient.R
|
||||
import com.atridad.mealient.ui.AppTheme
|
||||
import com.atridad.mealient.ui.Dimens
|
||||
import com.atridad.mealient.ui.components.TopProgressIndicator
|
||||
import com.atridad.mealient.ui.preview.ColorSchemePreview
|
||||
|
||||
@Destination
|
||||
@Composable
|
||||
internal fun BaseURLScreen(
|
||||
navController: NavController,
|
||||
baseScreenState: BaseScreenState,
|
||||
viewModel: BaseURLViewModel = hiltViewModel(),
|
||||
) {
|
||||
val screenState by viewModel.screenState.collectAsState()
|
||||
@@ -54,7 +49,6 @@ internal fun BaseURLScreen(
|
||||
|
||||
BaseURLScreen(
|
||||
state = screenState,
|
||||
baseScreenState = baseScreenState,
|
||||
onEvent = viewModel::onEvent,
|
||||
)
|
||||
}
|
||||
@@ -62,27 +56,13 @@ internal fun BaseURLScreen(
|
||||
@Composable
|
||||
private fun BaseURLScreen(
|
||||
state: BaseURLScreenState,
|
||||
baseScreenState: BaseScreenState,
|
||||
onEvent: (BaseURLScreenEvent) -> Unit,
|
||||
) {
|
||||
val content: @Composable (Modifier) -> Unit = {
|
||||
BaseURLScreen(
|
||||
modifier = it,
|
||||
state = state,
|
||||
onEvent = onEvent,
|
||||
)
|
||||
}
|
||||
|
||||
if (state.isNavigationEnabled) {
|
||||
BaseScreenWithNavigation(
|
||||
baseScreenState = baseScreenState,
|
||||
content = content,
|
||||
)
|
||||
} else {
|
||||
BaseScreen(
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
BaseURLScreen(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
state = state,
|
||||
onEvent = onEvent,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -177,29 +157,7 @@ private fun UrlInputField(
|
||||
keyboardOptions = KeyboardOptions(
|
||||
imeAction = ImeAction.Done,
|
||||
),
|
||||
keyboardActions = KeyboardActions(
|
||||
onDone = {
|
||||
defaultKeyboardAction(ImeAction.Done)
|
||||
onEvent(BaseURLScreenEvent.OnProceedClick)
|
||||
},
|
||||
)
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
@ColorSchemePreview
|
||||
@Composable
|
||||
private fun BaseURLScreenPreview() {
|
||||
AppTheme {
|
||||
BaseURLScreen(
|
||||
state = BaseURLScreenState(
|
||||
userInput = "https://www.google.com",
|
||||
errorText = null,
|
||||
isButtonEnabled = true,
|
||||
isLoading = true,
|
||||
isNavigationEnabled = false,
|
||||
),
|
||||
baseScreenState = previewBaseScreenState(),
|
||||
onEvent = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.ui.baseurl
|
||||
package com.atridad.mealient.ui.baseurl
|
||||
|
||||
import java.security.cert.X509Certificate
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.ui.baseurl
|
||||
package com.atridad.mealient.ui.baseurl
|
||||
|
||||
internal data class BaseURLScreenState(
|
||||
val isConfigured: Boolean = false,
|
||||
@@ -1,19 +1,19 @@
|
||||
package gq.kirmanak.mealient.ui.baseurl
|
||||
package com.atridad.mealient.ui.baseurl
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.data.auth.AuthRepo
|
||||
import gq.kirmanak.mealient.data.baseurl.ServerInfoRepo
|
||||
import gq.kirmanak.mealient.data.baseurl.impl.BaseUrlLogRedactor
|
||||
import gq.kirmanak.mealient.data.recipes.RecipeRepo
|
||||
import gq.kirmanak.mealient.datasource.CertificateCombinedException
|
||||
import gq.kirmanak.mealient.datasource.NetworkError
|
||||
import gq.kirmanak.mealient.datasource.TrustedCertificatesStore
|
||||
import gq.kirmanak.mealient.datasource.findCauseAsInstanceOf
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import com.atridad.mealient.R
|
||||
import com.atridad.mealient.data.auth.AuthRepo
|
||||
import com.atridad.mealient.data.baseurl.ServerInfoRepo
|
||||
import com.atridad.mealient.data.baseurl.impl.BaseUrlLogRedactor
|
||||
import com.atridad.mealient.data.recipes.RecipeRepo
|
||||
import com.atridad.mealient.datasource.CertificateCombinedException
|
||||
import com.atridad.mealient.datasource.NetworkError
|
||||
import com.atridad.mealient.datasource.TrustedCertificatesStore
|
||||
import com.atridad.mealient.datasource.findCauseAsInstanceOf
|
||||
import com.atridad.mealient.logging.Logger
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
@@ -1,13 +1,13 @@
|
||||
package gq.kirmanak.mealient.ui.baseurl
|
||||
package com.atridad.mealient.ui.baseurl
|
||||
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.ui.AppTheme
|
||||
import gq.kirmanak.mealient.ui.preview.ColorSchemePreview
|
||||
import com.atridad.mealient.R
|
||||
import com.atridad.mealient.ui.AppTheme
|
||||
import com.atridad.mealient.ui.preview.ColorSchemePreview
|
||||
|
||||
@Composable
|
||||
internal fun InvalidCertificateDialog(
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.ui.disclaimer
|
||||
package com.atridad.mealient.ui.disclaimer
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -20,11 +20,11 @@ import androidx.compose.ui.semantics.testTag
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.navigation.NavController
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.ui.AppTheme
|
||||
import gq.kirmanak.mealient.ui.Dimens
|
||||
import gq.kirmanak.mealient.ui.components.BaseScreen
|
||||
import gq.kirmanak.mealient.ui.preview.ColorSchemePreview
|
||||
import com.atridad.mealient.R
|
||||
import com.atridad.mealient.ui.AppTheme
|
||||
import com.atridad.mealient.ui.Dimens
|
||||
import com.atridad.mealient.ui.components.BaseScreen
|
||||
import com.atridad.mealient.ui.preview.ColorSchemePreview
|
||||
|
||||
@Destination
|
||||
@Composable
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.ui.disclaimer
|
||||
package com.atridad.mealient.ui.disclaimer
|
||||
|
||||
internal data class DisclaimerScreenState(
|
||||
val isCountDownOver: Boolean,
|
||||
@@ -1,11 +1,11 @@
|
||||
package gq.kirmanak.mealient.ui.disclaimer
|
||||
package com.atridad.mealient.ui.disclaimer
|
||||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import gq.kirmanak.mealient.data.disclaimer.DisclaimerStorage
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import com.atridad.mealient.data.disclaimer.DisclaimerStorage
|
||||
import com.atridad.mealient.logging.Logger
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.ui.recipes.info
|
||||
package com.atridad.mealient.ui.recipes.info
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -17,8 +17,8 @@ import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil.compose.AsyncImage
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.ui.Dimens
|
||||
import com.atridad.mealient.R
|
||||
import com.atridad.mealient.ui.Dimens
|
||||
|
||||
@Composable
|
||||
internal fun HeaderSection(
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.ui.recipes.info
|
||||
package com.atridad.mealient.ui.recipes.info
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -19,9 +19,9 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeIngredientEntity
|
||||
import gq.kirmanak.mealient.ui.Dimens
|
||||
import com.atridad.mealient.R
|
||||
import com.atridad.mealient.database.recipe.entity.RecipeIngredientEntity
|
||||
import com.atridad.mealient.ui.Dimens
|
||||
|
||||
@Composable
|
||||
internal fun IngredientsSection(
|
||||
@@ -1,4 +1,4 @@
|
||||
package gq.kirmanak.mealient.ui.recipes.info
|
||||
package com.atridad.mealient.ui.recipes.info
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -13,10 +13,10 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeIngredientEntity
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeInstructionEntity
|
||||
import gq.kirmanak.mealient.ui.Dimens
|
||||
import com.atridad.mealient.R
|
||||
import com.atridad.mealient.database.recipe.entity.RecipeIngredientEntity
|
||||
import com.atridad.mealient.database.recipe.entity.RecipeInstructionEntity
|
||||
import com.atridad.mealient.ui.Dimens
|
||||
|
||||
@Composable
|
||||
internal fun InstructionsSection(
|
||||
@@ -1,10 +1,10 @@
|
||||
package gq.kirmanak.mealient.ui.recipes.info
|
||||
package com.atridad.mealient.ui.recipes.info
|
||||
|
||||
import android.view.WindowManager
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import gq.kirmanak.mealient.extensions.findActivity
|
||||
import com.atridad.mealient.extensions.findActivity
|
||||
|
||||
@Composable
|
||||
fun KeepScreenOn() {
|
||||
@@ -1,8 +1,8 @@
|
||||
package gq.kirmanak.mealient.ui.recipes.info
|
||||
package com.atridad.mealient.ui.recipes.info
|
||||
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeIngredientEntity
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeInstructionEntity
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
import com.atridad.mealient.database.recipe.entity.RecipeIngredientEntity
|
||||
import com.atridad.mealient.database.recipe.entity.RecipeInstructionEntity
|
||||
import com.atridad.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
import kotlinx.datetime.LocalDate
|
||||
|
||||
internal val INGREDIENT_TWO = RecipeIngredientEntity(
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user