Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
f79fd6d658 | ||
|
362cf8e347 | ||
|
2f4910bf07 | ||
|
03d07bb9da | ||
|
e4b8fb9a5f | ||
|
1819d11e39 | ||
|
ca3c675988 | ||
|
d3fde20a0a | ||
|
acd37136b0 | ||
|
042350ca59 | ||
|
cc6178a845 | ||
|
2a42e395cf | ||
|
8c7ed46049 | ||
|
d2db976f34 | ||
|
ae769e167a | ||
|
9360565c77 | ||
|
1ea8ed0e93 | ||
|
e4b1cf3daa | ||
|
bb21aa8cac | ||
|
fab50baea5 | ||
|
176147f583 | ||
|
7c3af23e3b |
42
.gitignore
vendored
42
.gitignore
vendored
@ -13,3 +13,45 @@
|
|||||||
.externalNativeBuild
|
.externalNativeBuild
|
||||||
.cxx
|
.cxx
|
||||||
local.properties
|
local.properties
|
||||||
|
|
||||||
|
HELP.md
|
||||||
|
.gradle
|
||||||
|
build/
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
data.mv.db
|
||||||
|
data.trace.db
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
bin/
|
||||||
|
!**/src/main/**/bin/
|
||||||
|
!**/src/test/**/bin/
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
out/
|
||||||
|
!**/src/main/**/out/
|
||||||
|
!**/src/test/**/out/
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
3
.idea/.gitignore
vendored
3
.idea/.gitignore
vendored
@ -1,3 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
@ -1 +0,0 @@
|
|||||||
Coffee Preorder
|
|
@ -3,7 +3,20 @@
|
|||||||
<component name="deploymentTargetDropDown">
|
<component name="deploymentTargetDropDown">
|
||||||
<value>
|
<value>
|
||||||
<entry key="app">
|
<entry key="app">
|
||||||
<State />
|
<State>
|
||||||
|
<targetSelectedWithDropDown>
|
||||||
|
<Target>
|
||||||
|
<type value="QUICK_BOOT_TARGET" />
|
||||||
|
<deviceKey>
|
||||||
|
<Key>
|
||||||
|
<type value="VIRTUAL_DEVICE_PATH" />
|
||||||
|
<value value="$USER_HOME$/.android/avd/Pixel_7_API_33.avd" />
|
||||||
|
</Key>
|
||||||
|
</deviceKey>
|
||||||
|
</Target>
|
||||||
|
</targetSelectedWithDropDown>
|
||||||
|
<timeTargetWasSelectedWithDropDown value="2023-12-20T08:37:35.519292927Z" />
|
||||||
|
</State>
|
||||||
</entry>
|
</entry>
|
||||||
</value>
|
</value>
|
||||||
</component>
|
</component>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<option name="linkedExternalProjectsSettings">
|
<option name="linkedExternalProjectsSettings">
|
||||||
<GradleProjectSettings>
|
<GradleProjectSettings>
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
<option name="gradleJvm" value="jbr-17" />
|
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
||||||
<option name="modules">
|
<option name="modules">
|
||||||
<set>
|
<set>
|
||||||
<option value="$PROJECT_DIR$" />
|
<option value="$PROJECT_DIR$" />
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<profile version="1.0">
|
|
||||||
<option name="myName" value="Project Default" />
|
|
||||||
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
|
|
||||||
<option name="composableFile" value="true" />
|
|
||||||
<option name="previewFile" value="true" />
|
|
||||||
</inspection_tool>
|
|
||||||
<inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
|
|
||||||
<option name="composableFile" value="true" />
|
|
||||||
<option name="previewFile" value="true" />
|
|
||||||
</inspection_tool>
|
|
||||||
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
|
||||||
<option name="composableFile" value="true" />
|
|
||||||
<option name="previewFile" value="true" />
|
|
||||||
</inspection_tool>
|
|
||||||
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
|
|
||||||
<option name="composableFile" value="true" />
|
|
||||||
<option name="previewFile" value="true" />
|
|
||||||
</inspection_tool>
|
|
||||||
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
|
|
||||||
<option name="composableFile" value="true" />
|
|
||||||
<option name="previewFile" value="true" />
|
|
||||||
</inspection_tool>
|
|
||||||
<inspection_tool class="PreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
|
||||||
<option name="composableFile" value="true" />
|
|
||||||
<option name="previewFile" value="true" />
|
|
||||||
</inspection_tool>
|
|
||||||
<inspection_tool class="PreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
|
||||||
<option name="composableFile" value="true" />
|
|
||||||
<option name="previewFile" value="true" />
|
|
||||||
</inspection_tool>
|
|
||||||
<inspection_tool class="PreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
|
||||||
<option name="composableFile" value="true" />
|
|
||||||
<option name="previewFile" value="true" />
|
|
||||||
</inspection_tool>
|
|
||||||
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
|
||||||
<option name="composableFile" value="true" />
|
|
||||||
<option name="previewFile" value="true" />
|
|
||||||
</inspection_tool>
|
|
||||||
</profile>
|
|
||||||
</component>
|
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="KotlinJpsPluginSettings">
|
<component name="KotlinJpsPluginSettings">
|
||||||
<option name="version" value="1.9.20" />
|
<option name="version" value="1.9.10" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="VcsDirectoryMappings">
|
<component name="VcsDirectoryMappings">
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
<mapping directory="" vcs="Git" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
@ -2,6 +2,7 @@ plugins {
|
|||||||
id("com.android.application")
|
id("com.android.application")
|
||||||
id("org.jetbrains.kotlin.android")
|
id("org.jetbrains.kotlin.android")
|
||||||
id("com.google.devtools.ksp")
|
id("com.google.devtools.ksp")
|
||||||
|
id("org.jetbrains.kotlin.plugin.serialization")
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@ -41,7 +42,7 @@ android {
|
|||||||
compose = true
|
compose = true
|
||||||
}
|
}
|
||||||
composeOptions {
|
composeOptions {
|
||||||
kotlinCompilerExtensionVersion = "1.5.5"
|
kotlinCompilerExtensionVersion = "1.5.3"
|
||||||
}
|
}
|
||||||
packaging {
|
packaging {
|
||||||
resources {
|
resources {
|
||||||
@ -56,10 +57,11 @@ kotlin {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// Retrofit
|
// Retrofit
|
||||||
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0")
|
|
||||||
implementation("com.squareup.retrofit2:retrofit:2.9.0")
|
implementation("com.squareup.retrofit2:retrofit:2.9.0")
|
||||||
implementation("io.coil-kt:coil-compose:2.5.0")
|
implementation("io.coil-kt:coil-compose:2.5.0")
|
||||||
|
implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
|
||||||
|
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0")
|
||||||
|
|
||||||
implementation("com.jcraft:jsch:0.1.55")
|
implementation("com.jcraft:jsch:0.1.55")
|
||||||
|
|
||||||
@ -68,30 +70,30 @@ dependencies {
|
|||||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
|
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
implementation("androidx.activity:activity-compose:1.8.1")
|
implementation("androidx.activity:activity-compose:1.8.2")
|
||||||
implementation(platform("androidx.compose:compose-bom:2023.10.01"))
|
implementation(platform("androidx.compose:compose-bom:2023.10.01"))
|
||||||
implementation("androidx.navigation:navigation-compose:2.7.5")
|
implementation("androidx.navigation:navigation-compose:2.7.6")
|
||||||
implementation("androidx.compose.ui:ui:1.6.0-beta02")
|
implementation("androidx.compose.ui:ui:1.6.0-beta03")
|
||||||
implementation("androidx.compose.ui:ui-graphics:1.6.0-beta02")
|
implementation("androidx.compose.ui:ui-graphics:1.6.0-beta03")
|
||||||
implementation("androidx.compose.ui:ui-tooling-preview:1.6.0-beta02")
|
implementation("androidx.compose.ui:ui-tooling-preview:1.6.0-beta03")
|
||||||
implementation("androidx.compose.material3:material3:1.1.2")
|
implementation("androidx.compose.material3:material3:1.1.2")
|
||||||
|
implementation("androidx.compose.material:material:1.5.4")
|
||||||
implementation("androidx.paging:paging-compose:3.2.1")
|
implementation("androidx.paging:paging-compose:3.2.1")
|
||||||
implementation("eu.bambooapps:compose-material3-pullrefresh:1.0.0")
|
implementation("eu.bambooapps:compose-material3-pullrefresh:1.0.1")
|
||||||
|
|
||||||
// Room
|
// Room
|
||||||
val roomVersion = "2.6.1"
|
implementation("androidx.room:room-runtime:2.6.1")
|
||||||
implementation("androidx.room:room-runtime:$roomVersion")
|
annotationProcessor("androidx.room:room-compiler:2.6.1")
|
||||||
annotationProcessor("androidx.room:room-compiler:$roomVersion")
|
ksp("androidx.room:room-compiler:2.6.1")
|
||||||
ksp("androidx.room:room-compiler:$roomVersion")
|
implementation("androidx.room:room-ktx:2.6.1")
|
||||||
implementation("androidx.room:room-ktx:$roomVersion")
|
implementation("androidx.room:room-paging:2.6.1")
|
||||||
implementation("androidx.room:room-paging:$roomVersion")
|
|
||||||
|
|
||||||
// Tests
|
// Tests
|
||||||
testImplementation("junit:junit:4.13.2")
|
testImplementation("junit:junit:4.13.2")
|
||||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||||
androidTestImplementation(platform("androidx.compose:compose-bom:2023.10.01"))
|
androidTestImplementation(platform("androidx.compose:compose-bom:2023.10.01"))
|
||||||
androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.6.0-beta02")
|
androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.6.0-beta03")
|
||||||
debugImplementation("androidx.compose.ui:ui-tooling:1.6.0-beta02")
|
debugImplementation("androidx.compose.ui:ui-tooling:1.6.0-beta03")
|
||||||
debugImplementation("androidx.compose.ui:ui-test-manifest:1.6.0-beta02")
|
debugImplementation("androidx.compose.ui:ui-test-manifest:1.6.0-beta03")
|
||||||
}
|
}
|
60
app/proguard-rules.pro
vendored
60
app/proguard-rules.pro
vendored
@ -1,21 +1,45 @@
|
|||||||
# Add project specific ProGuard rules here.
|
# Retrofit does reflection on generic parameters. InnerClasses is required to use Signature and
|
||||||
# You can control the set of applied configuration files using the
|
# EnclosingMethod is required to use InnerClasses.
|
||||||
# proguardFiles setting in build.gradle.
|
-keepattributes Signature, InnerClasses, EnclosingMethod
|
||||||
#
|
|
||||||
# For more details, see
|
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
|
||||||
|
|
||||||
# If your project uses WebView with JS, uncomment the following
|
# Retrofit does reflection on method and parameter annotations.
|
||||||
# and specify the fully qualified class name to the JavaScript interface
|
-keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations
|
||||||
# class:
|
|
||||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
|
||||||
# public *;
|
|
||||||
#}
|
|
||||||
|
|
||||||
# Uncomment this to preserve the line number information for
|
# Keep annotation default values (e.g., retrofit2.http.Field.encoded).
|
||||||
# debugging stack traces.
|
-keepattributes AnnotationDefault
|
||||||
#-keepattributes SourceFile,LineNumberTable
|
|
||||||
|
|
||||||
# If you keep the line number information, uncomment this to
|
# Retain service method parameters when optimizing.
|
||||||
# hide the original source file name.
|
-keepclassmembers,allowshrinking,allowobfuscation interface * {
|
||||||
#-renamesourcefileattribute SourceFile
|
@retrofit2.http.* <methods>;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ignore JSR 305 annotations for embedding nullability information.
|
||||||
|
-dontwarn javax.annotation.**
|
||||||
|
|
||||||
|
# Guarded by a NoClassDefFoundError try/catch and only used when on the classpath.
|
||||||
|
-dontwarn kotlin.Unit
|
||||||
|
|
||||||
|
# Top-level functions that can only be used by Kotlin.
|
||||||
|
-dontwarn retrofit2.KotlinExtensions
|
||||||
|
-dontwarn retrofit2.KotlinExtensions$*
|
||||||
|
|
||||||
|
# With R8 full mode, it sees no subtypes of Retrofit interfaces since they are created with a Proxy
|
||||||
|
# and replaces all potential values with null. Explicitly keeping the interfaces prevents this.
|
||||||
|
-if interface * { @retrofit2.http.* <methods>; }
|
||||||
|
-keep,allowobfuscation interface <1>
|
||||||
|
|
||||||
|
# Keep inherited services.
|
||||||
|
-if interface * { @retrofit2.http.* <methods>; }
|
||||||
|
-keep,allowobfuscation interface * extends <1>
|
||||||
|
|
||||||
|
# With R8 full mode generic signatures are stripped for classes that are not
|
||||||
|
# kept. Suspend functions are wrapped in continuations where the type argument
|
||||||
|
# is used.
|
||||||
|
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
|
||||||
|
|
||||||
|
# R8 full mode strips generic signatures from return types if not kept.
|
||||||
|
-if interface * { @retrofit2.http.* public *** *(...); }
|
||||||
|
-keep,allowoptimization,allowshrinking,allowobfuscation class <3>
|
||||||
|
|
||||||
|
# With R8 full mode generic signatures are stripped for classes that are not kept.
|
||||||
|
-keep,allowobfuscation,allowshrinking class retrofit2.Response
|
@ -16,7 +16,8 @@
|
|||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.CoffeePreorder"
|
android:theme="@style/Theme.CoffeePreorder"
|
||||||
tools:targetApi="31">
|
tools:targetApi="31"
|
||||||
|
android:networkSecurityConfig="@xml/network_security_config">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainComposeActivity"
|
android:name=".MainComposeActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
@ -3,10 +3,15 @@ package com.zyzf.coffeepreorder
|
|||||||
import android.app.Application
|
import android.app.Application
|
||||||
import com.zyzf.coffeepreorder.database.AppContainer
|
import com.zyzf.coffeepreorder.database.AppContainer
|
||||||
import com.zyzf.coffeepreorder.database.AppDataContainer
|
import com.zyzf.coffeepreorder.database.AppDataContainer
|
||||||
|
import com.zyzf.coffeepreorder.database.model.User
|
||||||
|
|
||||||
class CoffeeApplication : Application() {
|
class CoffeeApplication : Application() {
|
||||||
lateinit var container: AppContainer
|
lateinit var container: AppContainer
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
var currentUser: User? = null
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
container = AppDataContainer(this)
|
container = AppDataContainer(this)
|
||||||
|
@ -1,15 +1,12 @@
|
|||||||
package com.zyzf.coffeepreorder
|
package com.zyzf.coffeepreorder
|
||||||
|
|
||||||
import android.content.res.Configuration
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import com.zyzf.coffeepreorder.ui.navigation.MainNavbar
|
import com.zyzf.coffeepreorder.ui.navigation.MainNavbar
|
||||||
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
|
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
|
||||||
|
|
||||||
|
103
app/src/main/java/com/zyzf/coffeepreorder/api/MyServerService.kt
Normal file
103
app/src/main/java/com/zyzf/coffeepreorder/api/MyServerService.kt
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
package com.zyzf.coffeepreorder.api
|
||||||
|
|
||||||
|
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
|
||||||
|
import com.zyzf.coffeepreorder.api.model.CoffeeRemote
|
||||||
|
import com.zyzf.coffeepreorder.api.model.UserRemote
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.logging.HttpLoggingInterceptor
|
||||||
|
import retrofit2.Retrofit
|
||||||
|
import retrofit2.http.Body
|
||||||
|
import retrofit2.http.DELETE
|
||||||
|
import retrofit2.http.GET
|
||||||
|
import retrofit2.http.POST
|
||||||
|
import retrofit2.http.PUT
|
||||||
|
import retrofit2.http.Path
|
||||||
|
import retrofit2.http.Query
|
||||||
|
|
||||||
|
interface MyServerService {
|
||||||
|
@GET("user/")
|
||||||
|
suspend fun getUsers(
|
||||||
|
@Query("pageNo") page: Int,
|
||||||
|
@Query("pageSize") limit: Int,
|
||||||
|
): List<UserRemote>
|
||||||
|
|
||||||
|
@GET("user/{id}")
|
||||||
|
suspend fun getUser(
|
||||||
|
@Path("id") id: Int,
|
||||||
|
): UserRemote
|
||||||
|
|
||||||
|
@GET("user/tryLogin")
|
||||||
|
suspend fun tryLogin(
|
||||||
|
@Query("login") login: String,
|
||||||
|
@Query("password") password: String
|
||||||
|
): UserRemote
|
||||||
|
|
||||||
|
@POST("user/")
|
||||||
|
suspend fun createUser(
|
||||||
|
@Body user: UserRemote,
|
||||||
|
): UserRemote
|
||||||
|
|
||||||
|
@PUT("user/{id}")
|
||||||
|
suspend fun updateUser(
|
||||||
|
@Path("id") id: Int,
|
||||||
|
@Body user: UserRemote,
|
||||||
|
): UserRemote
|
||||||
|
|
||||||
|
@DELETE("user/{id}")
|
||||||
|
suspend fun deleteUser(
|
||||||
|
@Path("id") id: Int,
|
||||||
|
): UserRemote
|
||||||
|
|
||||||
|
@GET("coffee/")
|
||||||
|
suspend fun getCoffees(
|
||||||
|
@Query("pageNo") page: Int,
|
||||||
|
@Query("pageSize") limit: Int,
|
||||||
|
): List<CoffeeRemote>
|
||||||
|
|
||||||
|
@GET("coffee/{id}")
|
||||||
|
suspend fun getCoffee(
|
||||||
|
@Path("id") id: Int,
|
||||||
|
): CoffeeRemote
|
||||||
|
|
||||||
|
@POST("coffee/")
|
||||||
|
suspend fun createCoffee(
|
||||||
|
@Body coffee: CoffeeRemote,
|
||||||
|
): CoffeeRemote
|
||||||
|
|
||||||
|
@PUT("coffee/{id}")
|
||||||
|
suspend fun updateCoffee(
|
||||||
|
@Path("id") id: Int,
|
||||||
|
@Body coffee: CoffeeRemote,
|
||||||
|
): CoffeeRemote
|
||||||
|
|
||||||
|
@DELETE("coffee/{id}")
|
||||||
|
suspend fun deleteCoffee(
|
||||||
|
@Path("id") id: Int,
|
||||||
|
): CoffeeRemote
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val BASE_URL = "http://192.168.0.100:8080/api/"
|
||||||
|
|
||||||
|
@Volatile
|
||||||
|
private var INSTANCE: MyServerService? = null
|
||||||
|
|
||||||
|
fun getInstance(): MyServerService {
|
||||||
|
return INSTANCE ?: synchronized(this) {
|
||||||
|
val logger = HttpLoggingInterceptor()
|
||||||
|
logger.level = HttpLoggingInterceptor.Level.BASIC
|
||||||
|
val client = OkHttpClient.Builder()
|
||||||
|
.addInterceptor(logger)
|
||||||
|
.build()
|
||||||
|
return Retrofit.Builder()
|
||||||
|
.baseUrl(BASE_URL)
|
||||||
|
.client(client)
|
||||||
|
.addConverterFactory(Json.asConverterFactory("application/json".toMediaType()))
|
||||||
|
.build()
|
||||||
|
.create(MyServerService::class.java)
|
||||||
|
.also { INSTANCE = it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,106 @@
|
|||||||
|
package com.zyzf.coffeepreorder.api.coffee
|
||||||
|
|
||||||
|
import androidx.paging.ExperimentalPagingApi
|
||||||
|
import androidx.paging.LoadType
|
||||||
|
import androidx.paging.PagingState
|
||||||
|
import androidx.paging.RemoteMediator
|
||||||
|
import androidx.room.withTransaction
|
||||||
|
import com.zyzf.coffeepreorder.api.MyServerService
|
||||||
|
import com.zyzf.coffeepreorder.api.model.toCoffee
|
||||||
|
import com.zyzf.coffeepreorder.database.AppDatabase
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Coffee
|
||||||
|
import com.zyzf.coffeepreorder.database.model.RemoteKeyType
|
||||||
|
import com.zyzf.coffeepreorder.database.model.RemoteKeys
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.OfflineCoffeeRepository
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.OfflineRemoteKeyRepository
|
||||||
|
import retrofit2.HttpException
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
@OptIn(ExperimentalPagingApi::class)
|
||||||
|
class CoffeeRemoteMediator(
|
||||||
|
private val service: MyServerService,
|
||||||
|
private val dbCoffeeRepository: OfflineCoffeeRepository,
|
||||||
|
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
|
||||||
|
private val database: AppDatabase
|
||||||
|
) : RemoteMediator<Int, Coffee>() {
|
||||||
|
|
||||||
|
override suspend fun initialize(): InitializeAction {
|
||||||
|
return InitializeAction.LAUNCH_INITIAL_REFRESH
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun load(
|
||||||
|
loadType: LoadType,
|
||||||
|
state: PagingState<Int, Coffee>
|
||||||
|
): MediatorResult {
|
||||||
|
val page = when (loadType) {
|
||||||
|
LoadType.REFRESH -> {
|
||||||
|
val remoteKeys = getRemoteKeyClosestToCurrentPosition(state)
|
||||||
|
remoteKeys?.nextKey?.minus(1) ?: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadType.PREPEND -> {
|
||||||
|
val remoteKeys = getRemoteKeyForFirstItem(state)
|
||||||
|
remoteKeys?.prevKey
|
||||||
|
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadType.APPEND -> {
|
||||||
|
val remoteKeys = getRemoteKeyForLastItem(state)
|
||||||
|
remoteKeys?.nextKey
|
||||||
|
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
val coffees = service.getCoffees(page, state.config.pageSize).map { it.toCoffee() }
|
||||||
|
val endOfPaginationReached = coffees.isEmpty()
|
||||||
|
database.withTransaction {
|
||||||
|
if (loadType == LoadType.REFRESH) {
|
||||||
|
dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.COFFEE)
|
||||||
|
dbCoffeeRepository.clearCoffees()
|
||||||
|
}
|
||||||
|
val prevKey = if (page == 1) null else page - 1
|
||||||
|
val nextKey = if (endOfPaginationReached) null else page + 1
|
||||||
|
val keys = coffees.map {
|
||||||
|
RemoteKeys(
|
||||||
|
entityId = it.uid,
|
||||||
|
type = RemoteKeyType.COFFEE,
|
||||||
|
prevKey = prevKey,
|
||||||
|
nextKey = nextKey
|
||||||
|
)
|
||||||
|
}
|
||||||
|
dbRemoteKeyRepository.createRemoteKeys(keys)
|
||||||
|
dbCoffeeRepository.insertCoffees(coffees)
|
||||||
|
}
|
||||||
|
return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached)
|
||||||
|
} catch (exception: IOException) {
|
||||||
|
return MediatorResult.Error(exception)
|
||||||
|
} catch (exception: HttpException) {
|
||||||
|
return MediatorResult.Error(exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, Coffee>): RemoteKeys? {
|
||||||
|
return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
|
||||||
|
?.let { coffee ->
|
||||||
|
dbRemoteKeyRepository.getAllRemoteKeys(coffee.uid, RemoteKeyType.COFFEE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, Coffee>): RemoteKeys? {
|
||||||
|
return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
|
||||||
|
?.let { coffee ->
|
||||||
|
dbRemoteKeyRepository.getAllRemoteKeys(coffee.uid, RemoteKeyType.COFFEE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getRemoteKeyClosestToCurrentPosition(
|
||||||
|
state: PagingState<Int, Coffee>
|
||||||
|
): RemoteKeys? {
|
||||||
|
return state.anchorPosition?.let { position ->
|
||||||
|
state.closestItemToPosition(position)?.uid?.let { coffeeUid ->
|
||||||
|
dbRemoteKeyRepository.getAllRemoteKeys(coffeeUid, RemoteKeyType.COFFEE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
package com.zyzf.coffeepreorder.api.coffee
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.paging.ExperimentalPagingApi
|
||||||
|
import androidx.paging.Pager
|
||||||
|
import androidx.paging.PagingConfig
|
||||||
|
import androidx.paging.PagingData
|
||||||
|
import com.zyzf.coffeepreorder.api.MyServerService
|
||||||
|
import com.zyzf.coffeepreorder.api.model.toCoffee
|
||||||
|
import com.zyzf.coffeepreorder.api.model.toCoffeeRemote
|
||||||
|
import com.zyzf.coffeepreorder.database.AppContainer
|
||||||
|
import com.zyzf.coffeepreorder.database.AppDatabase
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Coffee
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.CoffeeRepository
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.OfflineCoffeeRepository
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.OfflineRemoteKeyRepository
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
class RestCoffeeRepository(
|
||||||
|
private val service: MyServerService,
|
||||||
|
private val dbCoffeeRepository: OfflineCoffeeRepository,
|
||||||
|
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
|
||||||
|
private val database: AppDatabase
|
||||||
|
) : CoffeeRepository {
|
||||||
|
override fun getAllCoffees(): Flow<PagingData<Coffee>> {
|
||||||
|
Log.d(RestCoffeeRepository::class.simpleName, "Get coffees")
|
||||||
|
|
||||||
|
val pagingSourceFactory = { dbCoffeeRepository.getAllCoffeesPagingSource() }
|
||||||
|
|
||||||
|
@OptIn(ExperimentalPagingApi::class)
|
||||||
|
return Pager(
|
||||||
|
config = PagingConfig(
|
||||||
|
pageSize = AppContainer.LIMIT,
|
||||||
|
enablePlaceholders = false
|
||||||
|
),
|
||||||
|
remoteMediator = CoffeeRemoteMediator(
|
||||||
|
service,
|
||||||
|
dbCoffeeRepository,
|
||||||
|
dbRemoteKeyRepository,
|
||||||
|
database,
|
||||||
|
),
|
||||||
|
pagingSourceFactory = pagingSourceFactory
|
||||||
|
).flow
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getByUid(uid: Int): Coffee =
|
||||||
|
service.getCoffee(uid).toCoffee()
|
||||||
|
|
||||||
|
override suspend fun insert(coffee: Coffee): Long {
|
||||||
|
return service.createCoffee(coffee.toCoffeeRemote()).toCoffee().uid.toLong()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun update(coffee: Coffee): Int {
|
||||||
|
return service.updateCoffee(coffee.uid, coffee.toCoffeeRemote()).toCoffee().uid
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun delete(coffee: Coffee) {
|
||||||
|
service.deleteCoffee(coffee.uid).toCoffee()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.zyzf.coffeepreorder.api.model
|
||||||
|
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Coffee
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CoffeeRemote(
|
||||||
|
val id: Int = 0,
|
||||||
|
val name: String = "",
|
||||||
|
val cost: Double = 0.0,
|
||||||
|
val ingredients: String = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
fun CoffeeRemote.toCoffee(): Coffee = Coffee(
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
cost,
|
||||||
|
ingredients
|
||||||
|
)
|
||||||
|
|
||||||
|
fun Coffee.toCoffeeRemote(): CoffeeRemote = CoffeeRemote(
|
||||||
|
uid,
|
||||||
|
name,
|
||||||
|
cost,
|
||||||
|
ingredients
|
||||||
|
)
|
@ -0,0 +1,32 @@
|
|||||||
|
package com.zyzf.coffeepreorder.api.model
|
||||||
|
|
||||||
|
import com.zyzf.coffeepreorder.database.model.User
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class UserRemote(
|
||||||
|
val id: Int = 0,
|
||||||
|
val login: String = "",
|
||||||
|
val fio: String = "",
|
||||||
|
val phone: String = "",
|
||||||
|
val password: String = "",
|
||||||
|
val role: String = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
fun UserRemote.toUser(): User = User(
|
||||||
|
id,
|
||||||
|
login,
|
||||||
|
fio,
|
||||||
|
phone,
|
||||||
|
password,
|
||||||
|
role
|
||||||
|
)
|
||||||
|
|
||||||
|
fun User.toUserRemote(): UserRemote = UserRemote(
|
||||||
|
uid,
|
||||||
|
login,
|
||||||
|
fio,
|
||||||
|
phone,
|
||||||
|
password,
|
||||||
|
role
|
||||||
|
)
|
@ -0,0 +1,64 @@
|
|||||||
|
package com.zyzf.coffeepreorder.api.user
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.paging.ExperimentalPagingApi
|
||||||
|
import androidx.paging.Pager
|
||||||
|
import androidx.paging.PagingConfig
|
||||||
|
import androidx.paging.PagingData
|
||||||
|
import com.zyzf.coffeepreorder.api.MyServerService
|
||||||
|
import com.zyzf.coffeepreorder.api.model.toUser
|
||||||
|
import com.zyzf.coffeepreorder.api.model.toUserRemote
|
||||||
|
import com.zyzf.coffeepreorder.database.AppContainer
|
||||||
|
import com.zyzf.coffeepreorder.database.AppDatabase
|
||||||
|
import com.zyzf.coffeepreorder.database.model.User
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.OfflineRemoteKeyRepository
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.OfflineUserRepository
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.UserRepository
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
class RestUserRepository(
|
||||||
|
private val service: MyServerService,
|
||||||
|
private val dbUserRepository: OfflineUserRepository,
|
||||||
|
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
|
||||||
|
private val database: AppDatabase
|
||||||
|
) : UserRepository {
|
||||||
|
override fun getAll(): Flow<PagingData<User>> {
|
||||||
|
Log.d(RestUserRepository::class.simpleName, "Get users")
|
||||||
|
|
||||||
|
val pagingSourceFactory = { dbUserRepository.getAllUserPagingSource() }
|
||||||
|
|
||||||
|
@OptIn(ExperimentalPagingApi::class)
|
||||||
|
return Pager(
|
||||||
|
config = PagingConfig(
|
||||||
|
pageSize = AppContainer.LIMIT,
|
||||||
|
enablePlaceholders = false
|
||||||
|
),
|
||||||
|
remoteMediator = UserRemoteMediator(
|
||||||
|
service,
|
||||||
|
dbUserRepository,
|
||||||
|
dbRemoteKeyRepository,
|
||||||
|
database,
|
||||||
|
),
|
||||||
|
pagingSourceFactory = pagingSourceFactory
|
||||||
|
).flow
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getByUid(uid: Int): User =
|
||||||
|
service.getUser(uid).toUser()!!
|
||||||
|
|
||||||
|
override suspend fun tryLogin(login: String, password: String): User? =
|
||||||
|
service.tryLogin(login, password).toUser()
|
||||||
|
|
||||||
|
|
||||||
|
override suspend fun insert(user: User): Long {
|
||||||
|
return service.createUser(user.toUserRemote()).toUser()?.uid?.toLong()!!
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun update(user: User): Int {
|
||||||
|
return service.updateUser(user.uid, user.toUserRemote()).toUser()?.uid!!
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun delete(user: User) {
|
||||||
|
service.deleteUser(user.uid).toUser()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,106 @@
|
|||||||
|
package com.zyzf.coffeepreorder.api.user
|
||||||
|
|
||||||
|
import androidx.paging.ExperimentalPagingApi
|
||||||
|
import androidx.paging.LoadType
|
||||||
|
import androidx.paging.PagingState
|
||||||
|
import androidx.paging.RemoteMediator
|
||||||
|
import androidx.room.withTransaction
|
||||||
|
import com.zyzf.coffeepreorder.api.MyServerService
|
||||||
|
import com.zyzf.coffeepreorder.api.model.toUser
|
||||||
|
import com.zyzf.coffeepreorder.database.AppDatabase
|
||||||
|
import com.zyzf.coffeepreorder.database.model.RemoteKeyType
|
||||||
|
import com.zyzf.coffeepreorder.database.model.RemoteKeys
|
||||||
|
import com.zyzf.coffeepreorder.database.model.User
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.OfflineRemoteKeyRepository
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.OfflineUserRepository
|
||||||
|
import retrofit2.HttpException
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
@OptIn(ExperimentalPagingApi::class)
|
||||||
|
class UserRemoteMediator(
|
||||||
|
private val service: MyServerService,
|
||||||
|
private val dbUserRepository: OfflineUserRepository,
|
||||||
|
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
|
||||||
|
private val database: AppDatabase
|
||||||
|
) : RemoteMediator<Int, User>() {
|
||||||
|
|
||||||
|
override suspend fun initialize(): InitializeAction {
|
||||||
|
return InitializeAction.LAUNCH_INITIAL_REFRESH
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun load(
|
||||||
|
loadType: LoadType,
|
||||||
|
state: PagingState<Int, User>
|
||||||
|
): MediatorResult {
|
||||||
|
val page = when (loadType) {
|
||||||
|
LoadType.REFRESH -> {
|
||||||
|
val remoteKeys = getRemoteKeyClosestToCurrentPosition(state)
|
||||||
|
remoteKeys?.nextKey?.minus(1) ?: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadType.PREPEND -> {
|
||||||
|
val remoteKeys = getRemoteKeyForFirstItem(state)
|
||||||
|
remoteKeys?.prevKey
|
||||||
|
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadType.APPEND -> {
|
||||||
|
val remoteKeys = getRemoteKeyForLastItem(state)
|
||||||
|
remoteKeys?.nextKey
|
||||||
|
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
val users = service.getUsers(page, state.config.pageSize).map { it.toUser() }
|
||||||
|
val endOfPaginationReached = users.isEmpty()
|
||||||
|
database.withTransaction {
|
||||||
|
if (loadType == LoadType.REFRESH) {
|
||||||
|
dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.USER)
|
||||||
|
dbUserRepository.clearUsers()
|
||||||
|
}
|
||||||
|
val prevKey = if (page == 1) null else page - 1
|
||||||
|
val nextKey = if (endOfPaginationReached) null else page + 1
|
||||||
|
val keys = users.map {
|
||||||
|
RemoteKeys(
|
||||||
|
entityId = it.uid,
|
||||||
|
type = RemoteKeyType.USER,
|
||||||
|
prevKey = prevKey,
|
||||||
|
nextKey = nextKey
|
||||||
|
)
|
||||||
|
}
|
||||||
|
dbRemoteKeyRepository.createRemoteKeys(keys)
|
||||||
|
dbUserRepository.insertUsers(users)
|
||||||
|
}
|
||||||
|
return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached)
|
||||||
|
} catch (exception: IOException) {
|
||||||
|
return MediatorResult.Error(exception)
|
||||||
|
} catch (exception: HttpException) {
|
||||||
|
return MediatorResult.Error(exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, User>): RemoteKeys? {
|
||||||
|
return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
|
||||||
|
?.let { user ->
|
||||||
|
dbRemoteKeyRepository.getAllRemoteKeys(user.uid, RemoteKeyType.USER)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, User>): RemoteKeys? {
|
||||||
|
return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
|
||||||
|
?.let { user ->
|
||||||
|
dbRemoteKeyRepository.getAllRemoteKeys(user.uid, RemoteKeyType.USER)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getRemoteKeyClosestToCurrentPosition(
|
||||||
|
state: PagingState<Int, User>
|
||||||
|
): RemoteKeys? {
|
||||||
|
return state.anchorPosition?.let { position ->
|
||||||
|
state.closestItemToPosition(position)?.uid?.let { userUid ->
|
||||||
|
dbRemoteKeyRepository.getAllRemoteKeys(userUid, RemoteKeyType.USER)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,32 +1,52 @@
|
|||||||
package com.zyzf.coffeepreorder.database
|
package com.zyzf.coffeepreorder.database
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import com.zyzf.coffeepreorder.api.MyServerService
|
||||||
|
import com.zyzf.coffeepreorder.api.coffee.RestCoffeeRepository
|
||||||
|
import com.zyzf.coffeepreorder.api.user.RestUserRepository
|
||||||
import com.zyzf.coffeepreorder.database.repository.CartRepository
|
import com.zyzf.coffeepreorder.database.repository.CartRepository
|
||||||
import com.zyzf.coffeepreorder.database.repository.CoffeeRepository
|
|
||||||
import com.zyzf.coffeepreorder.database.repository.OfflineCartRepository
|
import com.zyzf.coffeepreorder.database.repository.OfflineCartRepository
|
||||||
import com.zyzf.coffeepreorder.database.repository.OfflineCoffeeRepository
|
import com.zyzf.coffeepreorder.database.repository.OfflineCoffeeRepository
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.OfflineRemoteKeyRepository
|
||||||
import com.zyzf.coffeepreorder.database.repository.OfflineUserRepository
|
import com.zyzf.coffeepreorder.database.repository.OfflineUserRepository
|
||||||
import com.zyzf.coffeepreorder.database.repository.UserRepository
|
|
||||||
|
|
||||||
interface AppContainer {
|
interface AppContainer {
|
||||||
val coffeeRepository: CoffeeRepository
|
|
||||||
val userRepository: UserRepository
|
|
||||||
val cartRepository: CartRepository
|
val cartRepository: CartRepository
|
||||||
|
val coffeeRestRepository: RestCoffeeRepository
|
||||||
|
val userRestRepository: RestUserRepository
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val LIMIT = 10
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AppDataContainer(private val context: Context) : AppContainer {
|
class AppDataContainer(private val context: Context) : AppContainer {
|
||||||
override val coffeeRepository: CoffeeRepository by lazy {
|
private val coffeeRepository: OfflineCoffeeRepository by lazy {
|
||||||
OfflineCoffeeRepository(AppDatabase.getInstance(context).coffeeDao())
|
OfflineCoffeeRepository(AppDatabase.getInstance(context).coffeeDao())
|
||||||
}
|
}
|
||||||
override val userRepository: UserRepository by lazy {
|
private val userRepository: OfflineUserRepository by lazy {
|
||||||
OfflineUserRepository(AppDatabase.getInstance(context).userDao())
|
OfflineUserRepository(AppDatabase.getInstance(context).userDao())
|
||||||
}
|
}
|
||||||
override val cartRepository: CartRepository by lazy {
|
override val cartRepository: CartRepository by lazy {
|
||||||
OfflineCartRepository(AppDatabase.getInstance(context).cartDao())
|
OfflineCartRepository(AppDatabase.getInstance(context).cartDao())
|
||||||
}
|
}
|
||||||
|
private val remoteKeyRepository: OfflineRemoteKeyRepository by lazy {
|
||||||
companion object {
|
OfflineRemoteKeyRepository(AppDatabase.getInstance(context).remoteKeysDao())
|
||||||
const val TIMEOUT = 5000L
|
}
|
||||||
const val LIMIT = 10
|
override val coffeeRestRepository: RestCoffeeRepository by lazy {
|
||||||
|
RestCoffeeRepository(
|
||||||
|
MyServerService.getInstance(),
|
||||||
|
coffeeRepository,
|
||||||
|
remoteKeyRepository,
|
||||||
|
AppDatabase.getInstance(context)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
override val userRestRepository: RestUserRepository by lazy {
|
||||||
|
RestUserRepository(
|
||||||
|
MyServerService.getInstance(),
|
||||||
|
userRepository,
|
||||||
|
remoteKeyRepository,
|
||||||
|
AppDatabase.getInstance(context)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,23 +4,21 @@ import android.content.Context
|
|||||||
import androidx.room.Database
|
import androidx.room.Database
|
||||||
import androidx.room.Room
|
import androidx.room.Room
|
||||||
import androidx.room.RoomDatabase
|
import androidx.room.RoomDatabase
|
||||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
|
||||||
import com.zyzf.coffeepreorder.database.dao.CartDao
|
import com.zyzf.coffeepreorder.database.dao.CartDao
|
||||||
import com.zyzf.coffeepreorder.database.dao.CoffeeDao
|
import com.zyzf.coffeepreorder.database.dao.CoffeeDao
|
||||||
|
import com.zyzf.coffeepreorder.database.dao.RemoteKeysDao
|
||||||
import com.zyzf.coffeepreorder.database.dao.UserDao
|
import com.zyzf.coffeepreorder.database.dao.UserDao
|
||||||
import com.zyzf.coffeepreorder.database.model.Cart
|
import com.zyzf.coffeepreorder.database.model.Cart
|
||||||
import com.zyzf.coffeepreorder.database.model.Coffee
|
import com.zyzf.coffeepreorder.database.model.Coffee
|
||||||
|
import com.zyzf.coffeepreorder.database.model.RemoteKeys
|
||||||
import com.zyzf.coffeepreorder.database.model.User
|
import com.zyzf.coffeepreorder.database.model.User
|
||||||
import com.zyzf.coffeepreorder.database.model.UserLogined
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
@Database(entities = [User::class, Coffee::class, Cart::class, UserLogined::class], version = 1, exportSchema = false)
|
@Database(entities = [User::class, Coffee::class, Cart::class, RemoteKeys::class], version = 1, exportSchema = false)
|
||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
abstract fun userDao(): UserDao
|
abstract fun userDao(): UserDao
|
||||||
abstract fun coffeeDao(): CoffeeDao
|
abstract fun coffeeDao(): CoffeeDao
|
||||||
abstract fun cartDao(): CartDao
|
abstract fun cartDao(): CartDao
|
||||||
|
abstract fun remoteKeysDao(): RemoteKeysDao
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val DB_NAME: String = "coffee-preorder"
|
private const val DB_NAME: String = "coffee-preorder"
|
||||||
@ -36,31 +34,31 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
userDao.insert(user1)
|
userDao.insert(user1)
|
||||||
// Coffees
|
// Coffees
|
||||||
val coffeeDao = database.coffeeDao()
|
val coffeeDao = database.coffeeDao()
|
||||||
val coffee1 = Coffee("Coffee1", 200.0, "Ing1", null, 0)
|
val coffee1 = Coffee("Coffee1", 200.0, "Ing1")
|
||||||
val coffee2 = Coffee("Coffee2", 200.0, "Ing1", null, 0)
|
val coffee2 = Coffee("Coffee2", 200.0, "Ing1")
|
||||||
val coffee3 = Coffee("Coffee3", 300.0, "Ing1", null, 0)
|
val coffee3 = Coffee("Coffee3", 300.0, "Ing1")
|
||||||
val coffee4 = Coffee("Coffee4", 200.0, "Ing1", null, 0)
|
val coffee4 = Coffee("Coffee4", 200.0, "Ing1")
|
||||||
val coffee5 = Coffee("Coffee5", 200.0, "Ing1", null, 0)
|
val coffee5 = Coffee("Coffee5", 200.0, "Ing1")
|
||||||
val coffee6 = Coffee("Coffee6", 25.0, "Ing1", null, 0)
|
val coffee6 = Coffee("Coffee6", 25.0, "Ing1")
|
||||||
val coffee7 = Coffee("Coffee7", 400.0, "Ing1", null, 0)
|
val coffee7 = Coffee("Coffee7", 400.0, "Ing1")
|
||||||
val coffee8 = Coffee("Coffee8", 200.0, "Ing1", null, 0)
|
val coffee8 = Coffee("Coffee8", 200.0, "Ing1")
|
||||||
val coffee9 = Coffee("Coffee9", 200.0, "Ing1", null, 0)
|
val coffee9 = Coffee("Coffee9", 200.0, "Ing1")
|
||||||
val coffee10 = Coffee("Coffee10", 900.0, "Ing1", null, 0)
|
val coffee10 = Coffee("Coffee10", 900.0, "Ing1")
|
||||||
val coffee11 = Coffee("Coffee11", 200.0, "Ing1", null, 0)
|
val coffee11 = Coffee("Coffee11", 200.0, "Ing1")
|
||||||
coffeeDao.insert(coffee1.name, coffee1.cost, coffee1.ingredients)
|
coffeeDao.insert(coffee1)
|
||||||
coffeeDao.insert(coffee2.name, coffee2.cost, coffee2.ingredients)
|
coffeeDao.insert(coffee2)
|
||||||
coffeeDao.insert(coffee3.name, coffee3.cost, coffee3.ingredients)
|
coffeeDao.insert(coffee3)
|
||||||
coffeeDao.insert(coffee4.name, coffee4.cost, coffee4.ingredients)
|
coffeeDao.insert(coffee4)
|
||||||
coffeeDao.insert(coffee5.name, coffee5.cost, coffee5.ingredients)
|
coffeeDao.insert(coffee5)
|
||||||
coffeeDao.insert(coffee6.name, coffee6.cost, coffee6.ingredients)
|
coffeeDao.insert(coffee6)
|
||||||
coffeeDao.insert(coffee7.name, coffee7.cost, coffee7.ingredients)
|
coffeeDao.insert(coffee7)
|
||||||
coffeeDao.insert(coffee8.name, coffee8.cost, coffee8.ingredients)
|
coffeeDao.insert(coffee8)
|
||||||
coffeeDao.insert(coffee9.name, coffee9.cost, coffee9.ingredients)
|
coffeeDao.insert(coffee9)
|
||||||
coffeeDao.insert(coffee10.name, coffee10.cost, coffee10.ingredients)
|
coffeeDao.insert(coffee10)
|
||||||
coffeeDao.insert(coffee11.name, coffee11.cost, coffee11.ingredients)
|
coffeeDao.insert(coffee11)
|
||||||
// Cart
|
// Cart
|
||||||
val cartDao = database.cartDao()
|
val cartDao = database.cartDao()
|
||||||
val cart = Cart()
|
val cart = Cart(2, 1)
|
||||||
cartDao.insert(cart)
|
cartDao.insert(cart)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,14 +70,15 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
AppDatabase::class.java,
|
AppDatabase::class.java,
|
||||||
DB_NAME
|
DB_NAME
|
||||||
)
|
)
|
||||||
.addCallback(object : Callback() {
|
// .addCallback(object : Callback() {
|
||||||
override fun onCreate(db: SupportSQLiteDatabase) {
|
// override fun onCreate(db: SupportSQLiteDatabase) {
|
||||||
super.onCreate(db)
|
// super.onCreate(db)
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
// CoroutineScope(Dispatchers.IO).launch {
|
||||||
populateDatabase()
|
// populateDatabase()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
|
.allowMainThreadQueries()
|
||||||
.build()
|
.build()
|
||||||
.also { INSTANCE = it }
|
.also { INSTANCE = it }
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,48 @@
|
|||||||
package com.zyzf.coffeepreorder.database.dao
|
package com.zyzf.coffeepreorder.database.dao
|
||||||
|
|
||||||
|
import androidx.paging.PagingSource
|
||||||
import androidx.room.Dao
|
import androidx.room.Dao
|
||||||
import androidx.room.Insert
|
import androidx.room.Insert
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
|
import androidx.room.Transaction
|
||||||
import androidx.room.Update
|
import androidx.room.Update
|
||||||
import com.zyzf.coffeepreorder.database.model.Cart
|
import com.zyzf.coffeepreorder.database.model.Cart
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Coffee
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface CartDao {
|
interface CartDao {
|
||||||
@Query("select * from cart limit 1")
|
@Query("select * from cart")
|
||||||
suspend fun get(): Cart
|
suspend fun getAll(): Cart
|
||||||
|
|
||||||
|
@Query("select coffee.uid, coffee.name, coffee.cost, coffee.ingredients from cart join coffee on coffee.uid = cart.coffee_id and cart.count > 0 collate nocase")
|
||||||
|
fun getAllInCart(): PagingSource<Int, Coffee>
|
||||||
|
|
||||||
|
@Query("select sum(coffee.cost * cart.count) from cart JOIN coffee on coffee.uid = cart.coffee_id and cart.count > 0")
|
||||||
|
fun getSumInCart(): Double
|
||||||
|
|
||||||
|
@Query("select cart.count from cart JOIN coffee on coffee.uid = cart.coffee_id where coffee.uid = :coffeeId")
|
||||||
|
fun getCountForCoffee(coffeeId: Int): Double
|
||||||
|
|
||||||
@Insert
|
@Insert
|
||||||
suspend fun insert(cart: Cart)
|
suspend fun insert(cart: Cart)
|
||||||
|
|
||||||
@Query("update coffee set cart_id = :cartId, count = count + :count where uid = :coffeeId")
|
@Query("select * from cart where coffee_id = :coffeeId limit 1")
|
||||||
suspend fun insertCoffee(cartId: Int, coffeeId: Int, count: Int)
|
suspend fun findCoffee(coffeeId: Int): Cart?
|
||||||
|
@Query("insert into cart (coffee_id, count) values (:coffeeId, :count)")
|
||||||
|
suspend fun insertNewCoffee(coffeeId: Int, count: Int)
|
||||||
|
@Query("update cart set count = count + :count where coffee_id = :coffeeId")
|
||||||
|
suspend fun updateExistingCoffee(coffeeId: Int, count: Int)
|
||||||
|
@Transaction
|
||||||
|
suspend fun insertCoffee(coffeeId: Int, count: Int) {
|
||||||
|
val cart: Cart? = findCoffee(coffeeId)
|
||||||
|
if (cart != null) {
|
||||||
|
updateExistingCoffee(coffeeId, count)
|
||||||
|
} else {
|
||||||
|
insertNewCoffee(coffeeId, count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Query("update coffee set count = count - :count where uid = :coffeeId")
|
@Query("update cart set count = count - :count where coffee_id = :coffeeId")
|
||||||
suspend fun deleteCoffee(coffeeId: Int, count: Int)
|
suspend fun deleteCoffee(coffeeId: Int, count: Int)
|
||||||
|
|
||||||
@Update
|
@Update
|
||||||
|
@ -3,30 +3,31 @@ package com.zyzf.coffeepreorder.database.dao
|
|||||||
import androidx.paging.PagingSource
|
import androidx.paging.PagingSource
|
||||||
import androidx.room.Dao
|
import androidx.room.Dao
|
||||||
import androidx.room.Delete
|
import androidx.room.Delete
|
||||||
|
import androidx.room.Insert
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
|
import androidx.room.Update
|
||||||
import com.zyzf.coffeepreorder.database.model.Coffee
|
import com.zyzf.coffeepreorder.database.model.Coffee
|
||||||
import com.zyzf.coffeepreorder.database.model.CoffeeWithCart
|
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface CoffeeDao {
|
interface CoffeeDao {
|
||||||
@Query("select * from coffee order by name collate nocase asc")
|
@Query("select * from coffee order by name collate nocase asc")
|
||||||
fun getAll(): PagingSource<Int, Coffee>
|
fun getAllCoffees(): PagingSource<Int, Coffee>
|
||||||
|
|
||||||
@Query("select * from coffee where cart_id is not null and count > 0 order by name collate nocase asc")
|
@Query("select coffee.uid, name, cost, ingredients from coffee where coffee.uid = :uid")
|
||||||
fun getAllInCart(): PagingSource<Int, Coffee>
|
suspend fun getByUid(uid: Int): Coffee?
|
||||||
|
|
||||||
@Query("select sum(cost) from coffee where cart_id is not null and count > 0")
|
@Insert
|
||||||
fun getSumInCart(): Double
|
fun insert(coffee: Coffee): Long
|
||||||
|
|
||||||
@Query("select coffee.uid, name, cost, ingredients, cart_id, count, cart.uid as cart_uid from coffee left join cart on coffee.cart_id = cart.uid where coffee.uid = :uid")
|
@Insert
|
||||||
suspend fun getByUid(uid: Int): CoffeeWithCart?
|
suspend fun insert(vararg coffee: Coffee)
|
||||||
|
|
||||||
@Query("insert into coffee (name, cost, ingredients, count) values (:name, :cost, :ingredients, 0)")
|
@Update
|
||||||
suspend fun insert(name: String, cost: Double, ingredients: String): Long
|
fun update(coffee: Coffee): Int
|
||||||
|
|
||||||
@Query("update coffee set name = :name, cost = :cost, ingredients = :ingredients, cart_id = :cartId, count = :count where uid = :uid")
|
|
||||||
suspend fun update(uid: Int, name: String, cost: Double, ingredients: String, cartId: Int?, count: Int): Int?
|
|
||||||
|
|
||||||
@Delete
|
@Delete
|
||||||
suspend fun delete(coffee: Coffee)
|
suspend fun delete(coffee: Coffee)
|
||||||
|
|
||||||
|
@Query("delete from coffee")
|
||||||
|
suspend fun deleteAll()
|
||||||
}
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package com.zyzf.coffeepreorder.database.dao
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy
|
||||||
|
import androidx.room.Query
|
||||||
|
import com.zyzf.coffeepreorder.database.model.RemoteKeyType
|
||||||
|
import com.zyzf.coffeepreorder.database.model.RemoteKeys
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface RemoteKeysDao {
|
||||||
|
@Query("SELECT * FROM remote_keys WHERE entityId = :entityId AND type = :type")
|
||||||
|
suspend fun getRemoteKeys(entityId: Int, type: RemoteKeyType): RemoteKeys?
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
suspend fun insertAll(remoteKey: List<RemoteKeys>)
|
||||||
|
|
||||||
|
@Query("DELETE FROM remote_keys WHERE type = :type")
|
||||||
|
suspend fun clearRemoteKeys(type: RemoteKeyType)
|
||||||
|
}
|
@ -1,15 +1,17 @@
|
|||||||
package com.zyzf.coffeepreorder.database.dao
|
package com.zyzf.coffeepreorder.database.dao
|
||||||
|
|
||||||
|
import androidx.paging.PagingSource
|
||||||
import androidx.room.Dao
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Delete
|
||||||
import androidx.room.Insert
|
import androidx.room.Insert
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
|
import androidx.room.Update
|
||||||
import com.zyzf.coffeepreorder.database.model.User
|
import com.zyzf.coffeepreorder.database.model.User
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface UserDao {
|
interface UserDao {
|
||||||
@Query("select * from user")
|
@Query("select * from user")
|
||||||
fun getAll(): Flow<List<User>>
|
fun getAll(): PagingSource<Int, User>
|
||||||
|
|
||||||
@Query("select * from user where login = :login and password = :password")
|
@Query("select * from user where login = :login and password = :password")
|
||||||
suspend fun tryLogin(login: String, password: String): User?
|
suspend fun tryLogin(login: String, password: String): User?
|
||||||
@ -17,21 +19,18 @@ interface UserDao {
|
|||||||
@Query("select * from user where uid = :uid")
|
@Query("select * from user where uid = :uid")
|
||||||
suspend fun getByUid(uid: Int): User?
|
suspend fun getByUid(uid: Int): User?
|
||||||
|
|
||||||
@Query("select user.uid, login, fio, phone, password, role from user join user_logined on user_logined.user_id = user.uid limit 1")
|
@Insert
|
||||||
suspend fun getLogined(): User?
|
fun insert(user: User): Long
|
||||||
|
|
||||||
@Query("insert into user_logined (user_id) values (:userId)")
|
|
||||||
suspend fun setLogined(userId: Int)
|
|
||||||
|
|
||||||
@Query("delete from user_logined")
|
|
||||||
suspend fun logout()
|
|
||||||
|
|
||||||
@Insert
|
@Insert
|
||||||
suspend fun insert(user: User)
|
fun insert(vararg user: User)
|
||||||
|
|
||||||
@Query("update user set login = :login, fio = :fio, phone = :phone, password = :password where uid = :uid")
|
@Update
|
||||||
suspend fun update(uid: Int, login: String, fio: String, phone: String, password: String) : Int?
|
fun update(user: User): Int?
|
||||||
|
|
||||||
@Query("delete from user where uid = :uid")
|
@Delete
|
||||||
suspend fun delete(uid: Int)
|
suspend fun delete(user: User)
|
||||||
|
|
||||||
|
@Query("delete from user")
|
||||||
|
suspend fun deleteAll()
|
||||||
}
|
}
|
@ -1,25 +1,47 @@
|
|||||||
package com.zyzf.coffeepreorder.database.model
|
package com.zyzf.coffeepreorder.database.model
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
|
import androidx.room.ForeignKey
|
||||||
import androidx.room.Ignore
|
import androidx.room.Ignore
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
@Entity(tableName = "cart")
|
@Entity(tableName = "cart", foreignKeys = [
|
||||||
|
ForeignKey(
|
||||||
|
entity = Cart::class,
|
||||||
|
parentColumns = ["uid"],
|
||||||
|
childColumns = ["coffee_id"],
|
||||||
|
onDelete = ForeignKey.RESTRICT,
|
||||||
|
onUpdate = ForeignKey.RESTRICT
|
||||||
|
)
|
||||||
|
])
|
||||||
data class Cart(
|
data class Cart(
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
val uid: Int?,
|
val uid: Int = 0,
|
||||||
|
@ColumnInfo(name = "coffee_id", index = true)
|
||||||
|
val coffeeId: Int,
|
||||||
|
@ColumnInfo(name = "count", index = true)
|
||||||
|
val count: Int = 0
|
||||||
) {
|
) {
|
||||||
@Ignore
|
@Ignore
|
||||||
constructor() : this(null)
|
constructor(
|
||||||
|
coffeeId: Int,
|
||||||
|
count: Int
|
||||||
|
) : this(0, coffeeId, count)
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
if (javaClass != other?.javaClass) return false
|
if (javaClass != other?.javaClass) return false
|
||||||
other as Cart
|
other as Cart
|
||||||
return uid == other.uid
|
if (uid != other.uid) return false
|
||||||
|
if (coffeeId != other.coffeeId) return false
|
||||||
|
return count == other.count
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
return uid ?: -1
|
var result = uid
|
||||||
|
result = 31 * result + coffeeId.hashCode()
|
||||||
|
result = 31 * result + count.hashCode()
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,19 +2,10 @@ package com.zyzf.coffeepreorder.database.model
|
|||||||
|
|
||||||
import androidx.room.ColumnInfo
|
import androidx.room.ColumnInfo
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.ForeignKey
|
|
||||||
import androidx.room.Ignore
|
import androidx.room.Ignore
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
@Entity(tableName = "coffee", foreignKeys = [
|
@Entity(tableName = "coffee")
|
||||||
ForeignKey(
|
|
||||||
entity = Cart::class,
|
|
||||||
parentColumns = ["uid"],
|
|
||||||
childColumns = ["cart_id"],
|
|
||||||
onDelete = ForeignKey.RESTRICT,
|
|
||||||
onUpdate = ForeignKey.RESTRICT
|
|
||||||
)
|
|
||||||
])
|
|
||||||
data class Coffee(
|
data class Coffee(
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
val uid: Int = 0,
|
val uid: Int = 0,
|
||||||
@ -23,20 +14,14 @@ data class Coffee(
|
|||||||
@ColumnInfo(name = "cost")
|
@ColumnInfo(name = "cost")
|
||||||
var cost: Double,
|
var cost: Double,
|
||||||
@ColumnInfo(name = "ingredients")
|
@ColumnInfo(name = "ingredients")
|
||||||
var ingredients: String,
|
var ingredients: String
|
||||||
@ColumnInfo(name = "cart_id", index = true)
|
|
||||||
val cartId: Int?,
|
|
||||||
@ColumnInfo(name = "count")
|
|
||||||
val count: Int = 0
|
|
||||||
) {
|
) {
|
||||||
@Ignore
|
@Ignore
|
||||||
constructor(
|
constructor(
|
||||||
name: String,
|
name: String,
|
||||||
cost: Double,
|
cost: Double,
|
||||||
ingredients: String,
|
ingredients: String
|
||||||
cartId: Int?,
|
) : this(0, name, cost, ingredients)
|
||||||
count: Int?
|
|
||||||
) : this(0, name, cost, ingredients, null, 0)
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun getCoffee(index: Int = 0): Coffee {
|
fun getCoffee(index: Int = 0): Coffee {
|
||||||
@ -44,9 +29,7 @@ data class Coffee(
|
|||||||
index,
|
index,
|
||||||
"",
|
"",
|
||||||
0.0,
|
0.0,
|
||||||
"",
|
""
|
||||||
null,
|
|
||||||
0
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,10 +38,17 @@ data class Coffee(
|
|||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
if (javaClass != other?.javaClass) return false
|
if (javaClass != other?.javaClass) return false
|
||||||
other as Coffee
|
other as Coffee
|
||||||
return uid == other.uid
|
if (uid != other.uid) return false
|
||||||
|
if (name != other.name) return false
|
||||||
|
if (cost != other.cost) return false
|
||||||
|
return ingredients == other.ingredients
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
return uid
|
var result = uid
|
||||||
|
result = 31 * result + name.hashCode()
|
||||||
|
result = 31 * result + cost.hashCode()
|
||||||
|
result = 31 * result + ingredients.hashCode()
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,11 +0,0 @@
|
|||||||
package com.zyzf.coffeepreorder.database.model
|
|
||||||
|
|
||||||
import androidx.room.ColumnInfo
|
|
||||||
import androidx.room.Embedded
|
|
||||||
|
|
||||||
data class CoffeeWithCart(
|
|
||||||
@Embedded
|
|
||||||
val coffee: Coffee,
|
|
||||||
@ColumnInfo(name = "cart_uid")
|
|
||||||
val cartUid: Int
|
|
||||||
)
|
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.zyzf.coffeepreorder.database.model
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import androidx.room.TypeConverter
|
||||||
|
import androidx.room.TypeConverters
|
||||||
|
|
||||||
|
enum class RemoteKeyType(private val type: String) {
|
||||||
|
COFFEE(Coffee::class.simpleName ?: "Coffee"),
|
||||||
|
USER(User::class.simpleName ?: "User");
|
||||||
|
|
||||||
|
@TypeConverter
|
||||||
|
fun toRemoteKeyType(value: String) = entries.first { it.type == value }
|
||||||
|
|
||||||
|
@TypeConverter
|
||||||
|
fun fromRemoteKeyType(value: RemoteKeyType) = value.type
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(tableName = "remote_keys")
|
||||||
|
data class RemoteKeys(
|
||||||
|
@PrimaryKey val entityId: Int,
|
||||||
|
@TypeConverters(RemoteKeyType::class)
|
||||||
|
val type: RemoteKeyType,
|
||||||
|
val prevKey: Int?,
|
||||||
|
val nextKey: Int?
|
||||||
|
)
|
||||||
|
|
@ -8,7 +8,7 @@ import androidx.room.PrimaryKey
|
|||||||
@Entity(tableName = "user")
|
@Entity(tableName = "user")
|
||||||
data class User(
|
data class User(
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
val uid: Int?,
|
val uid: Int = 0,
|
||||||
@ColumnInfo(name = "login")
|
@ColumnInfo(name = "login")
|
||||||
var login: String,
|
var login: String,
|
||||||
@ColumnInfo(name = "fio")
|
@ColumnInfo(name = "fio")
|
||||||
@ -27,16 +27,40 @@ data class User(
|
|||||||
phone: String,
|
phone: String,
|
||||||
password: String,
|
password: String,
|
||||||
role: String
|
role: String
|
||||||
) : this(null, login, fio, phone, password, role)
|
) : this(0, login, fio, phone, password, role)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun getUser(index: Int = 0): User {
|
||||||
|
return User(
|
||||||
|
index,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"user"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
if (javaClass != other?.javaClass) return false
|
if (javaClass != other?.javaClass) return false
|
||||||
other as User
|
other as User
|
||||||
return uid == other.uid
|
if (uid != other.uid) return false
|
||||||
|
if (login != other.login) return false
|
||||||
|
if (fio != other.fio) return false
|
||||||
|
if (phone != other.phone) return false
|
||||||
|
if (password != other.password) return false
|
||||||
|
return role == other.role
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
return uid ?: -1
|
var result = uid
|
||||||
|
result = 31 * result + login.hashCode()
|
||||||
|
result = 31 * result + fio.hashCode()
|
||||||
|
result = 31 * result + phone.hashCode()
|
||||||
|
result = 31 * result + password.hashCode()
|
||||||
|
result = 31 * result + role.hashCode()
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,39 +0,0 @@
|
|||||||
package com.zyzf.coffeepreorder.database.model
|
|
||||||
|
|
||||||
import androidx.room.ColumnInfo
|
|
||||||
import androidx.room.Entity
|
|
||||||
import androidx.room.ForeignKey
|
|
||||||
import androidx.room.Ignore
|
|
||||||
import androidx.room.PrimaryKey
|
|
||||||
|
|
||||||
@Entity(tableName = "user_logined", foreignKeys = [
|
|
||||||
ForeignKey(
|
|
||||||
entity = User::class,
|
|
||||||
parentColumns = ["uid"],
|
|
||||||
childColumns = ["user_id"],
|
|
||||||
onDelete = ForeignKey.RESTRICT,
|
|
||||||
onUpdate = ForeignKey.RESTRICT
|
|
||||||
)]
|
|
||||||
)
|
|
||||||
data class UserLogined(
|
|
||||||
@PrimaryKey(autoGenerate = true)
|
|
||||||
val uid: Int?,
|
|
||||||
@ColumnInfo(name = "user_id", index = true)
|
|
||||||
val userId: Int?
|
|
||||||
) {
|
|
||||||
@Ignore
|
|
||||||
constructor(
|
|
||||||
userId: Int?
|
|
||||||
) : this(null, userId)
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
other as UserLogined
|
|
||||||
return uid == other.uid
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
return uid ?: -1
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +1,18 @@
|
|||||||
package com.zyzf.coffeepreorder.database.repository
|
package com.zyzf.coffeepreorder.database.repository
|
||||||
|
|
||||||
|
import androidx.paging.PagingData
|
||||||
import com.zyzf.coffeepreorder.database.model.Cart
|
import com.zyzf.coffeepreorder.database.model.Cart
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Coffee
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
interface CartRepository {
|
interface CartRepository {
|
||||||
suspend fun get(): Cart
|
suspend fun getAll(): Cart
|
||||||
suspend fun insert(cart: Cart)
|
suspend fun insert(cart: Cart)
|
||||||
suspend fun insertCoffee(cartId: Int, coffeeId: Int, count: Int)
|
fun getAllInCart(): Flow<PagingData<Coffee>>
|
||||||
|
fun getSumInCart(): Double
|
||||||
|
suspend fun insertCoffee(coffeeId: Int, count: Int)
|
||||||
suspend fun deleteCoffee(coffeeId: Int, count: Int)
|
suspend fun deleteCoffee(coffeeId: Int, count: Int)
|
||||||
|
fun getCountForCoffee(coffeeId: Int): Double
|
||||||
suspend fun update(cart: Cart)
|
suspend fun update(cart: Cart)
|
||||||
suspend fun deleteAll()
|
suspend fun deleteAll()
|
||||||
}
|
}
|
@ -2,15 +2,12 @@ package com.zyzf.coffeepreorder.database.repository
|
|||||||
|
|
||||||
import androidx.paging.PagingData
|
import androidx.paging.PagingData
|
||||||
import com.zyzf.coffeepreorder.database.model.Coffee
|
import com.zyzf.coffeepreorder.database.model.Coffee
|
||||||
import com.zyzf.coffeepreorder.database.model.CoffeeWithCart
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
interface CoffeeRepository {
|
interface CoffeeRepository {
|
||||||
fun getAll(): Flow<PagingData<Coffee>>
|
fun getAllCoffees(): Flow<PagingData<Coffee>>
|
||||||
fun getAllInCart(): Flow<PagingData<Coffee>>
|
suspend fun getByUid(uid: Int): Coffee?
|
||||||
fun getSumInCart(): Double
|
suspend fun insert(coffee: Coffee): Long
|
||||||
suspend fun getByUid(uid: Int): CoffeeWithCart?
|
suspend fun update(coffee: Coffee): Int
|
||||||
suspend fun insert(name: String, cost: Double, ingredients: String): Long
|
|
||||||
suspend fun update(uid: Int, name: String, cost: Double, ingredients: String, cartId: Int?, count: Int): Int?
|
|
||||||
suspend fun delete(coffee: Coffee)
|
suspend fun delete(coffee: Coffee)
|
||||||
}
|
}
|
@ -1,13 +1,28 @@
|
|||||||
package com.zyzf.coffeepreorder.database.repository
|
package com.zyzf.coffeepreorder.database.repository
|
||||||
|
|
||||||
|
import androidx.paging.Pager
|
||||||
|
import androidx.paging.PagingConfig
|
||||||
|
import androidx.paging.PagingData
|
||||||
|
import com.zyzf.coffeepreorder.database.AppContainer
|
||||||
import com.zyzf.coffeepreorder.database.dao.CartDao
|
import com.zyzf.coffeepreorder.database.dao.CartDao
|
||||||
import com.zyzf.coffeepreorder.database.model.Cart
|
import com.zyzf.coffeepreorder.database.model.Cart
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Coffee
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
class OfflineCartRepository(private val cartDao: CartDao) : CartRepository {
|
class OfflineCartRepository(private val cartDao: CartDao) : CartRepository {
|
||||||
override suspend fun get(): Cart = cartDao.get()
|
override fun getAllInCart(): Flow<PagingData<Coffee>> = Pager(
|
||||||
|
config = PagingConfig(
|
||||||
|
pageSize = AppContainer.LIMIT,
|
||||||
|
enablePlaceholders = false
|
||||||
|
),
|
||||||
|
pagingSourceFactory = cartDao::getAllInCart
|
||||||
|
).flow
|
||||||
|
override fun getSumInCart(): Double = cartDao.getSumInCart()
|
||||||
|
override suspend fun getAll(): Cart = cartDao.getAll()
|
||||||
override suspend fun insert(cart: Cart) = cartDao.insert(cart)
|
override suspend fun insert(cart: Cart) = cartDao.insert(cart)
|
||||||
override suspend fun insertCoffee(cartId: Int, coffeeId: Int, count: Int) = cartDao.insertCoffee(cartId, coffeeId, count)
|
override suspend fun insertCoffee(coffeeId: Int, count: Int) = cartDao.insertCoffee(coffeeId, count)
|
||||||
override suspend fun deleteCoffee(coffeeId: Int, count: Int) = cartDao.deleteCoffee(coffeeId, count)
|
override suspend fun deleteCoffee(coffeeId: Int, count: Int) = cartDao.deleteCoffee(coffeeId, count)
|
||||||
|
override fun getCountForCoffee(coffeeId: Int): Double = cartDao.getCountForCoffee(coffeeId)
|
||||||
override suspend fun update(cart: Cart) = cartDao.update(cart)
|
override suspend fun update(cart: Cart) = cartDao.update(cart)
|
||||||
override suspend fun deleteAll() = cartDao.deleteAll()
|
override suspend fun deleteAll() = cartDao.deleteAll()
|
||||||
}
|
}
|
@ -3,30 +3,27 @@ package com.zyzf.coffeepreorder.database.repository
|
|||||||
import androidx.paging.Pager
|
import androidx.paging.Pager
|
||||||
import androidx.paging.PagingConfig
|
import androidx.paging.PagingConfig
|
||||||
import androidx.paging.PagingData
|
import androidx.paging.PagingData
|
||||||
import com.zyzf.coffeepreorder.database.AppDataContainer
|
import androidx.paging.PagingSource
|
||||||
|
import com.zyzf.coffeepreorder.database.AppContainer
|
||||||
import com.zyzf.coffeepreorder.database.dao.CoffeeDao
|
import com.zyzf.coffeepreorder.database.dao.CoffeeDao
|
||||||
import com.zyzf.coffeepreorder.database.model.Coffee
|
import com.zyzf.coffeepreorder.database.model.Coffee
|
||||||
import com.zyzf.coffeepreorder.database.model.CoffeeWithCart
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
class OfflineCoffeeRepository(private val coffeeDao: CoffeeDao) : CoffeeRepository {
|
class OfflineCoffeeRepository(private val coffeeDao: CoffeeDao) : CoffeeRepository {
|
||||||
override fun getAll(): Flow<PagingData<Coffee>> = Pager(
|
override fun getAllCoffees(): Flow<PagingData<Coffee>> = Pager(
|
||||||
config = PagingConfig(
|
config = PagingConfig(
|
||||||
pageSize = AppDataContainer.LIMIT,
|
pageSize = AppContainer.LIMIT,
|
||||||
enablePlaceholders = false
|
enablePlaceholders = false
|
||||||
),
|
),
|
||||||
pagingSourceFactory = coffeeDao::getAll
|
pagingSourceFactory = coffeeDao::getAllCoffees
|
||||||
).flow
|
).flow
|
||||||
override fun getAllInCart(): Flow<PagingData<Coffee>> = Pager(
|
|
||||||
config = PagingConfig(
|
fun getAllCoffeesPagingSource(): PagingSource<Int, Coffee> = coffeeDao.getAllCoffees()
|
||||||
pageSize = AppDataContainer.LIMIT,
|
suspend fun clearCoffees() = coffeeDao.deleteAll()
|
||||||
enablePlaceholders = false
|
override suspend fun getByUid(uid: Int): Coffee? = coffeeDao.getByUid(uid)
|
||||||
),
|
override suspend fun insert(coffee: Coffee): Long = coffeeDao.insert(coffee)
|
||||||
pagingSourceFactory = coffeeDao::getAllInCart
|
suspend fun insertCoffees(coffees: List<Coffee>) =
|
||||||
).flow
|
coffeeDao.insert(*coffees.toTypedArray())
|
||||||
override fun getSumInCart(): Double = coffeeDao.getSumInCart()
|
override suspend fun update(coffee: Coffee): Int = coffeeDao.update(coffee)
|
||||||
override suspend fun getByUid(uid: Int): CoffeeWithCart? = coffeeDao.getByUid(uid)
|
|
||||||
override suspend fun insert(name: String, cost: Double, ingredients: String): Long = coffeeDao.insert(name, cost, ingredients)
|
|
||||||
override suspend fun update(uid: Int, name: String, cost: Double, ingredients: String, cartId: Int?, count: Int): Int? = coffeeDao.update(uid, name, cost, ingredients, cartId, count)
|
|
||||||
override suspend fun delete(coffee: Coffee) = coffeeDao.delete(coffee)
|
override suspend fun delete(coffee: Coffee) = coffeeDao.delete(coffee)
|
||||||
}
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.zyzf.coffeepreorder.database.repository
|
||||||
|
|
||||||
|
import com.zyzf.coffeepreorder.database.dao.RemoteKeysDao
|
||||||
|
import com.zyzf.coffeepreorder.database.model.RemoteKeyType
|
||||||
|
import com.zyzf.coffeepreorder.database.model.RemoteKeys
|
||||||
|
|
||||||
|
class OfflineRemoteKeyRepository(private val remoteKeysDao: RemoteKeysDao) : RemoteKeyRepository {
|
||||||
|
override suspend fun getAllRemoteKeys(id: Int, type: RemoteKeyType) =
|
||||||
|
remoteKeysDao.getRemoteKeys(id, type)
|
||||||
|
|
||||||
|
override suspend fun createRemoteKeys(remoteKeys: List<RemoteKeys>) =
|
||||||
|
remoteKeysDao.insertAll(remoteKeys)
|
||||||
|
|
||||||
|
override suspend fun deleteRemoteKey(type: RemoteKeyType) =
|
||||||
|
remoteKeysDao.clearRemoteKeys(type)
|
||||||
|
}
|
@ -1,17 +1,29 @@
|
|||||||
package com.zyzf.coffeepreorder.database.repository
|
package com.zyzf.coffeepreorder.database.repository
|
||||||
|
|
||||||
|
import androidx.paging.Pager
|
||||||
|
import androidx.paging.PagingConfig
|
||||||
|
import androidx.paging.PagingData
|
||||||
|
import androidx.paging.PagingSource
|
||||||
|
import com.zyzf.coffeepreorder.database.AppContainer
|
||||||
import com.zyzf.coffeepreorder.database.dao.UserDao
|
import com.zyzf.coffeepreorder.database.dao.UserDao
|
||||||
import com.zyzf.coffeepreorder.database.model.User
|
import com.zyzf.coffeepreorder.database.model.User
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
class OfflineUserRepository(private val userDao: UserDao) : UserRepository {
|
class OfflineUserRepository(private val userDao: UserDao) : UserRepository {
|
||||||
override fun getAll(): Flow<List<User>> = userDao.getAll()
|
override fun getAll(): Flow<PagingData<User>> = Pager(
|
||||||
override suspend fun tryLogin(login: String, password: String): User? = userDao.tryLogin(login, password)
|
config = PagingConfig(
|
||||||
|
pageSize = AppContainer.LIMIT,
|
||||||
|
enablePlaceholders = false
|
||||||
|
),
|
||||||
|
pagingSourceFactory = userDao::getAll
|
||||||
|
).flow
|
||||||
|
fun getAllUserPagingSource(): PagingSource<Int, User> = userDao.getAll()
|
||||||
override suspend fun getByUid(uid: Int): User? = userDao.getByUid(uid)
|
override suspend fun getByUid(uid: Int): User? = userDao.getByUid(uid)
|
||||||
override suspend fun getLogined(): User? = userDao.getLogined()
|
override suspend fun tryLogin(login: String, password: String): User? = userDao.tryLogin(login, password)
|
||||||
override suspend fun setLogined(userId: Int) = userDao.setLogined(userId)
|
override suspend fun insert(user: User): Long = userDao.insert(user)
|
||||||
override suspend fun logout() = userDao.logout()
|
fun insertUsers(users: List<User>) =
|
||||||
override suspend fun insert(user: User) = userDao.insert(user)
|
userDao.insert(*users.toTypedArray())
|
||||||
override suspend fun update(uid: Int, login: String, fio: String, phone: String, password: String) : Int? = userDao.update(uid, login, fio, phone, password)
|
override suspend fun update(user: User): Int? = userDao.update(user)
|
||||||
override suspend fun delete(uid: Int) = userDao.delete(uid)
|
override suspend fun delete(user: User) = userDao.delete(user)
|
||||||
|
suspend fun clearUsers() = userDao.deleteAll()
|
||||||
}
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package com.zyzf.coffeepreorder.database.repository
|
||||||
|
|
||||||
|
import com.zyzf.coffeepreorder.database.model.RemoteKeyType
|
||||||
|
import com.zyzf.coffeepreorder.database.model.RemoteKeys
|
||||||
|
|
||||||
|
interface RemoteKeyRepository {
|
||||||
|
suspend fun getAllRemoteKeys(id: Int, type: RemoteKeyType): RemoteKeys?
|
||||||
|
suspend fun createRemoteKeys(remoteKeys: List<RemoteKeys>)
|
||||||
|
suspend fun deleteRemoteKey(type: RemoteKeyType)
|
||||||
|
}
|
@ -1,16 +1,14 @@
|
|||||||
package com.zyzf.coffeepreorder.database.repository
|
package com.zyzf.coffeepreorder.database.repository
|
||||||
|
|
||||||
|
import androidx.paging.PagingData
|
||||||
import com.zyzf.coffeepreorder.database.model.User
|
import com.zyzf.coffeepreorder.database.model.User
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
interface UserRepository {
|
interface UserRepository {
|
||||||
fun getAll(): Flow<List<User>>
|
fun getAll(): Flow<PagingData<User>>
|
||||||
suspend fun tryLogin(login: String, password: String): User?
|
|
||||||
suspend fun getByUid(uid: Int): User?
|
suspend fun getByUid(uid: Int): User?
|
||||||
suspend fun getLogined(): User?
|
suspend fun tryLogin(login: String, password: String): User?
|
||||||
suspend fun setLogined(userId: Int)
|
suspend fun insert(user: User): Long
|
||||||
suspend fun logout()
|
suspend fun update(user: User) : Int?
|
||||||
suspend fun insert(user: User)
|
suspend fun delete(user: User)
|
||||||
suspend fun update(uid: Int, login: String, fio: String, phone: String, password: String) : Int?
|
|
||||||
suspend fun delete(uid: Int)
|
|
||||||
}
|
}
|
@ -7,14 +7,26 @@ import androidx.lifecycle.viewmodel.viewModelFactory
|
|||||||
import com.zyzf.coffeepreorder.CoffeeApplication
|
import com.zyzf.coffeepreorder.CoffeeApplication
|
||||||
import com.zyzf.coffeepreorder.ui.cart.CartViewModel
|
import com.zyzf.coffeepreorder.ui.cart.CartViewModel
|
||||||
import com.zyzf.coffeepreorder.ui.coffee.CoffeeListViewModel
|
import com.zyzf.coffeepreorder.ui.coffee.CoffeeListViewModel
|
||||||
|
import com.zyzf.coffeepreorder.ui.login.LoginViewModel
|
||||||
|
import com.zyzf.coffeepreorder.ui.profile.ProfileViewModel
|
||||||
|
import com.zyzf.coffeepreorder.ui.register.RegisterViewModel
|
||||||
|
|
||||||
object AppViewModelProvider {
|
object AppViewModelProvider {
|
||||||
val Factory = viewModelFactory {
|
val Factory = viewModelFactory {
|
||||||
initializer {
|
initializer {
|
||||||
CoffeeListViewModel(coffeeApplication().container.coffeeRepository, coffeeApplication().container.cartRepository)
|
CoffeeListViewModel(coffeeApplication().container.coffeeRestRepository, coffeeApplication().container.cartRepository)
|
||||||
}
|
}
|
||||||
initializer {
|
initializer {
|
||||||
CartViewModel(coffeeApplication().container.coffeeRepository, coffeeApplication().container.cartRepository)
|
CartViewModel(coffeeApplication().container.cartRepository)
|
||||||
|
}
|
||||||
|
initializer {
|
||||||
|
LoginViewModel(coffeeApplication().container.userRestRepository)
|
||||||
|
}
|
||||||
|
initializer {
|
||||||
|
RegisterViewModel(coffeeApplication().container.userRestRepository)
|
||||||
|
}
|
||||||
|
initializer {
|
||||||
|
ProfileViewModel(coffeeApplication().container.userRestRepository)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.Box
|
|||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.heightIn
|
import androidx.compose.foundation.layout.heightIn
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
@ -95,37 +94,39 @@ fun Cart(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
Box (modifier = Modifier.padding(0.dp).pullRefresh(state)) {
|
Box (modifier = Modifier
|
||||||
|
.padding(innerPadding)
|
||||||
|
.pullRefresh(state)) {
|
||||||
PullRefreshIndicator(refreshing = refreshing, state = state,
|
PullRefreshIndicator(refreshing = refreshing, state = state,
|
||||||
modifier = Modifier.zIndex(100f).align(Alignment.TopCenter)
|
modifier = Modifier
|
||||||
|
.zIndex(100f)
|
||||||
|
.align(Alignment.TopCenter)
|
||||||
)
|
)
|
||||||
CartList(
|
CartList(
|
||||||
modifier = Modifier
|
|
||||||
.padding(innerPadding).pullRefresh(state)
|
|
||||||
.fillMaxSize(),
|
|
||||||
coffeeList = coffeeListUiState,
|
coffeeList = coffeeListUiState,
|
||||||
onDeleteFromCartClick = {currentCoffee: Coffee ->
|
onDeleteFromCartClick = {currentCoffee: Coffee ->
|
||||||
coffee.value = currentCoffee
|
coffee.value = currentCoffee
|
||||||
openDialog.value = true
|
openDialog.value = true
|
||||||
}
|
}
|
||||||
)
|
) { coffeeId: Int ->
|
||||||
|
viewModel.getCountForCoffee(coffeeId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DeleteFromCartAlertDialog(
|
DeleteFromCartAlertDialog(
|
||||||
openDialog = openDialog,
|
openDialog = openDialog
|
||||||
onConfirmClick = {
|
) {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
viewModel.deleteCoffeeFromCart(coffee.value)
|
viewModel.deleteCoffeeFromCart(coffee.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun CartList(
|
private fun CartList(
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
coffeeList: LazyPagingItems<Coffee>,
|
coffeeList: LazyPagingItems<Coffee>,
|
||||||
onDeleteFromCartClick: (coffee: Coffee) -> Unit
|
onDeleteFromCartClick: (coffee: Coffee) -> Unit,
|
||||||
|
getCoffeeCount: (coffeeId: Int) -> Double
|
||||||
) {
|
) {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally) {
|
horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
@ -138,7 +139,8 @@ private fun CartList(
|
|||||||
coffee?.let {
|
coffee?.let {
|
||||||
CartListItem(
|
CartListItem(
|
||||||
coffee = coffee,
|
coffee = coffee,
|
||||||
onDeleteFromCartClick = onDeleteFromCartClick
|
onDeleteFromCartClick = onDeleteFromCartClick,
|
||||||
|
getCoffeeCount = getCoffeeCount
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,8 +150,8 @@ private fun CartList(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun CartListItem (
|
private fun CartListItem (
|
||||||
coffee: Coffee,
|
coffee: Coffee,
|
||||||
modifier: Modifier = Modifier,
|
onDeleteFromCartClick: (coffee: Coffee) -> Unit,
|
||||||
onDeleteFromCartClick: (coffee: Coffee) -> Unit
|
getCoffeeCount: (coffeeId: Int) -> Double
|
||||||
) {
|
) {
|
||||||
Row(modifier = Modifier
|
Row(modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@ -171,8 +173,9 @@ private fun CartListItem (
|
|||||||
Modifier
|
Modifier
|
||||||
.weight(2f)
|
.weight(2f)
|
||||||
.padding(start = 20.dp)) {
|
.padding(start = 20.dp)) {
|
||||||
val currentCoffeeName: String = if (coffee.count > 1) {
|
val coffeeCount: Double = getCoffeeCount(coffee.uid)
|
||||||
coffee.name + " x" + coffee.count.toString()
|
val currentCoffeeName: String = if (coffeeCount > 1) {
|
||||||
|
coffee.name + " x" + coffeeCount.toString()
|
||||||
} else {
|
} else {
|
||||||
coffee.name
|
coffee.name
|
||||||
}
|
}
|
||||||
@ -207,7 +210,6 @@ private fun CartListItem (
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun DeleteFromCartAlertDialog(
|
private fun DeleteFromCartAlertDialog(
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
openDialog: MutableState<Boolean>,
|
openDialog: MutableState<Boolean>,
|
||||||
onConfirmClick: () -> Unit
|
onConfirmClick: () -> Unit
|
||||||
) {
|
) {
|
||||||
|
@ -4,14 +4,16 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.paging.PagingData
|
import androidx.paging.PagingData
|
||||||
import com.zyzf.coffeepreorder.database.model.Coffee
|
import com.zyzf.coffeepreorder.database.model.Coffee
|
||||||
import com.zyzf.coffeepreorder.database.repository.CartRepository
|
import com.zyzf.coffeepreorder.database.repository.CartRepository
|
||||||
import com.zyzf.coffeepreorder.database.repository.CoffeeRepository
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
class CartViewModel(
|
class CartViewModel(
|
||||||
private val coffeeRepository: CoffeeRepository,
|
|
||||||
private val cartRepository: CartRepository
|
private val cartRepository: CartRepository
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
val coffeeListUiState: Flow<PagingData<Coffee>> = coffeeRepository.getAllInCart()
|
val coffeeListUiState: Flow<PagingData<Coffee>> = cartRepository.getAllInCart()
|
||||||
|
|
||||||
|
fun getCountForCoffee(coffeeId: Int): Double {
|
||||||
|
return cartRepository.getCountForCoffee(coffeeId)
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun deleteCoffeeFromCart(coffee: Coffee) {
|
suspend fun deleteCoffeeFromCart(coffee: Coffee) {
|
||||||
cartRepository.deleteCoffee(coffee.uid, 1)
|
cartRepository.deleteCoffee(coffee.uid, 1)
|
||||||
|
@ -15,7 +15,6 @@ import androidx.compose.foundation.layout.Box
|
|||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.heightIn
|
import androidx.compose.foundation.layout.heightIn
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
@ -69,6 +68,7 @@ import androidx.paging.compose.itemContentType
|
|||||||
import androidx.paging.compose.itemKey
|
import androidx.paging.compose.itemKey
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
|
import com.zyzf.coffeepreorder.CoffeeApplication
|
||||||
import com.zyzf.coffeepreorder.R
|
import com.zyzf.coffeepreorder.R
|
||||||
import com.zyzf.coffeepreorder.database.model.Coffee
|
import com.zyzf.coffeepreorder.database.model.Coffee
|
||||||
import com.zyzf.coffeepreorder.ui.AppViewModelProvider
|
import com.zyzf.coffeepreorder.ui.AppViewModelProvider
|
||||||
@ -89,6 +89,7 @@ fun CoffeeList(
|
|||||||
val sheetState = rememberModalBottomSheetState()
|
val sheetState = rememberModalBottomSheetState()
|
||||||
val openDialog = remember { mutableStateOf(false) }
|
val openDialog = remember { mutableStateOf(false) }
|
||||||
val coffee = remember { mutableStateOf(Coffee.getCoffee()) }
|
val coffee = remember { mutableStateOf(Coffee.getCoffee()) }
|
||||||
|
val isImageChanged = remember { mutableStateOf(false) }
|
||||||
var imageUri: Any? by remember { mutableStateOf(R.drawable.img) }
|
var imageUri: Any? by remember { mutableStateOf(R.drawable.img) }
|
||||||
var refreshing by remember { mutableStateOf(false) }
|
var refreshing by remember { mutableStateOf(false) }
|
||||||
fun refresh() = coroutineScope.launch {
|
fun refresh() = coroutineScope.launch {
|
||||||
@ -103,6 +104,7 @@ fun CoffeeList(
|
|||||||
if (it != null) {
|
if (it != null) {
|
||||||
Log.d("PhotoPicker", "Selected URI: $it")
|
Log.d("PhotoPicker", "Selected URI: $it")
|
||||||
imageUri = it
|
imageUri = it
|
||||||
|
isImageChanged.value = true
|
||||||
} else {
|
} else {
|
||||||
Log.d("PhotoPicker", "No media selected")
|
Log.d("PhotoPicker", "No media selected")
|
||||||
}
|
}
|
||||||
@ -110,6 +112,7 @@ fun CoffeeList(
|
|||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {},
|
topBar = {},
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
|
if (CoffeeApplication.currentUser?.role == "admin") {
|
||||||
FloatingActionButton(
|
FloatingActionButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
@ -127,28 +130,29 @@ fun CoffeeList(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
Box (modifier = Modifier.padding(0.dp).pullRefresh(state)) {
|
Box (modifier = Modifier
|
||||||
|
.padding(innerPadding)
|
||||||
|
.pullRefresh(state)) {
|
||||||
PullRefreshIndicator(refreshing = refreshing, state = state,
|
PullRefreshIndicator(refreshing = refreshing, state = state,
|
||||||
modifier = Modifier.zIndex(100f).align(Alignment.TopCenter)
|
modifier = Modifier
|
||||||
|
.zIndex(100f)
|
||||||
|
.align(Alignment.TopCenter)
|
||||||
)
|
)
|
||||||
CoffeeList(
|
CoffeeList(
|
||||||
modifier = Modifier
|
|
||||||
.padding(innerPadding)
|
|
||||||
.fillMaxSize(),
|
|
||||||
coffeeList = coffeeListUiState,
|
coffeeList = coffeeListUiState,
|
||||||
onAddToCartClick = { coffeeUid: Int ->
|
onAddToCartClick = { coffeeUid: Int ->
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
viewModel.addCoffeeToCart(coffeeUid = coffeeUid)
|
viewModel.addCoffeeToCart(coffeeUid = coffeeUid)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
onEditClick = { currentCoffee: Coffee ->
|
) { currentCoffee: Coffee ->
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
coffee.value = currentCoffee
|
coffee.value = currentCoffee
|
||||||
openDialog.value = true
|
openDialog.value = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AddEditModalBottomSheet(
|
AddEditModalBottomSheet(
|
||||||
@ -162,16 +166,23 @@ fun CoffeeList(
|
|||||||
},
|
},
|
||||||
onEditClick = { currentCoffee: Coffee, context: Context ->
|
onEditClick = { currentCoffee: Coffee, context: Context ->
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
viewModel.editCoffee(currentCoffee, imageUri as Uri, context)
|
viewModel.editCoffee(currentCoffee, imageUri as Uri?, context)
|
||||||
}
|
}
|
||||||
|
isImageChanged.value = false
|
||||||
},
|
},
|
||||||
onDeleteClick = { currentCoffee: Coffee ->
|
onDeleteClick = { currentCoffee: Coffee ->
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
viewModel.deleteCoffee(currentCoffee)
|
viewModel.deleteCoffee(currentCoffee)
|
||||||
}
|
}
|
||||||
|
isImageChanged.value = false
|
||||||
},
|
},
|
||||||
photoPicker = photoPicker,
|
photoPicker = photoPicker,
|
||||||
imageUri = imageUri
|
imageUri = imageUri,
|
||||||
|
beforeOpen = {coffeeUid: Int ->
|
||||||
|
if (coffeeUid != 0 && !isImageChanged.value) {
|
||||||
|
imageUri = Uri.parse("https://zyzf.space/s/zXgFRTmbR4KMxMH/download?path=&files=coffee_image_" + coffee.value.uid +".png")
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,8 +196,10 @@ private fun AddEditModalBottomSheet(
|
|||||||
onEditClick: (coffee: Coffee, context: Context) -> Unit,
|
onEditClick: (coffee: Coffee, context: Context) -> Unit,
|
||||||
onDeleteClick: (coffee: Coffee) -> Unit,
|
onDeleteClick: (coffee: Coffee) -> Unit,
|
||||||
photoPicker: ManagedActivityResultLauncher<PickVisualMediaRequest, Uri?>,
|
photoPicker: ManagedActivityResultLauncher<PickVisualMediaRequest, Uri?>,
|
||||||
imageUri: Any?
|
imageUri: Any?,
|
||||||
|
beforeOpen: (coffeeUid: Int) -> Unit
|
||||||
) {
|
) {
|
||||||
|
beforeOpen(coffee.value.uid)
|
||||||
var name: String by remember { mutableStateOf("")}
|
var name: String by remember { mutableStateOf("")}
|
||||||
var cost: Double by remember { mutableDoubleStateOf(0.0) }
|
var cost: Double by remember { mutableDoubleStateOf(0.0) }
|
||||||
var ingredients: String by remember { mutableStateOf("")}
|
var ingredients: String by remember { mutableStateOf("")}
|
||||||
@ -251,22 +264,23 @@ private fun AddEditModalBottomSheet(
|
|||||||
error = painterResource(R.drawable.ic_broken_image),
|
error = painterResource(R.drawable.ic_broken_image),
|
||||||
placeholder = painterResource(R.drawable.loading_img),
|
placeholder = painterResource(R.drawable.loading_img),
|
||||||
contentScale = ContentScale.Crop,
|
contentScale = ContentScale.Crop,
|
||||||
model = ImageRequest.Builder(LocalContext.current)
|
model = ImageRequest
|
||||||
.data(if (coffee.value.uid != 0) "https://zyzf.space/s/zXgFRTmbR4KMxMH/download?path=&files=coffee_image_" + coffee.value.uid +".png" else imageUri)
|
.Builder(context = LocalContext.current)
|
||||||
.crossfade(enable = true)
|
.data(imageUri)
|
||||||
|
.crossfade(true)
|
||||||
.build(),
|
.build(),
|
||||||
)
|
)
|
||||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
|
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
|
||||||
if (coffee.value.uid == 0) {
|
if (coffee.value.uid == 0) {
|
||||||
Button(onClick = {
|
Button(onClick = {
|
||||||
onAddClick(Coffee(coffee.value.uid, name, cost, ingredients, coffee.value.cartId, coffee.value.count), context)
|
onAddClick(Coffee(coffee.value.uid, name, cost, ingredients), context)
|
||||||
openDialog.value = false
|
openDialog.value = false
|
||||||
}, modifier = Modifier.padding(0.dp, 10.dp, 0.dp, 30.dp)) {
|
}, modifier = Modifier.padding(0.dp, 10.dp, 0.dp, 30.dp)) {
|
||||||
Text("Добавить")
|
Text("Добавить")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Button(onClick = {
|
Button(onClick = {
|
||||||
onEditClick(Coffee(coffee.value.uid, name, cost, ingredients, coffee.value.cartId, coffee.value.count), context)
|
onEditClick(Coffee(coffee.value.uid, name, cost, ingredients), context)
|
||||||
openDialog.value = false
|
openDialog.value = false
|
||||||
}, modifier = Modifier.padding(0.dp, 10.dp, 0.dp, 30.dp)) {
|
}, modifier = Modifier.padding(0.dp, 10.dp, 0.dp, 30.dp)) {
|
||||||
Text("Изменить")
|
Text("Изменить")
|
||||||
@ -287,7 +301,6 @@ private fun AddEditModalBottomSheet(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun CoffeeList(
|
private fun CoffeeList(
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
coffeeList: LazyPagingItems<Coffee>,
|
coffeeList: LazyPagingItems<Coffee>,
|
||||||
onAddToCartClick: (coffeeUid: Int) -> Unit,
|
onAddToCartClick: (coffeeUid: Int) -> Unit,
|
||||||
onEditClick: (coffee: Coffee) -> Unit
|
onEditClick: (coffee: Coffee) -> Unit
|
||||||
@ -301,7 +314,11 @@ private fun CoffeeList(
|
|||||||
) {index ->
|
) {index ->
|
||||||
val coffee = coffeeList[index]
|
val coffee = coffeeList[index]
|
||||||
coffee?.let {
|
coffee?.let {
|
||||||
CoffeeListItem(coffee = coffee, onAddToCartClick = onAddToCartClick, onEditClick = onEditClick)
|
CoffeeListItem(
|
||||||
|
coffee = coffee,
|
||||||
|
onAddToCartClick = onAddToCartClick,
|
||||||
|
onEditClick = onEditClick
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -310,7 +327,6 @@ private fun CoffeeList(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun CoffeeListItem(
|
private fun CoffeeListItem(
|
||||||
coffee: Coffee,
|
coffee: Coffee,
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
onAddToCartClick: (coffeeUid: Int) -> Unit,
|
onAddToCartClick: (coffeeUid: Int) -> Unit,
|
||||||
onEditClick: (coffee: Coffee) -> Unit
|
onEditClick: (coffee: Coffee) -> Unit
|
||||||
) {
|
) {
|
||||||
@ -321,7 +337,9 @@ private fun CoffeeListItem(
|
|||||||
horizontalArrangement = Arrangement.SpaceAround) {
|
horizontalArrangement = Arrangement.SpaceAround) {
|
||||||
|
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = ImageRequest.Builder(context = LocalContext.current).data("https://zyzf.space/s/zXgFRTmbR4KMxMH/download?path=&files=coffee_image_" + coffee.uid +".png")
|
model = ImageRequest
|
||||||
|
.Builder(context = LocalContext.current)
|
||||||
|
.data("https://zyzf.space/s/zXgFRTmbR4KMxMH/download?path=&files=coffee_image_" + coffee.uid +".png")
|
||||||
.crossfade(true).build(),
|
.crossfade(true).build(),
|
||||||
error = painterResource(R.drawable.ic_broken_image),
|
error = painterResource(R.drawable.ic_broken_image),
|
||||||
placeholder = painterResource(R.drawable.loading_img),
|
placeholder = painterResource(R.drawable.loading_img),
|
||||||
@ -358,13 +376,15 @@ private fun CoffeeListItem(
|
|||||||
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
|
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
|
||||||
Text(text = "В корзину")
|
Text(text = "В корзину")
|
||||||
}
|
}
|
||||||
|
if (CoffeeApplication.currentUser?.role == "admin") {
|
||||||
OutlinedIconButton(
|
OutlinedIconButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
onEditClick(coffee)
|
onEditClick(coffee)
|
||||||
},
|
},
|
||||||
Modifier
|
Modifier
|
||||||
.padding(start = 10.dp)
|
.padding(start = 10.dp)
|
||||||
.clip(CircleShape)) {
|
.clip(CircleShape)
|
||||||
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Outlined.Create,
|
imageVector = Icons.Outlined.Create,
|
||||||
contentDescription = "Favorite",
|
contentDescription = "Favorite",
|
||||||
@ -374,6 +394,7 @@ private fun CoffeeListItem(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -389,9 +410,8 @@ fun CoffeeListPreview() {
|
|||||||
coffeeList = MutableStateFlow(
|
coffeeList = MutableStateFlow(
|
||||||
PagingData.from((1..20).map { i -> Coffee.getCoffee(i) })
|
PagingData.from((1..20).map { i -> Coffee.getCoffee(i) })
|
||||||
).collectAsLazyPagingItems(),
|
).collectAsLazyPagingItems(),
|
||||||
onAddToCartClick = {},
|
onAddToCartClick = {}
|
||||||
onEditClick = {}
|
) {}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -409,9 +429,8 @@ fun CoffeeEmptyListPreview() {
|
|||||||
coffeeList = MutableStateFlow(
|
coffeeList = MutableStateFlow(
|
||||||
PagingData.empty<Coffee>()
|
PagingData.empty<Coffee>()
|
||||||
).collectAsLazyPagingItems(),
|
).collectAsLazyPagingItems(),
|
||||||
onAddToCartClick = {},
|
onAddToCartClick = {}
|
||||||
onEditClick = {}
|
) {}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -13,7 +13,6 @@ import com.jcraft.jsch.JSch
|
|||||||
import com.jcraft.jsch.JSchException
|
import com.jcraft.jsch.JSchException
|
||||||
import com.jcraft.jsch.Session
|
import com.jcraft.jsch.Session
|
||||||
import com.jcraft.jsch.SftpException
|
import com.jcraft.jsch.SftpException
|
||||||
import com.zyzf.coffeepreorder.database.model.Cart
|
|
||||||
import com.zyzf.coffeepreorder.database.model.Coffee
|
import com.zyzf.coffeepreorder.database.model.Coffee
|
||||||
import com.zyzf.coffeepreorder.database.repository.CartRepository
|
import com.zyzf.coffeepreorder.database.repository.CartRepository
|
||||||
import com.zyzf.coffeepreorder.database.repository.CoffeeRepository
|
import com.zyzf.coffeepreorder.database.repository.CoffeeRepository
|
||||||
@ -29,14 +28,13 @@ class CoffeeListViewModel(
|
|||||||
private val coffeeRepository: CoffeeRepository,
|
private val coffeeRepository: CoffeeRepository,
|
||||||
private val cartRepository: CartRepository
|
private val cartRepository: CartRepository
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
val coffeeListUiState: Flow<PagingData<Coffee>> = coffeeRepository.getAll()
|
val coffeeListUiState: Flow<PagingData<Coffee>> = coffeeRepository.getAllCoffees()
|
||||||
|
|
||||||
suspend fun addCoffeeToCart(coffeeUid: Int) {
|
suspend fun addCoffeeToCart(coffeeUid: Int) {
|
||||||
val cart: Cart = cartRepository.get()
|
cartRepository.insertCoffee(coffeeUid, 1)
|
||||||
cart.uid?.let { cartRepository.insertCoffee(it, coffeeUid, 1) }
|
|
||||||
}
|
}
|
||||||
suspend fun createCoffee(coffee: Coffee, imageUri: Uri, context: Context) {
|
suspend fun createCoffee(coffee: Coffee, imageUri: Uri, context: Context) {
|
||||||
val newCoffee: Long = coffeeRepository.insert(coffee.name, coffee.cost, coffee.ingredients)
|
val newCoffee: Long = coffeeRepository.insert(coffee)
|
||||||
val inputStream = context.contentResolver.openInputStream(imageUri)
|
val inputStream = context.contentResolver.openInputStream(imageUri)
|
||||||
val bitmap = BitmapFactory.decodeStream(inputStream)
|
val bitmap = BitmapFactory.decodeStream(inputStream)
|
||||||
|
|
||||||
@ -54,8 +52,9 @@ class CoffeeListViewModel(
|
|||||||
|
|
||||||
copyFileToSftp(f, "/mnt/nextcloud/data/Zyzf/files/Images")
|
copyFileToSftp(f, "/mnt/nextcloud/data/Zyzf/files/Images")
|
||||||
}
|
}
|
||||||
suspend fun editCoffee(coffee: Coffee, imageUri: Uri, context: Context) {
|
suspend fun editCoffee(coffee: Coffee, imageUri: Any?, context: Context) {
|
||||||
val editedCoffee: Int = coffeeRepository.update(coffee.uid, coffee.name, coffee.cost, coffee.ingredients, coffee.cartId, coffee.count)!!
|
val editedCoffee: Int = coffeeRepository.update(coffee)
|
||||||
|
if (imageUri !is Uri) return
|
||||||
val inputStream = context.contentResolver.openInputStream(imageUri)
|
val inputStream = context.contentResolver.openInputStream(imageUri)
|
||||||
val bitmap = BitmapFactory.decodeStream(inputStream)
|
val bitmap = BitmapFactory.decodeStream(inputStream)
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ import androidx.compose.runtime.LaunchedEffect
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@ -34,24 +35,29 @@ import androidx.compose.ui.text.input.KeyboardType
|
|||||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
|
import com.zyzf.coffeepreorder.CoffeeApplication
|
||||||
import com.zyzf.coffeepreorder.R
|
import com.zyzf.coffeepreorder.R
|
||||||
import com.zyzf.coffeepreorder.database.AppDatabase
|
import com.zyzf.coffeepreorder.database.AppDatabase
|
||||||
import com.zyzf.coffeepreorder.database.model.User
|
import com.zyzf.coffeepreorder.database.model.User
|
||||||
|
import com.zyzf.coffeepreorder.ui.AppViewModelProvider
|
||||||
import com.zyzf.coffeepreorder.ui.navigation.Screen
|
import com.zyzf.coffeepreorder.ui.navigation.Screen
|
||||||
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
|
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Login(navController: NavController?) {
|
fun Login(
|
||||||
|
navController: NavController?,
|
||||||
|
viewModel: LoginViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
||||||
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
var login by remember { mutableStateOf("") }
|
var login by remember { mutableStateOf("") }
|
||||||
var password by remember { mutableStateOf("") }
|
var password by remember { mutableStateOf("") }
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
@ -90,11 +96,11 @@ fun Login(navController: NavController?) {
|
|||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
var user: User?
|
var user: User?
|
||||||
GlobalScope.launch (Dispatchers.Main) {
|
coroutineScope.launch {
|
||||||
user = AppDatabase.getInstance(context).userDao().tryLogin(login, password)
|
user = viewModel.tryLogin(login, password)
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
AppDatabase.getInstance(context).userDao().logout()
|
CoffeeApplication.currentUser = null
|
||||||
AppDatabase.getInstance(context).userDao().setLogined(user!!.uid!!)
|
CoffeeApplication.currentUser = user
|
||||||
navController?.navigate(Screen.CoffeeList.route)
|
navController?.navigate(Screen.CoffeeList.route)
|
||||||
} else {
|
} else {
|
||||||
password = ""
|
password = ""
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.zyzf.coffeepreorder.ui.login
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.zyzf.coffeepreorder.database.model.User
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.UserRepository
|
||||||
|
|
||||||
|
class LoginViewModel(
|
||||||
|
private val userRepository: UserRepository
|
||||||
|
) : ViewModel() {
|
||||||
|
suspend fun tryLogin(login: String, password: String): User? {
|
||||||
|
val user: User? = userRepository.tryLogin(login, password)
|
||||||
|
return if (user?.uid == 0) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -52,6 +52,7 @@ fun Topbar(
|
|||||||
if (
|
if (
|
||||||
navController.previousBackStackEntry != null
|
navController.previousBackStackEntry != null
|
||||||
&& (currentScreen == null || !currentScreen.showInBottomBar)
|
&& (currentScreen == null || !currentScreen.showInBottomBar)
|
||||||
|
&& currentScreen != Screen.Login
|
||||||
) {
|
) {
|
||||||
IconButton(onClick = { navController.navigateUp() }) {
|
IconButton(onClick = { navController.navigateUp() }) {
|
||||||
Icon(
|
Icon(
|
||||||
|
@ -64,7 +64,7 @@ fun Order(navController: NavController?) {
|
|||||||
val sum = remember { mutableStateOf(0.0) }
|
val sum = remember { mutableStateOf(0.0) }
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
sum.value = AppDatabase.getInstance(context).coffeeDao().getSumInCart()
|
sum.value = AppDatabase.getInstance(context).cartDao().getSumInCart()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Column(Modifier.padding(all = 10.dp), horizontalAlignment = Alignment.CenterHorizontally) {
|
Column(Modifier.padding(all = 10.dp), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
|
@ -29,6 +29,7 @@ import androidx.compose.runtime.LaunchedEffect
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@ -39,12 +40,17 @@ import androidx.compose.ui.text.input.KeyboardType
|
|||||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
|
import com.zyzf.coffeepreorder.CoffeeApplication
|
||||||
import com.zyzf.coffeepreorder.R
|
import com.zyzf.coffeepreorder.R
|
||||||
import com.zyzf.coffeepreorder.database.AppDatabase
|
import com.zyzf.coffeepreorder.database.AppDatabase
|
||||||
import com.zyzf.coffeepreorder.database.model.User
|
import com.zyzf.coffeepreorder.database.model.User
|
||||||
|
import com.zyzf.coffeepreorder.ui.AppViewModelProvider
|
||||||
|
import com.zyzf.coffeepreorder.ui.login.LoginViewModel
|
||||||
import com.zyzf.coffeepreorder.ui.navigation.Screen
|
import com.zyzf.coffeepreorder.ui.navigation.Screen
|
||||||
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
|
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
@ -53,10 +59,14 @@ import kotlinx.coroutines.withContext
|
|||||||
|
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun Profile(navController: NavController?) {
|
fun Profile(
|
||||||
|
navController: NavController?,
|
||||||
|
viewModel: ProfileViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
||||||
|
) {
|
||||||
val openDialogEdit = remember { mutableStateOf(false) }
|
val openDialogEdit = remember { mutableStateOf(false) }
|
||||||
val openDialogExit = remember { mutableStateOf(false) }
|
val openDialogExit = remember { mutableStateOf(false) }
|
||||||
val openDialogDelete = remember { mutableStateOf(false) }
|
val openDialogDelete = remember { mutableStateOf(false) }
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
var user: User by remember { mutableStateOf(User("", "", "", "", "")) }
|
var user: User by remember { mutableStateOf(User("", "", "", "", "")) }
|
||||||
var userLogin by remember { mutableStateOf("") }
|
var userLogin by remember { mutableStateOf("") }
|
||||||
@ -67,7 +77,7 @@ fun Profile(navController: NavController?) {
|
|||||||
val userNewPsswdConf = remember { mutableStateOf("") }
|
val userNewPsswdConf = remember { mutableStateOf("") }
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
user = AppDatabase.getInstance(context).userDao().getLogined()!!
|
user = CoffeeApplication.currentUser!!
|
||||||
userLogin = user.login
|
userLogin = user.login
|
||||||
userFIO = user.fio
|
userFIO = user.fio
|
||||||
userPhone = user.phone
|
userPhone = user.phone
|
||||||
@ -192,11 +202,10 @@ fun Profile(navController: NavController?) {
|
|||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
GlobalScope.launch (Dispatchers.Main) {
|
coroutineScope.launch {
|
||||||
if (userOldPsswd.value == user.password && userNewPsswd.value == userNewPsswdConf.value) {
|
val user: User = viewModel.changeUser(user.password, userOldPsswd.value, userNewPsswd.value, userNewPsswd.value,
|
||||||
AppDatabase.getInstance(context).userDao().update(user.uid!!, userLogin, userFIO, userPhone, userNewPsswd.value)
|
user.uid, userLogin, userFIO, userPhone, user.role)
|
||||||
user = AppDatabase.getInstance(context).userDao().getLogined()!!
|
CoffeeApplication.currentUser = user
|
||||||
}
|
|
||||||
}
|
}
|
||||||
openDialogEdit.value = false
|
openDialogEdit.value = false
|
||||||
}
|
}
|
||||||
@ -233,7 +242,7 @@ fun Profile(navController: NavController?) {
|
|||||||
TextButton(
|
TextButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
GlobalScope.launch (Dispatchers.Main) {
|
GlobalScope.launch (Dispatchers.Main) {
|
||||||
AppDatabase.getInstance(context).userDao().logout()
|
CoffeeApplication.currentUser = null
|
||||||
}
|
}
|
||||||
openDialogExit.value = false
|
openDialogExit.value = false
|
||||||
navController?.navigate(Screen.Login.route)
|
navController?.navigate(Screen.Login.route)
|
||||||
@ -271,8 +280,8 @@ fun Profile(navController: NavController?) {
|
|||||||
TextButton(
|
TextButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
GlobalScope.launch (Dispatchers.Main) {
|
GlobalScope.launch (Dispatchers.Main) {
|
||||||
AppDatabase.getInstance(context).userDao().logout()
|
CoffeeApplication.currentUser = null
|
||||||
AppDatabase.getInstance(context).userDao().delete(user.uid!!)
|
AppDatabase.getInstance(context).userDao().delete(user)
|
||||||
}
|
}
|
||||||
openDialogDelete.value = false
|
openDialogDelete.value = false
|
||||||
navController?.navigate(Screen.Login.route)
|
navController?.navigate(Screen.Login.route)
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.zyzf.coffeepreorder.ui.profile
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.zyzf.coffeepreorder.CoffeeApplication
|
||||||
|
import com.zyzf.coffeepreorder.database.AppDatabase
|
||||||
|
import com.zyzf.coffeepreorder.database.model.User
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.UserRepository
|
||||||
|
|
||||||
|
class ProfileViewModel(
|
||||||
|
private val userRepository: UserRepository
|
||||||
|
) : ViewModel() {
|
||||||
|
suspend fun changeUser(currentUserPassw: String,
|
||||||
|
userOldPsswd: String,
|
||||||
|
userNewPsswd: String,
|
||||||
|
userNewPsswdConf: String,
|
||||||
|
userUid: Int,
|
||||||
|
userLogin: String,
|
||||||
|
userFIO: String,
|
||||||
|
userPhone: String,
|
||||||
|
userRole: String): User {
|
||||||
|
if (userOldPsswd == currentUserPassw && userNewPsswd == userNewPsswdConf) {
|
||||||
|
val userUid: Int? = userRepository.update(User(
|
||||||
|
userUid, userLogin, userFIO, userPhone, userNewPsswd, userRole))
|
||||||
|
} else {
|
||||||
|
val userUid: Int? = userRepository.update(User(
|
||||||
|
userUid, userLogin, userFIO, userPhone, currentUserPassw, userRole))
|
||||||
|
}
|
||||||
|
return userRepository.getByUid(userUid!!)!!
|
||||||
|
}
|
||||||
|
}
|
@ -22,6 +22,7 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@ -33,23 +34,26 @@ import androidx.compose.ui.text.input.KeyboardType
|
|||||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
|
import com.zyzf.coffeepreorder.CoffeeApplication
|
||||||
import com.zyzf.coffeepreorder.R
|
import com.zyzf.coffeepreorder.R
|
||||||
import com.zyzf.coffeepreorder.database.AppDatabase
|
|
||||||
import com.zyzf.coffeepreorder.database.model.User
|
import com.zyzf.coffeepreorder.database.model.User
|
||||||
|
import com.zyzf.coffeepreorder.ui.AppViewModelProvider
|
||||||
import com.zyzf.coffeepreorder.ui.navigation.Screen
|
import com.zyzf.coffeepreorder.ui.navigation.Screen
|
||||||
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
|
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Register(navController: NavController?) {
|
fun Register(
|
||||||
val context = LocalContext.current
|
navController: NavController?,
|
||||||
|
viewModel: RegisterViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
||||||
|
) {
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
var login: String by remember { mutableStateOf("") }
|
var login: String by remember { mutableStateOf("") }
|
||||||
var fio: String by remember { mutableStateOf("") }
|
var fio: String by remember { mutableStateOf("") }
|
||||||
var phone: String by remember { mutableStateOf("") }
|
var phone: String by remember { mutableStateOf("") }
|
||||||
@ -104,13 +108,12 @@ fun Register(navController: NavController?) {
|
|||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
var user: User?
|
var user: User?
|
||||||
GlobalScope.launch (Dispatchers.Main) {
|
coroutineScope.launch (Dispatchers.Main) {
|
||||||
if (password == confPassword) {
|
if (password == confPassword) {
|
||||||
AppDatabase.getInstance(context).userDao().insert(User(login, fio, phone, password, "user"))
|
user = viewModel.register(User(login, fio, phone, password, "user"))
|
||||||
user = AppDatabase.getInstance(context).userDao().tryLogin(login, password)
|
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
AppDatabase.getInstance(context).userDao().logout()
|
CoffeeApplication.currentUser = null
|
||||||
AppDatabase.getInstance(context).userDao().setLogined(user!!.uid!!)
|
CoffeeApplication.currentUser = user!!
|
||||||
navController?.navigate(Screen.CoffeeList.route)
|
navController?.navigate(Screen.CoffeeList.route)
|
||||||
} else {
|
} else {
|
||||||
password = ""
|
password = ""
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.zyzf.coffeepreorder.ui.register
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.zyzf.coffeepreorder.database.model.User
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.UserRepository
|
||||||
|
|
||||||
|
class RegisterViewModel(
|
||||||
|
private val userRepository: UserRepository
|
||||||
|
) : ViewModel() {
|
||||||
|
suspend fun register(user: User): User? {
|
||||||
|
userRepository.insert(user)
|
||||||
|
return userRepository.tryLogin(user.login, user.password)
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
Before Width: | Height: | Size: 30 KiB |
@ -1,25 +0,0 @@
|
|||||||
<!--
|
|
||||||
~ Copyright (C) 2023 The Android Open Source Project
|
|
||||||
~
|
|
||||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
~ you may not use this file except in compliance with the License.
|
|
||||||
~ You may obtain a copy of the License at
|
|
||||||
~
|
|
||||||
~ https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
~
|
|
||||||
~ Unless required by applicable law or agreed to in writing, software
|
|
||||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
~ See the License for the specific language governing permissions and
|
|
||||||
~ limitations under the License.
|
|
||||||
-->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="100dp"
|
|
||||||
android:height="100dp"
|
|
||||||
android:tint="#A9A9AC"
|
|
||||||
android:viewportWidth="24.0"
|
|
||||||
android:viewportHeight="24.0">
|
|
||||||
<path
|
|
||||||
android:fillColor="#A9A9AC"
|
|
||||||
android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4c-1.48,0 -2.85,0.43 -4.01,1.17l1.46,1.46C10.21,6.23 11.08,6 12,6c3.04,0 5.5,2.46 5.5,5.5v0.5H19c1.66,0 3,1.34 3,3 0,1.13 -0.64,2.11 -1.56,2.62l1.45,1.45C23.16,18.16 24,16.68 24,15c0,-2.64 -2.05,-4.78 -4.65,-4.96zM3,5.27l2.75,2.74C2.56,8.15 0,10.77 0,14c0,3.31 2.69,6 6,6h11.73l2,2L21,20.73 4.27,4 3,5.27zM7.73,10l8,8H6c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4h1.73z" />
|
|
||||||
</vector>
|
|
@ -1,10 +1,2 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources></resources>
|
||||||
<color name="purple_200">#FFBB86FC</color>
|
|
||||||
<color name="purple_500">#FF6200EE</color>
|
|
||||||
<color name="purple_700">#FF3700B3</color>
|
|
||||||
<color name="teal_200">#FF03DAC5</color>
|
|
||||||
<color name="teal_700">#FF018786</color>
|
|
||||||
<color name="black">#FF000000</color>
|
|
||||||
<color name="white">#FFFFFFFF</color>
|
|
||||||
</resources>
|
|
@ -8,7 +8,6 @@
|
|||||||
<string name="coffee_name">Название</string>
|
<string name="coffee_name">Название</string>
|
||||||
<string name="coffee_cost">Стоимость</string>
|
<string name="coffee_cost">Стоимость</string>
|
||||||
<string name="coffee_ingredients">Ингредиенты</string>
|
<string name="coffee_ingredients">Ингредиенты</string>
|
||||||
<string name="coffee_view_title">Изменить кофе</string>
|
|
||||||
|
|
||||||
<string name="profile_title">Профиль</string>
|
<string name="profile_title">Профиль</string>
|
||||||
<string name="profile_login_label">Логин</string>
|
<string name="profile_login_label">Логин</string>
|
||||||
@ -19,17 +18,4 @@
|
|||||||
<string name="profile_newpassw_label">Новый пароль</string>
|
<string name="profile_newpassw_label">Новый пароль</string>
|
||||||
<string name="profile_confpassw_label">Подтверждение пароля</string>
|
<string name="profile_confpassw_label">Подтверждение пароля</string>
|
||||||
|
|
||||||
<string name="student_firstname">Имя</string>
|
|
||||||
<string name="student_lastname">Фамилия</string>
|
|
||||||
<string name="student_group">Группа</string>
|
|
||||||
<string name="student_phone">Телефон</string>
|
|
||||||
<string name="student_email">e-mail</string>
|
|
||||||
<string name="student_main_title">Список студентов</string>
|
|
||||||
<string name="student_view_title">Профиль студента</string>
|
|
||||||
<string name="about_title">О нас</string>
|
|
||||||
<string name="about_text">
|
|
||||||
<p>Это текст <b>о нас</b>!</p>\n\n
|
|
||||||
<p>Здесь могла быть Ваша реклама!</p>\n\n
|
|
||||||
<p>Наш сайт <a href="https://ulstu.ru">ulstu.ru</a></p>
|
|
||||||
</string>
|
|
||||||
</resources>
|
</resources>
|
6
app/src/main/res/xml/network_security_config.xml
Normal file
6
app/src/main/res/xml/network_security_config.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<network-security-config>
|
||||||
|
<domain-config cleartextTrafficPermitted="true">
|
||||||
|
<domain includeSubdomains="true">192.168.0.100</domain>
|
||||||
|
</domain-config>
|
||||||
|
</network-security-config>
|
2
backend/README.md
Normal file
2
backend/README.md
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# IP_1_Kalyshev_Yan_PIbd-22
|
||||||
|
|
31
backend/build.gradle
Normal file
31
backend/build.gradle
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
id 'org.springframework.boot' version '3.2.0'
|
||||||
|
id 'io.spring.dependency-management' version '1.1.4'
|
||||||
|
}
|
||||||
|
|
||||||
|
group = 'com.kalyshev'
|
||||||
|
version = '0.0.1-SNAPSHOT'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-devtools'
|
||||||
|
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||||
|
implementation 'com.h2database:h2'
|
||||||
|
|
||||||
|
implementation 'org.hibernate.validator:hibernate-validator'
|
||||||
|
implementation 'org.projectlombok:lombok'
|
||||||
|
|
||||||
|
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named('test') {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
|
|
||||||
|
java.targetCompatibility = JavaVersion.VERSION_17
|
BIN
backend/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
backend/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
7
backend/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
7
backend/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
|
validateDistributionUrl=true
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
249
backend/gradlew
vendored
Executable file
249
backend/gradlew
vendored
Executable file
@ -0,0 +1,249 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright © 2015-2021 the original authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# Gradle start up script for POSIX generated by Gradle.
|
||||||
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
app_path=$0
|
||||||
|
|
||||||
|
# Need this for daisy-chained symlinks.
|
||||||
|
while
|
||||||
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
|
[ -h "$app_path" ]
|
||||||
|
do
|
||||||
|
ls=$( ls -ld "$app_path" )
|
||||||
|
link=${ls#*' -> '}
|
||||||
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# This is normally unused
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
APP_BASE_NAME=${0##*/}
|
||||||
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
|
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD=maximum
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "$( uname )" in #(
|
||||||
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
|
Darwin* ) darwin=true ;; #(
|
||||||
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
|
NONSTOP* ) nonstop=true ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
|
else
|
||||||
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD=java
|
||||||
|
if ! command -v java >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
|
case $MAX_FD in #(
|
||||||
|
max*)
|
||||||
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC2039,SC3045
|
||||||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
|
warn "Could not query maximum file descriptor limit"
|
||||||
|
esac
|
||||||
|
case $MAX_FD in #(
|
||||||
|
'' | soft) :;; #(
|
||||||
|
*)
|
||||||
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC2039,SC3045
|
||||||
|
ulimit -n "$MAX_FD" ||
|
||||||
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
|
# * args from the command line
|
||||||
|
# * the main class name
|
||||||
|
# * -classpath
|
||||||
|
# * -D...appname settings
|
||||||
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
|
||||||
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Collect all arguments for the java command:
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
|
# and any embedded shellness will be escaped.
|
||||||
|
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||||
|
# treated as '${Hostname}' itself on the command line.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Stop when "xargs" is not available.
|
||||||
|
if ! command -v xargs >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "xargs is not available"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
92
backend/gradlew.bat
vendored
Normal file
92
backend/gradlew.bat
vendored
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
|
@if "%DEBUG%"=="" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
|
@rem This is normally unused
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
|
exit /b %EXIT_CODE%
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
1
backend/settings.gradle
Normal file
1
backend/settings.gradle
Normal file
@ -0,0 +1 @@
|
|||||||
|
rootProject.name = 'yan'
|
24
backend/src/main/java/com/kalyshev/yan/WebConfiguration.java
Normal file
24
backend/src/main/java/com/kalyshev/yan/WebConfiguration.java
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package com.kalyshev.yan;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||||
|
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebMvc
|
||||||
|
@ComponentScan
|
||||||
|
public class WebConfiguration implements WebMvcConfigurer {
|
||||||
|
public static final String REST_API = "/api";
|
||||||
|
@Override
|
||||||
|
public void addViewControllers(ViewControllerRegistry registry) {
|
||||||
|
WebMvcConfigurer.super.addViewControllers(registry);
|
||||||
|
registry.addViewController("rest-test");
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void addCorsMappings(CorsRegistry registry) {
|
||||||
|
registry.addMapping("/**").allowedMethods("*");
|
||||||
|
}
|
||||||
|
}
|
11
backend/src/main/java/com/kalyshev/yan/YanApplication.java
Normal file
11
backend/src/main/java/com/kalyshev/yan/YanApplication.java
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package com.kalyshev.yan;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class YanApplication {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(YanApplication.class, args);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
package com.kalyshev.yan.coffee.controller;
|
||||||
|
|
||||||
|
import com.kalyshev.yan.WebConfiguration;
|
||||||
|
import com.kalyshev.yan.coffee.service.CoffeeService;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping(WebConfiguration.REST_API + "/coffee")
|
||||||
|
public class CoffeeController {
|
||||||
|
private final CoffeeService coffeeService;
|
||||||
|
public CoffeeController(CoffeeService coffeeService) {
|
||||||
|
this.coffeeService = coffeeService;
|
||||||
|
}
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public CoffeeDto getCoffee(@PathVariable Long id) {
|
||||||
|
return new CoffeeDto(coffeeService.findCoffee(id));
|
||||||
|
}
|
||||||
|
@GetMapping("/")
|
||||||
|
public List<CoffeeDto> getAllCoffees(
|
||||||
|
@RequestParam(value = "pageNo", defaultValue = "1", required = false) int pageNo,
|
||||||
|
@RequestParam(value = "pageSize", defaultValue = "10", required = false) int pageSize,
|
||||||
|
@RequestParam(value = "sortBy", defaultValue = "id", required = false) String sortBy,
|
||||||
|
@RequestParam(value = "sortDir", defaultValue = "asc", required = false) String sortDir
|
||||||
|
){
|
||||||
|
return coffeeService.findAllCoffees(pageNo-1, pageSize, sortBy, sortDir);
|
||||||
|
}
|
||||||
|
@PostMapping("/")
|
||||||
|
public CoffeeDto createCoffee(@RequestBody @Valid CoffeeDto coffeeDto) {
|
||||||
|
return new CoffeeDto(coffeeService.addCoffee(coffeeDto.getName(), coffeeDto.getCost(), coffeeDto.getIngredients()));
|
||||||
|
}
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public CoffeeDto updateCoffee(@PathVariable Long id,
|
||||||
|
@RequestBody @Valid CoffeeDto coffeeDto) {
|
||||||
|
return new CoffeeDto(coffeeService.updateCoffee(id, coffeeDto.getName(), coffeeDto.getCost(), coffeeDto.getIngredients()));
|
||||||
|
}
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public CoffeeDto deleteCoffee(@PathVariable Long id) {
|
||||||
|
return new CoffeeDto(coffeeService.deleteCoffee(id));
|
||||||
|
}
|
||||||
|
@DeleteMapping("/")
|
||||||
|
public void deleteAllCoffees() {
|
||||||
|
coffeeService.deleteAllCoffees();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package com.kalyshev.yan.coffee.controller;
|
||||||
|
|
||||||
|
import com.kalyshev.yan.coffee.model.Coffee;
|
||||||
|
|
||||||
|
public class CoffeeDto {
|
||||||
|
private Long id;
|
||||||
|
private String name;
|
||||||
|
private Double cost;
|
||||||
|
private String ingredients;
|
||||||
|
public CoffeeDto() {}
|
||||||
|
public CoffeeDto(Coffee coffee) {
|
||||||
|
this.id = coffee.getId();
|
||||||
|
this.name = coffee.getName();
|
||||||
|
this.cost = coffee.getCost();
|
||||||
|
this.ingredients = coffee.getIngredients();
|
||||||
|
}
|
||||||
|
public Long getId() {return id;}
|
||||||
|
public void setId(Long id) {this.id = id;}
|
||||||
|
public String getName() {return name;}
|
||||||
|
public void setName(String name) {this.name = name;}
|
||||||
|
public Double getCost() {return cost;}
|
||||||
|
public void setCost(Double cost) {this.cost = cost;}
|
||||||
|
public String getIngredients() {return ingredients;}
|
||||||
|
public void setIngredients(String ingredients) {this.ingredients = ingredients;}
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
package com.kalyshev.yan.coffee.model;
|
||||||
|
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "coffee")
|
||||||
|
public class Coffee {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private Long id;
|
||||||
|
private String name;
|
||||||
|
private Double cost;
|
||||||
|
private String ingredients;
|
||||||
|
public Coffee() {
|
||||||
|
}
|
||||||
|
public Coffee(String name, Double cost, String ingredients) {
|
||||||
|
this.name = name;
|
||||||
|
this.cost = cost;
|
||||||
|
this.ingredients = ingredients;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {return id;}
|
||||||
|
public String getName() {return name;}
|
||||||
|
public void setName(String name) {this.name = name;}
|
||||||
|
public Double getCost() {return cost;}
|
||||||
|
public void setCost(Double cost) {this.cost = cost;}
|
||||||
|
public String getIngredients() {return ingredients;}
|
||||||
|
public void setIngredients(String ingredients) {this.ingredients = ingredients;}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o)
|
||||||
|
return true;
|
||||||
|
if (!(o instanceof Coffee))
|
||||||
|
return false;
|
||||||
|
Coffee coffee = (Coffee) o;
|
||||||
|
return Objects.equals(id, coffee.id) &&
|
||||||
|
Objects.equals(this.name, coffee.name) &&
|
||||||
|
Objects.equals(this.cost, coffee.cost) &&
|
||||||
|
Objects.equals(this.ingredients, coffee.ingredients);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(id);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Coffee{" +
|
||||||
|
"id=" + id +
|
||||||
|
", name='" + name + '\'' +
|
||||||
|
", cost='" + cost + '\'' +
|
||||||
|
", ingredients='" + ingredients + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.kalyshev.yan.coffee.repository;
|
||||||
|
|
||||||
|
public class CoffeeNotFoundException extends RuntimeException {
|
||||||
|
public CoffeeNotFoundException(Long id) {
|
||||||
|
super(String.format("Coffee with id [%s] is not found", id));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.kalyshev.yan.coffee.repository;
|
||||||
|
|
||||||
|
import com.kalyshev.yan.coffee.model.Coffee;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
public interface CoffeeRepository extends JpaRepository<Coffee, Long> {
|
||||||
|
}
|
@ -0,0 +1,90 @@
|
|||||||
|
package com.kalyshev.yan.coffee.service;
|
||||||
|
|
||||||
|
import com.kalyshev.yan.coffee.controller.CoffeeDto;
|
||||||
|
import com.kalyshev.yan.coffee.model.Coffee;
|
||||||
|
import com.kalyshev.yan.coffee.repository.CoffeeNotFoundException;
|
||||||
|
import com.kalyshev.yan.coffee.repository.CoffeeRepository;
|
||||||
|
import com.kalyshev.yan.user.repository.UserNotFoundException;
|
||||||
|
import com.kalyshev.yan.util.validation.ValidatorUtil;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class CoffeeService {
|
||||||
|
private final CoffeeRepository coffeeRepository;
|
||||||
|
private final ValidatorUtil validatorUtil;
|
||||||
|
public CoffeeService(CoffeeRepository coffeeRepository,
|
||||||
|
ValidatorUtil validatorUtil) {
|
||||||
|
this.coffeeRepository = coffeeRepository;
|
||||||
|
this.validatorUtil = validatorUtil;
|
||||||
|
}
|
||||||
|
@Transactional
|
||||||
|
public Coffee addCoffee(String name, Double cost, String ingredients) {
|
||||||
|
if (!StringUtils.hasText(name)) {
|
||||||
|
throw new IllegalArgumentException("Coffee name is null or empty");
|
||||||
|
}
|
||||||
|
if (cost <= 0.0) {
|
||||||
|
throw new IllegalArgumentException("Coffee cost is null or empty or negative");
|
||||||
|
}
|
||||||
|
final Coffee coffee = new Coffee(name, cost, ingredients);
|
||||||
|
validatorUtil.validate(coffee);
|
||||||
|
return coffeeRepository.save(coffee);
|
||||||
|
}
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Coffee findCoffee(Long id) {
|
||||||
|
final Optional<Coffee> coffee = coffeeRepository.findById(id);
|
||||||
|
return coffee.orElseThrow(() -> new CoffeeNotFoundException(id));
|
||||||
|
}
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public List<CoffeeDto> findAllCoffees(int pageNo, int pageSize, String sortBy, String sortDir) {
|
||||||
|
Sort sort = sortDir.equalsIgnoreCase(Sort.Direction.ASC.name()) ? Sort.by(sortBy).ascending()
|
||||||
|
: Sort.by(sortBy).descending();
|
||||||
|
|
||||||
|
// create Pageable instance
|
||||||
|
Pageable pageable = PageRequest.of(pageNo, pageSize, sort);
|
||||||
|
|
||||||
|
Page<Coffee> coffees = coffeeRepository.findAll(pageable);
|
||||||
|
|
||||||
|
// get content for page object
|
||||||
|
List<Coffee> listOfCoffees = coffees.getContent();
|
||||||
|
|
||||||
|
List<CoffeeDto> content = listOfCoffees.stream()
|
||||||
|
.map(CoffeeDto::new)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
@Transactional
|
||||||
|
public Coffee updateCoffee(Long id, String name, Double cost, String ingredients) {
|
||||||
|
final Coffee currentCoffee = findCoffee(id);
|
||||||
|
if (StringUtils.hasText(name)) {
|
||||||
|
currentCoffee.setName(name);
|
||||||
|
}
|
||||||
|
if (cost > 0.0) {
|
||||||
|
currentCoffee.setCost(cost);
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(ingredients)) {
|
||||||
|
currentCoffee.setIngredients(ingredients);
|
||||||
|
}
|
||||||
|
validatorUtil.validate(currentCoffee);
|
||||||
|
return coffeeRepository.save(currentCoffee);
|
||||||
|
}
|
||||||
|
@Transactional
|
||||||
|
public Coffee deleteCoffee(Long id) {
|
||||||
|
final Coffee currentCoffee = findCoffee(id);
|
||||||
|
coffeeRepository.delete(currentCoffee);
|
||||||
|
return currentCoffee;
|
||||||
|
}
|
||||||
|
@Transactional
|
||||||
|
public void deleteAllCoffees() {
|
||||||
|
coffeeRepository.deleteAll();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
package com.kalyshev.yan.user.controller;
|
||||||
|
|
||||||
|
import com.kalyshev.yan.WebConfiguration;
|
||||||
|
import com.kalyshev.yan.user.model.User;
|
||||||
|
import com.kalyshev.yan.user.service.UserService;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping(WebConfiguration.REST_API + "/user")
|
||||||
|
public class UserController {
|
||||||
|
private final UserService userService;
|
||||||
|
public UserController(UserService userService) {
|
||||||
|
this.userService = userService;
|
||||||
|
}
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public UserDto getUser(@PathVariable Long id) {
|
||||||
|
return new UserDto(userService.findUser(id));
|
||||||
|
}
|
||||||
|
@GetMapping("/")
|
||||||
|
public List<UserDto> getAllUsers(
|
||||||
|
@RequestParam(value = "pageNo", defaultValue = "0", required = false) int pageNo,
|
||||||
|
@RequestParam(value = "pageSize", defaultValue = "10", required = false) int pageSize,
|
||||||
|
@RequestParam(value = "sortBy", defaultValue = "id", required = false) String sortBy,
|
||||||
|
@RequestParam(value = "sortDir", defaultValue = "asc", required = false) String sortDir
|
||||||
|
){
|
||||||
|
return userService.findAllUsers(pageNo, pageSize, sortBy, sortDir);
|
||||||
|
}
|
||||||
|
@GetMapping("/tryLogin")
|
||||||
|
public User tryLogin(
|
||||||
|
@RequestParam(value = "login") String login,
|
||||||
|
@RequestParam(value = "password") String password
|
||||||
|
){
|
||||||
|
return userService.tryLogin(login, password);
|
||||||
|
}
|
||||||
|
@PostMapping("/")
|
||||||
|
public UserDto createUser(@RequestBody @Valid UserDto userDto) {
|
||||||
|
return new UserDto(userService.addUser(userDto.getLogin(), userDto.getFio(), userDto.getPhone(), userDto.getPassword(), userDto.getRole()));
|
||||||
|
}
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public UserDto updateUser(@PathVariable Long id,
|
||||||
|
@RequestBody @Valid UserDto userDto) {
|
||||||
|
return new UserDto(userService.updateUser(id, userDto.getLogin(), userDto.getFio(), userDto.getPhone(), userDto.getPassword(), userDto.getRole()));
|
||||||
|
}
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public UserDto deleteUser(@PathVariable Long id) {
|
||||||
|
return new UserDto(userService.deleteUser(id));
|
||||||
|
}
|
||||||
|
@DeleteMapping("/")
|
||||||
|
public void deleteAllUsers() {
|
||||||
|
userService.deleteAllUsers();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
package com.kalyshev.yan.user.controller;
|
||||||
|
|
||||||
|
import com.kalyshev.yan.user.model.User;
|
||||||
|
|
||||||
|
public class UserDto {
|
||||||
|
private Long id;
|
||||||
|
private String login;
|
||||||
|
private String fio;
|
||||||
|
private String phone;
|
||||||
|
private String password;
|
||||||
|
private String role;
|
||||||
|
public UserDto() {}
|
||||||
|
public UserDto(User user) {
|
||||||
|
this.id = user.getId();
|
||||||
|
this.login = user.getLogin();
|
||||||
|
this.fio = user.getFio();
|
||||||
|
this.phone = user.getPhone();
|
||||||
|
this.password = user.getPassword();
|
||||||
|
this.role = user.getRole();
|
||||||
|
}
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
public String getLogin() {
|
||||||
|
return login;
|
||||||
|
}
|
||||||
|
public void setLogin(String login) {
|
||||||
|
this.login = login;
|
||||||
|
}
|
||||||
|
public String getFio() {
|
||||||
|
return fio;
|
||||||
|
}
|
||||||
|
public void setFio(String fio) {
|
||||||
|
this.fio = fio;
|
||||||
|
}
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
public String getPhone() {
|
||||||
|
return phone;
|
||||||
|
}
|
||||||
|
public void setPhone(String phone) {
|
||||||
|
this.phone = phone;
|
||||||
|
}
|
||||||
|
public String getRole() {
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
public void setRole(String role) {
|
||||||
|
this.role = role;
|
||||||
|
}
|
||||||
|
}
|
102
backend/src/main/java/com/kalyshev/yan/user/model/User.java
Normal file
102
backend/src/main/java/com/kalyshev/yan/user/model/User.java
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
package com.kalyshev.yan.user.model;
|
||||||
|
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "user_u")
|
||||||
|
public class User {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private Long id;
|
||||||
|
private String login;
|
||||||
|
private String fio;
|
||||||
|
private String phone;
|
||||||
|
private String password;
|
||||||
|
private String role;
|
||||||
|
public User() {
|
||||||
|
}
|
||||||
|
public User(String login, String fio, String phone, String password, String role) {
|
||||||
|
this.login = login;
|
||||||
|
this.fio = fio;
|
||||||
|
this.phone = phone;
|
||||||
|
this.password = password;
|
||||||
|
this.role = role;
|
||||||
|
}
|
||||||
|
public User(Long id, String login, String fio, String phone, String password, String role) {
|
||||||
|
this.id = id;
|
||||||
|
this.login = login;
|
||||||
|
this.fio = fio;
|
||||||
|
this.phone = phone;
|
||||||
|
this.password = password;
|
||||||
|
this.role = role;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
public String getLogin() {
|
||||||
|
return login;
|
||||||
|
}
|
||||||
|
public void setLogin(String login) {
|
||||||
|
this.login = login;
|
||||||
|
}
|
||||||
|
public String getFio() {
|
||||||
|
return fio;
|
||||||
|
}
|
||||||
|
public void setFio(String fio) {
|
||||||
|
this.fio = fio;
|
||||||
|
}
|
||||||
|
public String getPhone() {
|
||||||
|
return phone;
|
||||||
|
}
|
||||||
|
public void setPhone(String phone) {
|
||||||
|
this.phone = phone;
|
||||||
|
}
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
public String getRole() {
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
public void setRole(String role) {
|
||||||
|
this.role = role;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o)
|
||||||
|
return true;
|
||||||
|
if (!(o instanceof User))
|
||||||
|
return false;
|
||||||
|
User user = (User) o;
|
||||||
|
return Objects.equals(id, user.id) &&
|
||||||
|
Objects.equals(this.login, user.login) &&
|
||||||
|
Objects.equals(this.fio, user.fio) &&
|
||||||
|
Objects.equals(this.phone, user.phone) &&
|
||||||
|
Objects.equals(this.password, user.password) &&
|
||||||
|
Objects.equals(this.role, user.role);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(id);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "User{" +
|
||||||
|
"id=" + id +
|
||||||
|
", login='" + login + '\'' +
|
||||||
|
", fio='" + fio + '\'' +
|
||||||
|
", phone='" + phone + '\'' +
|
||||||
|
", password='" + password + '\'' +
|
||||||
|
", role='" + role + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.kalyshev.yan.user.repository;
|
||||||
|
|
||||||
|
public class UserNotFoundException extends RuntimeException {
|
||||||
|
public UserNotFoundException(Long id) {
|
||||||
|
super(String.format("User with id [%s] is not found", id));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.kalyshev.yan.user.repository;
|
||||||
|
|
||||||
|
import com.kalyshev.yan.user.model.User;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public interface UserRepository extends JpaRepository<User, Long> {
|
||||||
|
@Query(value = "select * from \"USER_U\" where \"LOGIN\" = :login and \"PASSWORD\" = :password", nativeQuery = true)
|
||||||
|
public Optional<User> tryLogin(@Param("login") String login,
|
||||||
|
@Param("password") String password);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,113 @@
|
|||||||
|
package com.kalyshev.yan.user.service;
|
||||||
|
|
||||||
|
import com.kalyshev.yan.coffee.repository.CoffeeNotFoundException;
|
||||||
|
import com.kalyshev.yan.user.controller.UserDto;
|
||||||
|
import com.kalyshev.yan.user.model.User;
|
||||||
|
import com.kalyshev.yan.user.repository.UserNotFoundException;
|
||||||
|
import com.kalyshev.yan.user.repository.UserRepository;
|
||||||
|
import com.kalyshev.yan.util.validation.ValidatorUtil;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class UserService {
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
private final ValidatorUtil validatorUtil;
|
||||||
|
public UserService(UserRepository userRepository,
|
||||||
|
ValidatorUtil validatorUtil) {
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
this.validatorUtil = validatorUtil;
|
||||||
|
}
|
||||||
|
@Transactional
|
||||||
|
public User addUser(String login, String fio, String phone, String password, String role) {
|
||||||
|
if (!StringUtils.hasText(login)) {
|
||||||
|
throw new IllegalArgumentException("User login is null or empty");
|
||||||
|
}
|
||||||
|
if (!StringUtils.hasText(fio)) {
|
||||||
|
throw new IllegalArgumentException("User fio is null or empty");
|
||||||
|
}
|
||||||
|
if (!StringUtils.hasText(phone)) {
|
||||||
|
throw new IllegalArgumentException("User phone is null or empty");
|
||||||
|
}
|
||||||
|
if (!StringUtils.hasText(password)) {
|
||||||
|
throw new IllegalArgumentException("User password is null or empty");
|
||||||
|
}
|
||||||
|
if (!StringUtils.hasText(role)) {
|
||||||
|
throw new IllegalArgumentException("User role is null or empty");
|
||||||
|
}
|
||||||
|
final User user = new User(login, fio, phone, password, role);
|
||||||
|
validatorUtil.validate(user);
|
||||||
|
return userRepository.save(user);
|
||||||
|
}
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public User findUser(Long id) {
|
||||||
|
final Optional<User> user = userRepository.findById(id);
|
||||||
|
return user.orElseThrow(() -> new UserNotFoundException(id));
|
||||||
|
}
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public User tryLogin(String login, String password) {
|
||||||
|
Optional<User> user = userRepository.tryLogin(login, password);
|
||||||
|
if (user.isPresent()) {
|
||||||
|
return user.orElseThrow((() -> new UserNotFoundException((long)0))) ;
|
||||||
|
} else {
|
||||||
|
return new User((long)0,"", "", "", "", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public List<UserDto> findAllUsers(int pageNo, int pageSize, String sortBy, String sortDir) {
|
||||||
|
Sort sort = sortDir.equalsIgnoreCase(Sort.Direction.ASC.name()) ? Sort.by(sortBy).ascending()
|
||||||
|
: Sort.by(sortBy).descending();
|
||||||
|
|
||||||
|
// create Pageable instance
|
||||||
|
Pageable pageable = PageRequest.of(pageNo, pageSize, sort);
|
||||||
|
|
||||||
|
Page<User> users = userRepository.findAll(pageable);
|
||||||
|
|
||||||
|
// get content for page object
|
||||||
|
List<User> listOfUsers = users.getContent();
|
||||||
|
|
||||||
|
return listOfUsers.stream()
|
||||||
|
.map(UserDto::new)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
@Transactional
|
||||||
|
public User updateUser(Long id, String login, String fio, String phone, String password, String role) {
|
||||||
|
final User currentUser = findUser(id);
|
||||||
|
if (StringUtils.hasText(login)) {
|
||||||
|
currentUser.setLogin(login);
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(fio)) {
|
||||||
|
currentUser.setFio(fio);
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(phone)) {
|
||||||
|
currentUser.setPhone(phone);
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(password)) {
|
||||||
|
currentUser.setPassword(password);
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(role)) {
|
||||||
|
currentUser.setRole(role);
|
||||||
|
}
|
||||||
|
validatorUtil.validate(currentUser);
|
||||||
|
return userRepository.save(currentUser);
|
||||||
|
}
|
||||||
|
@Transactional
|
||||||
|
public User deleteUser(Long id) {
|
||||||
|
final User currentUser = findUser(id);
|
||||||
|
userRepository.delete(currentUser);
|
||||||
|
return currentUser;
|
||||||
|
}
|
||||||
|
@Transactional
|
||||||
|
public void deleteAllUsers() {
|
||||||
|
userRepository.deleteAll();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package com.kalyshev.yan.util.error;
|
||||||
|
|
||||||
|
import com.kalyshev.yan.coffee.repository.CoffeeNotFoundException;
|
||||||
|
import com.kalyshev.yan.user.repository.UserNotFoundException;
|
||||||
|
import com.kalyshev.yan.util.validation.ValidationException;
|
||||||
|
import org.springframework.context.support.DefaultMessageSourceResolvable;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||||
|
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@ControllerAdvice(annotations = RestController.class)
|
||||||
|
public class AdviceController {
|
||||||
|
@ExceptionHandler({
|
||||||
|
CoffeeNotFoundException.class,
|
||||||
|
UserNotFoundException.class,
|
||||||
|
ValidationException.class
|
||||||
|
})
|
||||||
|
public ResponseEntity<Object> handleException(Throwable e) {
|
||||||
|
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||||
|
public ResponseEntity<Object> handleBindException(MethodArgumentNotValidException e) {
|
||||||
|
final ValidationException validationException = new ValidationException(
|
||||||
|
e.getBindingResult().getAllErrors().stream()
|
||||||
|
.map(DefaultMessageSourceResolvable::getDefaultMessage)
|
||||||
|
.collect(Collectors.toSet()));
|
||||||
|
return handleException(validationException);
|
||||||
|
}
|
||||||
|
@ExceptionHandler(Exception.class)
|
||||||
|
public ResponseEntity<Object> handleUnknownException(Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package com.kalyshev.yan.util.validation;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class ValidationException extends RuntimeException {
|
||||||
|
public <T> ValidationException(Set<String> errors) {
|
||||||
|
super(String.join("\n", errors));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package com.kalyshev.yan.util.validation;
|
||||||
|
|
||||||
|
import jakarta.validation.ConstraintViolation;
|
||||||
|
import jakarta.validation.Validation;
|
||||||
|
import jakarta.validation.Validator;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class ValidatorUtil {
|
||||||
|
private final Validator validator;
|
||||||
|
public ValidatorUtil() {
|
||||||
|
this.validator = Validation.buildDefaultValidatorFactory().getValidator();
|
||||||
|
}
|
||||||
|
public <T> void validate(T object) {
|
||||||
|
final Set<ConstraintViolation<T>> errors = validator.validate(object);
|
||||||
|
if (!errors.isEmpty()) {
|
||||||
|
throw new ValidationException(errors.stream()
|
||||||
|
.map(ConstraintViolation::getMessage)
|
||||||
|
.collect(Collectors.toSet()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
backend/src/main/resources/application.properties
Normal file
11
backend/src/main/resources/application.properties
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
spring.main.banner-mode=off
|
||||||
|
#server.port=8080
|
||||||
|
spring.datasource.url=jdbc:h2:file:./data
|
||||||
|
spring.datasource.driverClassName=org.h2.Driver
|
||||||
|
spring.datasource.username=sa
|
||||||
|
spring.datasource.password=password
|
||||||
|
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
|
||||||
|
spring.jpa.hibernate.ddl-auto=update
|
||||||
|
spring.h2.console.enabled=true
|
||||||
|
spring.h2.console.settings.trace=false
|
||||||
|
spring.h2.console.settings.web-allow-others=true
|
@ -1,6 +1,7 @@
|
|||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
plugins {
|
plugins {
|
||||||
id("com.android.application") version "8.2.0" apply false
|
id("com.android.application") version "8.2.0" apply false
|
||||||
id("org.jetbrains.kotlin.android") version "1.9.20" apply false
|
id("org.jetbrains.kotlin.android") version "1.9.10" apply false
|
||||||
id("com.google.devtools.ksp") version "1.9.20-1.0.14" apply false
|
id("com.google.devtools.ksp") version "1.9.10-1.0.13" apply false
|
||||||
|
id("org.jetbrains.kotlin.plugin.serialization") version "1.9.10" apply false
|
||||||
}
|
}
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
|||||||
#Sun Oct 15 15:51:04 GMT+04:00 2023
|
#Thu Dec 14 21:41:14 GMT+04:00 2023
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
BIN
Отчеты/Отчет мобилки 1.docx
Normal file
BIN
Отчеты/Отчет мобилки 1.docx
Normal file
Binary file not shown.
BIN
Отчеты/Отчет мобилки 2.docx
Normal file
BIN
Отчеты/Отчет мобилки 2.docx
Normal file
Binary file not shown.
BIN
Отчеты/Отчет мобилки 3.docx
Normal file
BIN
Отчеты/Отчет мобилки 3.docx
Normal file
Binary file not shown.
BIN
Отчеты/Отчет мобилки 4.docx
Normal file
BIN
Отчеты/Отчет мобилки 4.docx
Normal file
Binary file not shown.
BIN
Отчеты/Отчет мобилки 5.docx
Normal file
BIN
Отчеты/Отчет мобилки 5.docx
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user