started 4 lab
This commit is contained in:
parent
e3e4d1cacb
commit
889e80ad9e
@ -1,17 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="deploymentTargetDropDown">
|
<component name="deploymentTargetDropDown">
|
||||||
<targetSelectedWithDropDown>
|
<value>
|
||||||
<Target>
|
<entry key="app">
|
||||||
<type value="QUICK_BOOT_TARGET" />
|
<State />
|
||||||
<deviceKey>
|
</entry>
|
||||||
<Key>
|
</value>
|
||||||
<type value="VIRTUAL_DEVICE_PATH" />
|
|
||||||
<value value="$USER_HOME$/.android/avd/Pixel_3a_API_25.avd" />
|
|
||||||
</Key>
|
|
||||||
</deviceKey>
|
|
||||||
</Target>
|
|
||||||
</targetSelectedWithDropDown>
|
|
||||||
<timeTargetWasSelectedWithDropDown value="2023-11-16T12:43:19.045949464Z" />
|
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
@ -4,16 +4,15 @@
|
|||||||
<component name="GradleSettings">
|
<component name="GradleSettings">
|
||||||
<option name="linkedExternalProjectsSettings">
|
<option name="linkedExternalProjectsSettings">
|
||||||
<GradleProjectSettings>
|
<GradleProjectSettings>
|
||||||
<option name="testRunner" value="GRADLE" />
|
|
||||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
|
||||||
<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$" />
|
||||||
<option value="$PROJECT_DIR$/app" />
|
<option value="$PROJECT_DIR$/app" />
|
||||||
</set>
|
</set>
|
||||||
</option>
|
</option>
|
||||||
|
<option name="resolveExternalAnnotations" value="false" />
|
||||||
</GradleProjectSettings>
|
</GradleProjectSettings>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</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.10" />
|
<option name="version" value="1.9.20" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
10
.idea/migrations.xml
Normal file
10
.idea/migrations.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectMigrations">
|
||||||
|
<option name="MigrateToGradleLocalJavaHome">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -41,7 +41,7 @@ android {
|
|||||||
compose = true
|
compose = true
|
||||||
}
|
}
|
||||||
composeOptions {
|
composeOptions {
|
||||||
kotlinCompilerExtensionVersion = "1.5.3"
|
kotlinCompilerExtensionVersion = "1.5.5"
|
||||||
}
|
}
|
||||||
packaging {
|
packaging {
|
||||||
resources {
|
resources {
|
||||||
@ -63,34 +63,32 @@ dependencies {
|
|||||||
implementation("androidx.activity:activity-compose:1.8.1")
|
implementation("androidx.activity:activity-compose:1.8.1")
|
||||||
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.5")
|
||||||
implementation("androidx.compose.ui:ui:1.6.0-beta01")
|
implementation("androidx.compose.ui:ui:1.6.0-beta02")
|
||||||
implementation("androidx.compose.ui:ui-graphics:1.6.0-beta01")
|
implementation("androidx.compose.ui:ui-graphics:1.6.0-beta02")
|
||||||
implementation("androidx.compose.ui:ui-tooling-preview:1.6.0-beta01")
|
implementation("androidx.compose.ui:ui-tooling-preview:1.6.0-beta02")
|
||||||
implementation("androidx.compose.material3:material3:1.2.0-alpha11")
|
implementation("androidx.compose.material3:material3:1.2.0-alpha12")
|
||||||
|
|
||||||
// Retrofit
|
// Retrofit
|
||||||
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0")
|
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("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.1")
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
|
||||||
|
|
||||||
implementation("com.jcraft:jsch:0.1.55")
|
implementation("com.jcraft:jsch:0.1.55")
|
||||||
|
|
||||||
// Room
|
// Room
|
||||||
implementation("androidx.room:room-runtime:2.6.0")
|
implementation("androidx.room:room-runtime:2.6.1")
|
||||||
annotationProcessor("androidx.room:room-compiler:2.6.0")
|
annotationProcessor("androidx.room:room-compiler:2.6.1")
|
||||||
ksp("androidx.room:room-compiler:2.6.0")
|
ksp("androidx.room:room-compiler:2.6.1")
|
||||||
implementation("androidx.room:room-ktx:2.6.0")
|
implementation("androidx.room:room-ktx:2.6.1")
|
||||||
implementation("androidx.room:room-paging:2.6.0")
|
implementation("androidx.room:room-paging:2.6.1")
|
||||||
|
|
||||||
// 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-beta01")
|
androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.6.0-beta02")
|
||||||
debugImplementation("androidx.compose.ui:ui-tooling:1.6.0-beta01")
|
debugImplementation("androidx.compose.ui:ui-tooling:1.6.0-beta02")
|
||||||
debugImplementation("androidx.compose.ui:ui-test-manifest:1.6.0-beta01")
|
debugImplementation("androidx.compose.ui:ui-test-manifest:1.6.0-beta02")
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.zyzf.coffeepreorder
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import com.zyzf.coffeepreorder.database.AppContainer
|
||||||
|
import com.zyzf.coffeepreorder.database.AppDataContainer
|
||||||
|
|
||||||
|
class CoffeeApplication : Application() {
|
||||||
|
lateinit var container: AppContainer
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
container = AppDataContainer(this)
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,7 @@ import androidx.compose.material3.Surface
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import com.zyzf.coffeepreorder.composeui.navigation.MainNavbar
|
import com.zyzf.coffeepreorder.ui.navigation.MainNavbar
|
||||||
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
|
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
@ -1,390 +0,0 @@
|
|||||||
package com.zyzf.coffeepreorder.coffee.composeui
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.res.Configuration
|
|
||||||
import android.graphics.Bitmap.CompressFormat
|
|
||||||
import android.graphics.BitmapFactory
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.StrictMode
|
|
||||||
import android.os.StrictMode.ThreadPolicy
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
|
||||||
import androidx.activity.result.PickVisualMediaRequest
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
|
||||||
import androidx.compose.foundation.border
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.heightIn
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.lazy.items
|
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.filled.Add
|
|
||||||
import androidx.compose.material.icons.outlined.Create
|
|
||||||
import androidx.compose.material3.Button
|
|
||||||
import androidx.compose.material3.ButtonDefaults
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.FloatingActionButton
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.ModalBottomSheet
|
|
||||||
import androidx.compose.material3.OutlinedIconButton
|
|
||||||
import androidx.compose.material3.OutlinedTextField
|
|
||||||
import androidx.compose.material3.Surface
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableDoubleStateOf
|
|
||||||
import androidx.compose.runtime.mutableStateListOf
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.draw.clip
|
|
||||||
import androidx.compose.ui.layout.ContentScale
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.res.painterResource
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.compose.ui.unit.sp
|
|
||||||
import androidx.navigation.NavController
|
|
||||||
import coil.compose.AsyncImage
|
|
||||||
import coil.request.ImageRequest
|
|
||||||
import com.jcraft.jsch.Channel
|
|
||||||
import com.jcraft.jsch.ChannelSftp
|
|
||||||
import com.jcraft.jsch.JSch
|
|
||||||
import com.jcraft.jsch.JSchException
|
|
||||||
import com.jcraft.jsch.Session
|
|
||||||
import com.jcraft.jsch.SftpException
|
|
||||||
import com.zyzf.coffeepreorder.R
|
|
||||||
import com.zyzf.coffeepreorder.database.AppDatabase
|
|
||||||
import com.zyzf.coffeepreorder.database.model.Coffee
|
|
||||||
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
|
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import java.io.ByteArrayOutputStream
|
|
||||||
import java.io.File
|
|
||||||
import java.io.FileOutputStream
|
|
||||||
import java.util.Properties
|
|
||||||
|
|
||||||
|
|
||||||
@SuppressLint("UnrememberedMutableState")
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class,
|
|
||||||
DelicateCoroutinesApi::class
|
|
||||||
)
|
|
||||||
@Composable
|
|
||||||
fun CoffeeList(navController: NavController?) {
|
|
||||||
val openDialog = remember { mutableStateOf(false) }
|
|
||||||
val add = remember { mutableStateOf(false) }
|
|
||||||
val coffee = remember { mutableStateOf(Coffee("", 0.0, "", null, 0)) }
|
|
||||||
val context = LocalContext.current
|
|
||||||
val itemsList = remember { mutableStateListOf<Coffee>() }
|
|
||||||
|
|
||||||
var imageUri: Any? by remember { mutableStateOf(R.drawable.img) }
|
|
||||||
|
|
||||||
val photoPicker = rememberLauncherForActivityResult(
|
|
||||||
contract = ActivityResultContracts.PickVisualMedia()
|
|
||||||
) {
|
|
||||||
if (it != null) {
|
|
||||||
Log.d("PhotoPicker", "Selected URI: $it")
|
|
||||||
imageUri = it
|
|
||||||
} else {
|
|
||||||
Log.d("PhotoPicker", "No media selected")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
AppDatabase.getInstance(context).coffeeDao().getAll().collect { data ->
|
|
||||||
itemsList.clear()
|
|
||||||
itemsList.addAll(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LazyColumn(
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally) {
|
|
||||||
items(itemsList) { currentCoffee ->
|
|
||||||
Row(modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.heightIn(max = 140.dp)
|
|
||||||
.padding(bottom = 10.dp, top = 10.dp),
|
|
||||||
horizontalArrangement = Arrangement.SpaceAround) {
|
|
||||||
|
|
||||||
AsyncImage(
|
|
||||||
model = ImageRequest.Builder(context = LocalContext.current).data("https://zyzf.space/s/zXgFRTmbR4KMxMH/download?path=&files=coffee_image_" + currentCoffee.uid +".png")
|
|
||||||
.crossfade(true).build(),
|
|
||||||
error = painterResource(R.drawable.ic_broken_image),
|
|
||||||
placeholder = painterResource(R.drawable.loading_img),
|
|
||||||
contentDescription = "Кофе",
|
|
||||||
contentScale = ContentScale.Crop,
|
|
||||||
modifier = Modifier
|
|
||||||
.size(100.dp)
|
|
||||||
.clip(RoundedCornerShape(50.dp))
|
|
||||||
)
|
|
||||||
|
|
||||||
Column(
|
|
||||||
Modifier
|
|
||||||
.weight(2f)
|
|
||||||
.padding(start = 20.dp)) {
|
|
||||||
Text(text = currentCoffee.name, fontSize = 25.sp)
|
|
||||||
Text(text = String.format("%.2f", currentCoffee.cost), fontSize = 20.sp)
|
|
||||||
Text(text = currentCoffee.ingredients, fontSize = 15.sp)
|
|
||||||
Row(
|
|
||||||
Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(top = 5.dp)) {
|
|
||||||
Button(
|
|
||||||
onClick = {
|
|
||||||
GlobalScope.launch (Dispatchers.Main) {
|
|
||||||
currentCoffee.uid?.let {
|
|
||||||
AppDatabase.getInstance(context).cartDao().get().uid?.let { it1 ->
|
|
||||||
AppDatabase.getInstance(context).cartDao().insertCoffee(
|
|
||||||
it1,
|
|
||||||
it,
|
|
||||||
1
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AppDatabase.getInstance(context).coffeeDao().getAll().collect { data ->
|
|
||||||
itemsList.clear()
|
|
||||||
itemsList.addAll(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
shape = CircleShape,
|
|
||||||
modifier = Modifier.fillMaxWidth(fraction = 0.75f)
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.Add,
|
|
||||||
contentDescription = "Favorite",
|
|
||||||
modifier = Modifier.size(20.dp)
|
|
||||||
)
|
|
||||||
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
|
|
||||||
Text(text = "В корзину")
|
|
||||||
}
|
|
||||||
OutlinedIconButton(
|
|
||||||
onClick = {
|
|
||||||
coffee.value = currentCoffee
|
|
||||||
add.value = false
|
|
||||||
openDialog.value = true
|
|
||||||
},
|
|
||||||
Modifier
|
|
||||||
.padding(start = 10.dp)
|
|
||||||
.clip(CircleShape)) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Outlined.Create,
|
|
||||||
contentDescription = "Favorite",
|
|
||||||
modifier = Modifier.size(20.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
|
||||||
FloatingActionButton(onClick = { coffee.value = Coffee("", 0.0, "", null, 0); add.value = true; openDialog.value = true },
|
|
||||||
Modifier
|
|
||||||
.padding(all = 20.dp)
|
|
||||||
.align(alignment = Alignment.BottomEnd)) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Filled.Add,
|
|
||||||
contentDescription = "Add",
|
|
||||||
modifier = Modifier.size(20.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (openDialog.value) {
|
|
||||||
var name by remember { mutableStateOf(coffee.value.name) }
|
|
||||||
var cost by remember { mutableDoubleStateOf(coffee.value.cost) }
|
|
||||||
var ingredients by remember { mutableStateOf(coffee.value.ingredients) }
|
|
||||||
|
|
||||||
ModalBottomSheet(onDismissRequest = { openDialog.value = false }) {
|
|
||||||
Column(modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(all = 10.dp),
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally) {
|
|
||||||
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
|
|
||||||
value = name, onValueChange = {name = it},
|
|
||||||
label = {
|
|
||||||
Text(stringResource(id = R.string.coffee_name))
|
|
||||||
},
|
|
||||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
|
|
||||||
)
|
|
||||||
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
|
|
||||||
value = cost.toString(), onValueChange = {cost = it.toDouble()},
|
|
||||||
label = {
|
|
||||||
Text(stringResource(id = R.string.coffee_cost))
|
|
||||||
},
|
|
||||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal)
|
|
||||||
)
|
|
||||||
OutlinedTextField(modifier = Modifier.fillMaxWidth().padding(bottom = 10.dp),
|
|
||||||
value = ingredients, onValueChange = {ingredients = it},
|
|
||||||
label = {
|
|
||||||
Text(stringResource(id = R.string.coffee_ingredients))
|
|
||||||
},
|
|
||||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
|
|
||||||
)
|
|
||||||
AsyncImage(
|
|
||||||
modifier = Modifier
|
|
||||||
.size(100.dp).clip(RoundedCornerShape(50.dp)).border(2.dp, MaterialTheme.colorScheme.outline, shape = RoundedCornerShape(50.dp))
|
|
||||||
.clickable {
|
|
||||||
photoPicker.launch(
|
|
||||||
PickVisualMediaRequest(
|
|
||||||
ActivityResultContracts.PickVisualMedia.ImageOnly
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}.border(2.dp, MaterialTheme.colorScheme.outlineVariant, shape = RoundedCornerShape(50.dp)),
|
|
||||||
contentDescription = "Кофе",
|
|
||||||
error = painterResource(R.drawable.ic_broken_image),
|
|
||||||
placeholder = painterResource(R.drawable.loading_img),
|
|
||||||
contentScale = ContentScale.Crop,
|
|
||||||
model = ImageRequest.Builder(LocalContext.current)
|
|
||||||
.data(if (coffee.value.name != "") "https://zyzf.space/s/zXgFRTmbR4KMxMH/download?path=&files=coffee_image_" + coffee.value.uid +".png" else imageUri)
|
|
||||||
.crossfade(enable = true)
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
|
|
||||||
if (add.value) {
|
|
||||||
Button(onClick = {
|
|
||||||
GlobalScope.launch (Dispatchers.Main) {
|
|
||||||
val newCoffee: Long = AppDatabase.getInstance(context).coffeeDao().insert(
|
|
||||||
name = name, cost = cost, ingredients = ingredients
|
|
||||||
)
|
|
||||||
|
|
||||||
val inputStream = context.contentResolver.openInputStream(imageUri as Uri)
|
|
||||||
val bitmap = BitmapFactory.decodeStream(inputStream)
|
|
||||||
|
|
||||||
val f = File(context.cacheDir, "coffee_image_$newCoffee.png")
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
f.createNewFile()
|
|
||||||
val bos = ByteArrayOutputStream()
|
|
||||||
bitmap.compress(CompressFormat.PNG, 0 /*ignored for PNG*/, bos)
|
|
||||||
val bitmapdata = bos.toByteArray()
|
|
||||||
val fos = FileOutputStream(f)
|
|
||||||
fos.write(bitmapdata)
|
|
||||||
fos.flush()
|
|
||||||
fos.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
copyFileToSftp(f, "/mnt/nextcloud/data/Zyzf/files/Images")
|
|
||||||
|
|
||||||
AppDatabase.getInstance(context).coffeeDao().getAll().collect { data ->
|
|
||||||
itemsList.clear()
|
|
||||||
itemsList.addAll(data)
|
|
||||||
}
|
|
||||||
openDialog.value = false
|
|
||||||
}
|
|
||||||
}, modifier = Modifier.padding(0.dp, 10.dp, 0.dp, 30.dp)) {
|
|
||||||
Text("Добавить")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Button(onClick = {
|
|
||||||
GlobalScope.launch (Dispatchers.Main) {
|
|
||||||
AppDatabase.getInstance(context).coffeeDao().update(
|
|
||||||
Coffee(name = name, cost = cost, ingredients = ingredients, cartId = coffee.value.cartId, count = coffee.value.count)
|
|
||||||
)
|
|
||||||
AppDatabase.getInstance(context).coffeeDao().getAll().collect { data ->
|
|
||||||
itemsList.clear()
|
|
||||||
itemsList.addAll(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}, modifier = Modifier.padding(0.dp, 10.dp, 0.dp, 30.dp)) {
|
|
||||||
Text("Изменить")
|
|
||||||
}
|
|
||||||
Spacer(modifier = Modifier.padding(all = 20.dp))
|
|
||||||
Button(onClick = {
|
|
||||||
GlobalScope.launch (Dispatchers.Main) {
|
|
||||||
AppDatabase.getInstance(context).coffeeDao().delete(
|
|
||||||
coffee.value
|
|
||||||
)
|
|
||||||
AppDatabase.getInstance(context).coffeeDao().getAll().collect { data ->
|
|
||||||
itemsList.clear()
|
|
||||||
itemsList.addAll(data)
|
|
||||||
}
|
|
||||||
openDialog.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
}, modifier = Modifier.padding(0.dp, 10.dp, 0.dp, 30.dp)) {
|
|
||||||
Text("Удалить")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const val REMOTE_HOST = "109.197.199.134"
|
|
||||||
const val USERNAME = "zyzf"
|
|
||||||
const val PASSWORD = "250303Zyzf-d-grad"
|
|
||||||
const val REMOTE_PORT = 2223
|
|
||||||
|
|
||||||
fun copyFileToSftp(srcFile: File, ftpPath: String): Boolean {
|
|
||||||
|
|
||||||
var jschSession: Session? = null
|
|
||||||
|
|
||||||
try {
|
|
||||||
val jsch = JSch()
|
|
||||||
jsch.setKnownHosts("/home/mkyong/.ssh/known_hosts")
|
|
||||||
jschSession = jsch.getSession(USERNAME, REMOTE_HOST, REMOTE_PORT)
|
|
||||||
jschSession.setPassword(PASSWORD)
|
|
||||||
|
|
||||||
val config = Properties()
|
|
||||||
config["StrictHostKeyChecking"] = "no"
|
|
||||||
jschSession.setConfig(config)
|
|
||||||
|
|
||||||
val policy = ThreadPolicy.Builder().permitAll().build()
|
|
||||||
StrictMode.setThreadPolicy(policy)
|
|
||||||
jschSession.connect(10000)
|
|
||||||
val sftp: Channel = jschSession.openChannel("sftp")
|
|
||||||
|
|
||||||
sftp.connect(5000)
|
|
||||||
val channelSftp: ChannelSftp = sftp as ChannelSftp
|
|
||||||
|
|
||||||
channelSftp.put(srcFile.absolutePath, ftpPath)
|
|
||||||
channelSftp.exit()
|
|
||||||
|
|
||||||
} catch (e: JSchException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
return false
|
|
||||||
} catch (e: SftpException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
return false
|
|
||||||
} finally {
|
|
||||||
jschSession?.disconnect()
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
|
|
||||||
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
|
||||||
@Composable
|
|
||||||
fun CoffeeListPreview() {
|
|
||||||
CoffeePreorderTheme {
|
|
||||||
Surface(
|
|
||||||
color = MaterialTheme.colorScheme.background
|
|
||||||
) {
|
|
||||||
CoffeeList(navController = null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
package com.zyzf.coffeepreorder.coffee.composeui
|
|
||||||
|
|
||||||
import android.content.res.Configuration
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.material3.Button
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.ModalBottomSheet
|
|
||||||
import androidx.compose.material3.OutlinedTextField
|
|
||||||
import androidx.compose.material3.Surface
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.navigation.NavController
|
|
||||||
import com.zyzf.coffeepreorder.R
|
|
||||||
import com.zyzf.coffeepreorder.coffee.model.getCoffee
|
|
||||||
import com.zyzf.coffeepreorder.composeui.navigation.Screen
|
|
||||||
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
|
||||||
fun CoffeeView(navController: NavController?, id: Int) {
|
|
||||||
val coffee = getCoffee()[id]
|
|
||||||
ModalBottomSheet(onDismissRequest = { navController?.navigate(Screen.CoffeeList.route) }) {
|
|
||||||
Column(modifier = Modifier.fillMaxWidth().padding(all = 10.dp),
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally) {
|
|
||||||
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
|
|
||||||
value = coffee.name, onValueChange = {}, readOnly = true,
|
|
||||||
label = {
|
|
||||||
Text(stringResource(id = R.string.coffee_name))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
|
|
||||||
value = coffee.cost.toString(), onValueChange = {}, readOnly = true,
|
|
||||||
label = {
|
|
||||||
Text(stringResource(id = R.string.coffee_cost))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
|
|
||||||
value = coffee.ingredients, onValueChange = {}, readOnly = true,
|
|
||||||
label = {
|
|
||||||
Text(stringResource(id = R.string.coffee_ingredients))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
Row(modifier = Modifier.fillMaxWidth()) {
|
|
||||||
Button(onClick = { navController?.navigate(Screen.CoffeeView.route) }) {
|
|
||||||
Text("Изменить")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
|
|
||||||
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
|
||||||
@Composable
|
|
||||||
fun StudentViewPreview() {
|
|
||||||
CoffeePreorderTheme {
|
|
||||||
Surface(
|
|
||||||
color = MaterialTheme.colorScheme.background
|
|
||||||
) {
|
|
||||||
CoffeeView(navController = null, id = 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
package com.zyzf.coffeepreorder.coffee.model
|
|
||||||
|
|
||||||
import com.zyzf.coffeepreorder.R
|
|
||||||
import java.io.Serializable
|
|
||||||
|
|
||||||
data class Coffee(
|
|
||||||
val name: String,
|
|
||||||
val cost: Double,
|
|
||||||
val ingredients: String,
|
|
||||||
val image: Int
|
|
||||||
) : Serializable
|
|
||||||
|
|
||||||
fun getCoffee(): List<Coffee> {
|
|
||||||
return listOf(
|
|
||||||
Coffee("Coffee1", 200.0, "Ing1", R.drawable.coffee_image),
|
|
||||||
Coffee("Coffee2", 300.0, "Ing2", R.drawable.coffee_image),
|
|
||||||
Coffee("Coffee3", 500.0, "Ing3", R.drawable.coffee_image),
|
|
||||||
Coffee("Coffee3", 500.0, "Ing3", R.drawable.coffee_image),
|
|
||||||
Coffee("Coffee3", 500.0, "Ing3", R.drawable.coffee_image),
|
|
||||||
Coffee("Coffee3", 500.0, "Ing3", R.drawable.coffee_image),
|
|
||||||
Coffee("Coffee3", 500.0, "Ing3", R.drawable.coffee_image),
|
|
||||||
Coffee("Coffee3", 500.0, "Ing3", R.drawable.coffee_image),
|
|
||||||
Coffee("Coffee3", 500.0, "Ing3", R.drawable.coffee_image),
|
|
||||||
Coffee("Coffee3", 500.0, "Ing3", R.drawable.coffee_image),
|
|
||||||
Coffee("Coffee3", 500.0, "Ing3", R.drawable.coffee_image)
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,152 +0,0 @@
|
|||||||
package com.zyzf.coffeepreorder.composeui
|
|
||||||
|
|
||||||
import android.content.res.Configuration
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.filled.Add
|
|
||||||
import androidx.compose.material.icons.filled.Check
|
|
||||||
import androidx.compose.material3.Button
|
|
||||||
import androidx.compose.material3.ButtonDefaults
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.OutlinedTextField
|
|
||||||
import androidx.compose.material3.Surface
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.layout.ContentScale
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.res.painterResource
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
|
||||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.navigation.NavController
|
|
||||||
import coil.compose.AsyncImage
|
|
||||||
import coil.request.ImageRequest
|
|
||||||
import com.zyzf.coffeepreorder.R
|
|
||||||
import com.zyzf.coffeepreorder.composeui.navigation.Screen
|
|
||||||
import com.zyzf.coffeepreorder.database.AppDatabase
|
|
||||||
import com.zyzf.coffeepreorder.database.model.User
|
|
||||||
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
|
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
|
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
|
||||||
@Composable
|
|
||||||
fun Login(navController: NavController?) {
|
|
||||||
val context = LocalContext.current
|
|
||||||
var login by remember { mutableStateOf("") }
|
|
||||||
var password by remember { mutableStateOf("") }
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
AppDatabase.getInstance(context).userDao().getAll()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Column(Modifier.padding(all = 10.dp), horizontalAlignment = Alignment.CenterHorizontally) {
|
|
||||||
AsyncImage(
|
|
||||||
model = ImageRequest.Builder(context = LocalContext.current).data("https://zyzf.space/s/YsHjPo3NDmoptSk/download/coffee_image.png")
|
|
||||||
.crossfade(true).build(),
|
|
||||||
error = painterResource(R.drawable.ic_broken_image),
|
|
||||||
placeholder = painterResource(R.drawable.loading_img),
|
|
||||||
contentDescription = "Кофе",
|
|
||||||
contentScale = ContentScale.Crop,
|
|
||||||
modifier = Modifier.size(150.dp)
|
|
||||||
)
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.padding(all = 20.dp))
|
|
||||||
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
|
|
||||||
value = login, onValueChange = {login = it},
|
|
||||||
label = {
|
|
||||||
Text(stringResource(id = R.string.profile_login_label))
|
|
||||||
},
|
|
||||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
|
|
||||||
)
|
|
||||||
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
|
|
||||||
value = password, onValueChange = {password = it},
|
|
||||||
label = {
|
|
||||||
Text(stringResource(id = R.string.profile_passw_label))
|
|
||||||
},
|
|
||||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
|
|
||||||
visualTransformation = PasswordVisualTransformation()
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.padding(all = 20.dp))
|
|
||||||
Button(
|
|
||||||
onClick = {
|
|
||||||
var user: User?
|
|
||||||
GlobalScope.launch (Dispatchers.Main) {
|
|
||||||
user = AppDatabase.getInstance(context).userDao().tryLogin(login, password)
|
|
||||||
if (user != null) {
|
|
||||||
AppDatabase.getInstance(context).userDao().logout()
|
|
||||||
AppDatabase.getInstance(context).userDao().setLogined(user!!.uid!!)
|
|
||||||
navController?.navigate(Screen.CoffeeList.route)
|
|
||||||
} else {
|
|
||||||
password = ""
|
|
||||||
login = "Неверный логин или пароль"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
shape = CircleShape,
|
|
||||||
modifier = Modifier.fillMaxWidth(fraction = 0.75f),
|
|
||||||
colors = ButtonDefaults.buttonColors(
|
|
||||||
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
|
||||||
contentColor = MaterialTheme.colorScheme.primary
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.Check,
|
|
||||||
contentDescription = "Favorite",
|
|
||||||
modifier = Modifier.size(20.dp)
|
|
||||||
)
|
|
||||||
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
|
|
||||||
Text(text = "Войти")
|
|
||||||
}
|
|
||||||
Button(
|
|
||||||
onClick = { navController?.navigate(Screen.Register.route) },
|
|
||||||
shape = CircleShape,
|
|
||||||
modifier = Modifier.fillMaxWidth(fraction = 0.75f),
|
|
||||||
colors = ButtonDefaults.buttonColors(
|
|
||||||
containerColor = MaterialTheme.colorScheme.secondaryContainer,
|
|
||||||
contentColor = MaterialTheme.colorScheme.secondary
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
// Inner content including an icon and a text label
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.Add,
|
|
||||||
contentDescription = "Favorite",
|
|
||||||
modifier = Modifier.size(20.dp)
|
|
||||||
)
|
|
||||||
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
|
|
||||||
Text(text = "Зарегистрироваться")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
|
|
||||||
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
|
||||||
@Composable
|
|
||||||
fun LoginPreview() {
|
|
||||||
CoffeePreorderTheme {
|
|
||||||
Surface(
|
|
||||||
color = MaterialTheme.colorScheme.background
|
|
||||||
) {
|
|
||||||
Login(null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,31 @@
|
|||||||
|
package com.zyzf.coffeepreorder.database
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
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.OfflineCoffeeRepository
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.OfflineUserRepository
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.UserRepository
|
||||||
|
|
||||||
|
interface AppContainer {
|
||||||
|
val coffeeRepository: CoffeeRepository
|
||||||
|
val userRepository: UserRepository
|
||||||
|
val cartRepository: CartRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
class AppDataContainer(private val context: Context) : AppContainer {
|
||||||
|
override val coffeeRepository: CoffeeRepository by lazy {
|
||||||
|
OfflineCoffeeRepository(AppDatabase.getInstance(context).coffeeDao())
|
||||||
|
}
|
||||||
|
override val userRepository: UserRepository by lazy {
|
||||||
|
OfflineUserRepository(AppDatabase.getInstance(context).userDao())
|
||||||
|
}
|
||||||
|
override val cartRepository: CartRepository by lazy {
|
||||||
|
OfflineCartRepository(AppDatabase.getInstance(context).cartDao())
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TIMEOUT = 5000L
|
||||||
|
}
|
||||||
|
}
|
@ -32,7 +32,7 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
INSTANCE?.let { database ->
|
INSTANCE?.let { database ->
|
||||||
// Users
|
// Users
|
||||||
val userDao = database.userDao()
|
val userDao = database.userDao()
|
||||||
val user1 = User("zyzf", "Ян К.", "+79911152503", "250303zyzf")
|
val user1 = User("zyzf", "Ян К.", "+79911152503", "250303zyzf", "admin")
|
||||||
userDao.insert(user1)
|
userDao.insert(user1)
|
||||||
// Coffees
|
// Coffees
|
||||||
val coffeeDao = database.coffeeDao()
|
val coffeeDao = database.coffeeDao()
|
||||||
|
@ -3,7 +3,6 @@ package com.zyzf.coffeepreorder.database.dao
|
|||||||
import androidx.room.Dao
|
import androidx.room.Dao
|
||||||
import androidx.room.Delete
|
import androidx.room.Delete
|
||||||
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
|
import com.zyzf.coffeepreorder.database.model.CoffeeWithCart
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
@ -20,13 +19,13 @@ interface CoffeeDao {
|
|||||||
fun getSumInCart(): Double
|
fun getSumInCart(): Double
|
||||||
|
|
||||||
@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")
|
@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")
|
||||||
suspend fun getByUid(uid: Int): CoffeeWithCart
|
suspend fun getByUid(uid: Int): CoffeeWithCart?
|
||||||
|
|
||||||
@Query("insert into coffee (name, cost, ingredients, count) values (:name, :cost, :ingredients, 0)")
|
@Query("insert into coffee (name, cost, ingredients, count) values (:name, :cost, :ingredients, 0)")
|
||||||
suspend fun insert(name: String, cost: Double, ingredients: String): Long
|
suspend fun insert(name: String, cost: Double, ingredients: String): Long
|
||||||
|
|
||||||
@Update
|
@Query("update coffee set name = :name, cost = :cost, ingredients = :ingredients, cart_id = :cartId, count = :count where uid = :uid")
|
||||||
suspend fun update(coffee: Coffee)
|
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)
|
||||||
|
@ -12,16 +12,17 @@ interface UserDao {
|
|||||||
fun getAll(): Flow<List<User>>
|
fun getAll(): Flow<List<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?
|
||||||
|
|
||||||
@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 from user join user_logined on user_logined.user_id = user.uid limit 1")
|
@Query("select user.uid, login, fio, phone, password, role from user join user_logined on user_logined.user_id = user.uid limit 1")
|
||||||
suspend fun getLogined(): User
|
suspend fun getLogined(): User?
|
||||||
|
|
||||||
@Query("insert into user_logined (user_id) values (:userId)")
|
@Query("insert into user_logined (user_id) values (:userId)")
|
||||||
suspend fun setLogined(userId: Int)
|
suspend fun setLogined(userId: Int)
|
||||||
|
|
||||||
@Query("delete from user_logined")
|
@Query("delete from user_logined")
|
||||||
suspend fun logout()
|
suspend fun logout()
|
||||||
|
|
||||||
@ -29,7 +30,7 @@ interface UserDao {
|
|||||||
suspend fun insert(user: User)
|
suspend fun insert(user: User)
|
||||||
|
|
||||||
@Query("update user set login = :login, fio = :fio, phone = :phone, password = :password where uid = :uid")
|
@Query("update user set login = :login, fio = :fio, phone = :phone, password = :password where uid = :uid")
|
||||||
suspend fun update(uid: Int, login: String, fio: String, phone: String, password: String) : Int
|
suspend fun update(uid: Int, login: String, fio: String, phone: String, password: String) : Int?
|
||||||
|
|
||||||
@Query("delete from user where uid = :uid")
|
@Query("delete from user where uid = :uid")
|
||||||
suspend fun delete(uid: Int)
|
suspend fun delete(uid: Int)
|
||||||
|
@ -38,12 +38,24 @@ data class Coffee(
|
|||||||
count: Int?
|
count: Int?
|
||||||
) : this(null, name, cost, ingredients, null, 0)
|
) : this(null, name, cost, ingredients, null, 0)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun getCoffee(index: Int = 0): Coffee {
|
||||||
|
return Coffee(
|
||||||
|
index,
|
||||||
|
"",
|
||||||
|
0.0,
|
||||||
|
"",
|
||||||
|
null,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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 Coffee
|
other as Coffee
|
||||||
if (uid != other.uid) return false
|
return uid == other.uid
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
|
@ -16,22 +16,24 @@ data class User(
|
|||||||
@ColumnInfo(name = "phone")
|
@ColumnInfo(name = "phone")
|
||||||
var phone: String,
|
var phone: String,
|
||||||
@ColumnInfo(name = "password")
|
@ColumnInfo(name = "password")
|
||||||
var password: String
|
var password: String,
|
||||||
|
@ColumnInfo(name = "role")
|
||||||
|
var role: String
|
||||||
) {
|
) {
|
||||||
@Ignore
|
@Ignore
|
||||||
constructor(
|
constructor(
|
||||||
login: String,
|
login: String,
|
||||||
fio: String,
|
fio: String,
|
||||||
phone: String,
|
phone: String,
|
||||||
password: String
|
password: String,
|
||||||
) : this(null, login, fio, phone, password)
|
role: String
|
||||||
|
) : this(null, login, fio, phone, password, role)
|
||||||
|
|
||||||
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
|
||||||
if (uid != other.uid) return false
|
return uid == other.uid
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
package com.zyzf.coffeepreorder.database.repository
|
||||||
|
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Cart
|
||||||
|
|
||||||
|
interface CartRepository {
|
||||||
|
suspend fun get(): Cart
|
||||||
|
suspend fun insert(cart: Cart)
|
||||||
|
suspend fun insertCoffee(cartId: Int, coffeeId: Int, count: Int)
|
||||||
|
suspend fun deleteCoffee(coffeeId: Int, count: Int)
|
||||||
|
suspend fun update(cart: Cart)
|
||||||
|
suspend fun deleteAll()
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.zyzf.coffeepreorder.database.repository
|
||||||
|
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Coffee
|
||||||
|
import com.zyzf.coffeepreorder.database.model.CoffeeWithCart
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
interface CoffeeRepository {
|
||||||
|
fun getAll(): Flow<List<Coffee>>
|
||||||
|
fun getAllInCart(): Flow<List<Coffee>>
|
||||||
|
fun getSumInCart(): Double
|
||||||
|
suspend fun getByUid(uid: Int): CoffeeWithCart?
|
||||||
|
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)
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.zyzf.coffeepreorder.database.repository
|
||||||
|
|
||||||
|
import com.zyzf.coffeepreorder.database.dao.CartDao
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Cart
|
||||||
|
|
||||||
|
class OfflineCartRepository(private val cartDao: CartDao) : CartRepository {
|
||||||
|
override suspend fun get(): Cart = cartDao.get()
|
||||||
|
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 deleteCoffee(coffeeId: Int, count: Int) = cartDao.deleteCoffee(coffeeId, count)
|
||||||
|
override suspend fun update(cart: Cart) = cartDao.update(cart)
|
||||||
|
override suspend fun deleteAll() = cartDao.deleteAll()
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.zyzf.coffeepreorder.database.repository
|
||||||
|
|
||||||
|
import com.zyzf.coffeepreorder.database.dao.CoffeeDao
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Coffee
|
||||||
|
import com.zyzf.coffeepreorder.database.model.CoffeeWithCart
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
class OfflineCoffeeRepository(private val coffeeDao: CoffeeDao) : CoffeeRepository {
|
||||||
|
override fun getAll(): Flow<List<Coffee>> = coffeeDao.getAll()
|
||||||
|
override fun getAllInCart(): Flow<List<Coffee>> = coffeeDao.getAllInCart()
|
||||||
|
override fun getSumInCart(): Double = coffeeDao.getSumInCart()
|
||||||
|
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)
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.zyzf.coffeepreorder.database.repository
|
||||||
|
|
||||||
|
import com.zyzf.coffeepreorder.database.dao.UserDao
|
||||||
|
import com.zyzf.coffeepreorder.database.model.User
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
class OfflineUserRepository(private val userDao: UserDao) : UserRepository {
|
||||||
|
override fun getAll(): Flow<List<User>> = userDao.getAll()
|
||||||
|
override suspend fun tryLogin(login: String, password: String): User? = userDao.tryLogin(login, password)
|
||||||
|
override suspend fun getByUid(uid: Int): User? = userDao.getByUid(uid)
|
||||||
|
override suspend fun getLogined(): User? = userDao.getLogined()
|
||||||
|
override suspend fun setLogined(userId: Int) = userDao.setLogined(userId)
|
||||||
|
override suspend fun logout() = userDao.logout()
|
||||||
|
override suspend fun insert(user: User) = userDao.insert(user)
|
||||||
|
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 delete(uid: Int) = userDao.delete(uid)
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.zyzf.coffeepreorder.database.repository
|
||||||
|
|
||||||
|
import com.zyzf.coffeepreorder.database.model.User
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
interface UserRepository {
|
||||||
|
fun getAll(): Flow<List<User>>
|
||||||
|
suspend fun tryLogin(login: String, password: String): User?
|
||||||
|
suspend fun getByUid(uid: Int): User?
|
||||||
|
suspend fun getLogined(): User?
|
||||||
|
suspend fun setLogined(userId: Int)
|
||||||
|
suspend fun logout()
|
||||||
|
suspend fun insert(user: User)
|
||||||
|
suspend fun update(uid: Int, login: String, fio: String, phone: String, password: String) : Int?
|
||||||
|
suspend fun delete(uid: Int)
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.zyzf.coffeepreorder.ui
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.viewmodel.CreationExtras
|
||||||
|
import androidx.lifecycle.viewmodel.initializer
|
||||||
|
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||||
|
import com.zyzf.coffeepreorder.CoffeeApplication
|
||||||
|
import com.zyzf.coffeepreorder.ui.coffee.CoffeeListViewModel
|
||||||
|
import com.zyzf.coffeepreorder.ui.login.LoginViewModel
|
||||||
|
|
||||||
|
object AppViewModelProvider {
|
||||||
|
val Factory = viewModelFactory {
|
||||||
|
initializer {
|
||||||
|
CoffeeListViewModel(coffeeApplication().container.coffeeRepository, coffeeApplication().container.cartRepository)
|
||||||
|
}
|
||||||
|
initializer {
|
||||||
|
LoginViewModel(coffeeApplication().container.userRepository)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun CreationExtras.coffeeApplication(): CoffeeApplication =
|
||||||
|
(this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as CoffeeApplication)
|
@ -1,4 +1,4 @@
|
|||||||
package com.zyzf.coffeepreorder.composeui
|
package com.zyzf.coffeepreorder.ui.cart
|
||||||
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
@ -21,7 +21,6 @@ import androidx.compose.material.icons.filled.Clear
|
|||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.ExtendedFloatingActionButton
|
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@ -48,9 +47,9 @@ import androidx.navigation.NavController
|
|||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import com.zyzf.coffeepreorder.R
|
import com.zyzf.coffeepreorder.R
|
||||||
import com.zyzf.coffeepreorder.composeui.navigation.Screen
|
|
||||||
import com.zyzf.coffeepreorder.database.AppDatabase
|
import com.zyzf.coffeepreorder.database.AppDatabase
|
||||||
import com.zyzf.coffeepreorder.database.model.Coffee
|
import com.zyzf.coffeepreorder.database.model.Coffee
|
||||||
|
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
|
||||||
@ -58,7 +57,7 @@ import kotlinx.coroutines.GlobalScope
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class, DelicateCoroutinesApi::class)
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun Cart(navController: NavController?) {
|
fun Cart(navController: NavController?) {
|
||||||
val openDialog = remember { mutableStateOf(false) }
|
val openDialog = remember { mutableStateOf(false) }
|
@ -0,0 +1,362 @@
|
|||||||
|
package com.zyzf.coffeepreorder.ui.coffee
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.net.Uri
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.activity.compose.ManagedActivityResultLauncher
|
||||||
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
|
import androidx.activity.result.PickVisualMediaRequest
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.compose.foundation.border
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.heightIn
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Add
|
||||||
|
import androidx.compose.material.icons.outlined.Create
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.FloatingActionButton
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
|
import androidx.compose.material3.OutlinedIconButton
|
||||||
|
import androidx.compose.material3.OutlinedTextField
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.SheetState
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.rememberModalBottomSheetState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableDoubleStateOf
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import coil.compose.AsyncImage
|
||||||
|
import coil.request.ImageRequest
|
||||||
|
import com.zyzf.coffeepreorder.R
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Coffee
|
||||||
|
import com.zyzf.coffeepreorder.ui.AppViewModelProvider
|
||||||
|
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun CoffeeList(
|
||||||
|
viewModel: CoffeeListViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
||||||
|
) {
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val coffeeListUiState = viewModel.coffeeListUiState.collectAsState()
|
||||||
|
val sheetState = rememberModalBottomSheetState()
|
||||||
|
val openDialog = remember { mutableStateOf(false) }
|
||||||
|
val photoPicker = rememberLauncherForActivityResult(
|
||||||
|
contract = ActivityResultContracts.PickVisualMedia()
|
||||||
|
) {
|
||||||
|
if (it != null) {
|
||||||
|
Log.d("PhotoPicker", "Selected URI: $it")
|
||||||
|
viewModel.imageUri = it
|
||||||
|
} else {
|
||||||
|
Log.d("PhotoPicker", "No media selected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Scaffold(
|
||||||
|
topBar = {},
|
||||||
|
floatingActionButton = {
|
||||||
|
FloatingActionButton(
|
||||||
|
onClick = {
|
||||||
|
coroutineScope.launch {
|
||||||
|
viewModel.cleanCurrentCoffee()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Modifier
|
||||||
|
.padding(all = 20.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.Add,
|
||||||
|
contentDescription = "Add",
|
||||||
|
modifier = Modifier.size(20.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) { innerPadding ->
|
||||||
|
coffeeListUiState?.value?.coffeeList?.let {
|
||||||
|
CoffeeList(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(innerPadding)
|
||||||
|
.fillMaxSize(),
|
||||||
|
coffeeList = it,
|
||||||
|
onAddToCartClick = { coffeeUid: Int ->
|
||||||
|
coroutineScope.launch {
|
||||||
|
viewModel.addCoffeeToCart(coffeeUid = coffeeUid)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onEditClick = { coffee: Coffee ->
|
||||||
|
coroutineScope.launch {
|
||||||
|
viewModel.editCoffee(coffee = coffee)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
AddEditModalBottomSheet(
|
||||||
|
coffee = viewModel.currentCoffee,
|
||||||
|
sheetState = sheetState,
|
||||||
|
openDialog = openDialog,
|
||||||
|
onAddClick = { coffee: Coffee, context: Context ->
|
||||||
|
coroutineScope.launch {
|
||||||
|
viewModel.createCoffee(coffee, context)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onEditClick = { coffee: Coffee, context: Context ->
|
||||||
|
coroutineScope.launch {
|
||||||
|
viewModel.editCoffee(coffee, context)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDeleteClick = { coffee: Coffee ->
|
||||||
|
coroutineScope.launch {
|
||||||
|
viewModel.deleteCoffee(coffee)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
photoPicker = photoPicker,
|
||||||
|
imageUri = viewModel.imageUri
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
private fun AddEditModalBottomSheet(
|
||||||
|
coffee: Coffee,
|
||||||
|
sheetState: SheetState,
|
||||||
|
openDialog: MutableState<Boolean>,
|
||||||
|
onAddClick: (coffee: Coffee, context: Context) -> Unit,
|
||||||
|
onEditClick: (coffee: Coffee, context: Context) -> Unit,
|
||||||
|
onDeleteClick: (coffee: Coffee) -> Unit,
|
||||||
|
photoPicker: ManagedActivityResultLauncher<PickVisualMediaRequest, Uri?>,
|
||||||
|
imageUri: Any?,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
var name by remember { mutableStateOf(coffee.name) }
|
||||||
|
var cost by remember { mutableDoubleStateOf(coffee.cost) }
|
||||||
|
var ingredients by remember { mutableStateOf(coffee.ingredients) }
|
||||||
|
val context = LocalContext.current
|
||||||
|
if (openDialog.value) {
|
||||||
|
ModalBottomSheet(
|
||||||
|
onDismissRequest = { openDialog.value = false },
|
||||||
|
sheetState = sheetState
|
||||||
|
) {
|
||||||
|
Column(modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(all = 10.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
|
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
|
||||||
|
value = name, onValueChange = {name = it},
|
||||||
|
label = {
|
||||||
|
Text(stringResource(id = R.string.coffee_name))
|
||||||
|
},
|
||||||
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
|
||||||
|
)
|
||||||
|
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
|
||||||
|
value = cost.toString(), onValueChange = {cost = it.toDouble()},
|
||||||
|
label = {
|
||||||
|
Text(stringResource(id = R.string.coffee_cost))
|
||||||
|
},
|
||||||
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal)
|
||||||
|
)
|
||||||
|
OutlinedTextField(modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(bottom = 10.dp),
|
||||||
|
value = ingredients, onValueChange = {ingredients = it},
|
||||||
|
label = {
|
||||||
|
Text(stringResource(id = R.string.coffee_ingredients))
|
||||||
|
},
|
||||||
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
|
||||||
|
)
|
||||||
|
AsyncImage(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(100.dp)
|
||||||
|
.clip(RoundedCornerShape(50.dp))
|
||||||
|
.border(
|
||||||
|
2.dp,
|
||||||
|
MaterialTheme.colorScheme.outline,
|
||||||
|
shape = RoundedCornerShape(50.dp)
|
||||||
|
)
|
||||||
|
.clickable {
|
||||||
|
photoPicker.launch(
|
||||||
|
PickVisualMediaRequest(
|
||||||
|
ActivityResultContracts.PickVisualMedia.ImageOnly
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.border(
|
||||||
|
2.dp,
|
||||||
|
MaterialTheme.colorScheme.outlineVariant,
|
||||||
|
shape = RoundedCornerShape(50.dp)
|
||||||
|
),
|
||||||
|
contentDescription = "Кофе",
|
||||||
|
error = painterResource(R.drawable.ic_broken_image),
|
||||||
|
placeholder = painterResource(R.drawable.loading_img),
|
||||||
|
contentScale = ContentScale.Crop,
|
||||||
|
model = ImageRequest.Builder(LocalContext.current)
|
||||||
|
.data(if (coffee.name != "") "https://zyzf.space/s/zXgFRTmbR4KMxMH/download?path=&files=coffee_image_" + coffee.uid +".png" else imageUri)
|
||||||
|
.crossfade(enable = true)
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
|
||||||
|
if (coffee.uid == 0) {
|
||||||
|
Button(onClick = {
|
||||||
|
onAddClick(coffee, context)
|
||||||
|
openDialog.value = false
|
||||||
|
}, modifier = Modifier.padding(0.dp, 10.dp, 0.dp, 30.dp)) {
|
||||||
|
Text("Добавить")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Button(onClick = {
|
||||||
|
onEditClick(coffee, context)
|
||||||
|
openDialog.value = false
|
||||||
|
}, modifier = Modifier.padding(0.dp, 10.dp, 0.dp, 30.dp)) {
|
||||||
|
Text("Изменить")
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.padding(all = 20.dp))
|
||||||
|
Button(onClick = {
|
||||||
|
onDeleteClick(coffee)
|
||||||
|
openDialog.value = false
|
||||||
|
}, modifier = Modifier.padding(0.dp, 10.dp, 0.dp, 30.dp)) {
|
||||||
|
Text("Удалить")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun CoffeeList(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
coffeeList: List<Coffee>,
|
||||||
|
onAddToCartClick: (coffeeUid: Int) -> Unit,
|
||||||
|
onEditClick: (coffee: Coffee) -> Unit
|
||||||
|
) {
|
||||||
|
LazyColumn(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
|
items(items = coffeeList, key = { it.uid!! }) { coffee ->
|
||||||
|
CoffeeListItem(coffee = coffee, onAddToCartClick = onAddToCartClick, onEditClick = onEditClick)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun CoffeeListItem(
|
||||||
|
coffee: Coffee,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
onAddToCartClick: (coffeeUid: Int) -> Unit,
|
||||||
|
onEditClick: (coffee: Coffee) -> Unit
|
||||||
|
) {
|
||||||
|
Row(modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.heightIn(max = 140.dp)
|
||||||
|
.padding(bottom = 10.dp, top = 10.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceAround) {
|
||||||
|
|
||||||
|
AsyncImage(
|
||||||
|
model = ImageRequest.Builder(context = LocalContext.current).data("https://zyzf.space/s/zXgFRTmbR4KMxMH/download?path=&files=coffee_image_" + coffee.uid +".png")
|
||||||
|
.crossfade(true).build(),
|
||||||
|
error = painterResource(R.drawable.ic_broken_image),
|
||||||
|
placeholder = painterResource(R.drawable.loading_img),
|
||||||
|
contentDescription = "Кофе",
|
||||||
|
contentScale = ContentScale.Crop,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(100.dp)
|
||||||
|
.clip(RoundedCornerShape(50.dp))
|
||||||
|
)
|
||||||
|
|
||||||
|
Column(
|
||||||
|
Modifier
|
||||||
|
.weight(2f)
|
||||||
|
.padding(start = 20.dp)) {
|
||||||
|
Text(text = coffee.name, fontSize = 25.sp)
|
||||||
|
Text(text = String.format("%.2f", coffee.cost), fontSize = 20.sp)
|
||||||
|
Text(text = coffee.ingredients, fontSize = 15.sp)
|
||||||
|
Row(
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(top = 5.dp)) {
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
coffee.uid?.let { onAddToCartClick(it) }
|
||||||
|
},
|
||||||
|
shape = CircleShape,
|
||||||
|
modifier = Modifier.fillMaxWidth(fraction = 0.75f)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Add,
|
||||||
|
contentDescription = "Favorite",
|
||||||
|
modifier = Modifier.size(20.dp)
|
||||||
|
)
|
||||||
|
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
|
||||||
|
Text(text = "В корзину")
|
||||||
|
}
|
||||||
|
OutlinedIconButton(
|
||||||
|
onClick = {
|
||||||
|
onEditClick(coffee)
|
||||||
|
},
|
||||||
|
Modifier
|
||||||
|
.padding(start = 10.dp)
|
||||||
|
.clip(CircleShape)) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Create,
|
||||||
|
contentDescription = "Favorite",
|
||||||
|
modifier = Modifier.size(20.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||||
|
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
@Composable
|
||||||
|
fun CoffeeListPreview() {
|
||||||
|
CoffeePreorderTheme {
|
||||||
|
Surface(
|
||||||
|
color = MaterialTheme.colorScheme.background
|
||||||
|
) {
|
||||||
|
CoffeeList(
|
||||||
|
coffeeList = (1..20).map { i -> Coffee.getCoffee(i) },
|
||||||
|
onAddToCartClick = {},
|
||||||
|
onEditClick = {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,141 @@
|
|||||||
|
package com.zyzf.coffeepreorder.ui.coffee
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.StrictMode
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.jcraft.jsch.Channel
|
||||||
|
import com.jcraft.jsch.ChannelSftp
|
||||||
|
import com.jcraft.jsch.JSch
|
||||||
|
import com.jcraft.jsch.JSchException
|
||||||
|
import com.jcraft.jsch.Session
|
||||||
|
import com.jcraft.jsch.SftpException
|
||||||
|
import com.zyzf.coffeepreorder.R
|
||||||
|
import com.zyzf.coffeepreorder.database.AppDataContainer
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Cart
|
||||||
|
import com.zyzf.coffeepreorder.database.model.Coffee
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.CartRepository
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.CoffeeRepository
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.util.Properties
|
||||||
|
|
||||||
|
class CoffeeListViewModel(
|
||||||
|
private val coffeeRepository: CoffeeRepository,
|
||||||
|
private val cartRepository: CartRepository
|
||||||
|
) : ViewModel() {
|
||||||
|
val coffeeListUiState: StateFlow<CoffeeListUiState> = coffeeRepository.getAll().map {
|
||||||
|
CoffeeListUiState(it)
|
||||||
|
}.stateIn(
|
||||||
|
scope = viewModelScope,
|
||||||
|
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppDataContainer.TIMEOUT),
|
||||||
|
initialValue = CoffeeListUiState()
|
||||||
|
)
|
||||||
|
var currentCoffee: Coffee = Coffee.getCoffee()
|
||||||
|
var imageUri: Any? = R.drawable.img
|
||||||
|
|
||||||
|
suspend fun addCoffeeToCart(coffeeUid: Int) {
|
||||||
|
val cart: Cart = cartRepository.get()
|
||||||
|
cart.uid?.let { cartRepository.insertCoffee(it, coffeeUid, 1) }
|
||||||
|
}
|
||||||
|
fun cleanCurrentCoffee() {
|
||||||
|
currentCoffee = Coffee.getCoffee()
|
||||||
|
}
|
||||||
|
fun editCoffee(coffee: Coffee) {
|
||||||
|
currentCoffee = coffee
|
||||||
|
}
|
||||||
|
suspend fun createCoffee(coffee: Coffee, context: Context) {
|
||||||
|
val newCoffee: Long = coffeeRepository.insert(coffee.name, coffee.cost, coffee.ingredients)
|
||||||
|
val inputStream = context.contentResolver.openInputStream(imageUri as Uri)
|
||||||
|
val bitmap = BitmapFactory.decodeStream(inputStream)
|
||||||
|
|
||||||
|
val f = File(context.cacheDir, "coffee_image_$newCoffee.png")
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
f.createNewFile()
|
||||||
|
val bos = ByteArrayOutputStream()
|
||||||
|
bitmap.compress(Bitmap.CompressFormat.PNG, 0 /*ignored for PNG*/, bos)
|
||||||
|
val bitmapdata = bos.toByteArray()
|
||||||
|
val fos = FileOutputStream(f)
|
||||||
|
fos.write(bitmapdata)
|
||||||
|
fos.flush()
|
||||||
|
fos.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
copyFileToSftp(f, "/mnt/nextcloud/data/Zyzf/files/Images")
|
||||||
|
}
|
||||||
|
suspend fun editCoffee(coffee: Coffee, context: Context) {
|
||||||
|
val editedCoffee: Int = coffeeRepository.update(coffee.uid!!, coffee.name, coffee.cost, coffee.ingredients, coffee.cartId, coffee.count)!!
|
||||||
|
val inputStream = context.contentResolver.openInputStream(imageUri as Uri)
|
||||||
|
val bitmap = BitmapFactory.decodeStream(inputStream)
|
||||||
|
|
||||||
|
val f = File(context.cacheDir, "coffee_image_$editedCoffee.png")
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
f.createNewFile()
|
||||||
|
val bos = ByteArrayOutputStream()
|
||||||
|
bitmap.compress(Bitmap.CompressFormat.PNG, 0 /*ignored for PNG*/, bos)
|
||||||
|
val bitmapdata = bos.toByteArray()
|
||||||
|
val fos = FileOutputStream(f)
|
||||||
|
fos.write(bitmapdata)
|
||||||
|
fos.flush()
|
||||||
|
fos.close()
|
||||||
|
}
|
||||||
|
copyFileToSftp(f, "/mnt/nextcloud/data/Zyzf/files/Images")
|
||||||
|
}
|
||||||
|
suspend fun deleteCoffee(coffee: Coffee) {
|
||||||
|
coffeeRepository.delete(coffee)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class CoffeeListUiState(val coffeeList: List<Coffee> = listOf())
|
||||||
|
|
||||||
|
const val REMOTE_HOST = "109.197.199.134"
|
||||||
|
const val USERNAME = "zyzf"
|
||||||
|
const val PASSWORD = "250303Zyzf-d-grad"
|
||||||
|
const val REMOTE_PORT = 2223
|
||||||
|
|
||||||
|
fun copyFileToSftp(srcFile: File, ftpPath: String): Boolean {
|
||||||
|
|
||||||
|
var jschSession: Session? = null
|
||||||
|
|
||||||
|
try {
|
||||||
|
val jsch = JSch()
|
||||||
|
jsch.setKnownHosts("/home/zyzf/.ssh/known_hosts")
|
||||||
|
jschSession = jsch.getSession(USERNAME, REMOTE_HOST, REMOTE_PORT)
|
||||||
|
jschSession.setPassword(PASSWORD)
|
||||||
|
|
||||||
|
val config = Properties()
|
||||||
|
config["StrictHostKeyChecking"] = "no"
|
||||||
|
jschSession.setConfig(config)
|
||||||
|
|
||||||
|
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
|
||||||
|
StrictMode.setThreadPolicy(policy)
|
||||||
|
jschSession.connect(10000)
|
||||||
|
val sftp: Channel = jschSession.openChannel("sftp")
|
||||||
|
|
||||||
|
sftp.connect(5000)
|
||||||
|
val channelSftp: ChannelSftp = sftp as ChannelSftp
|
||||||
|
|
||||||
|
channelSftp.put(srcFile.absolutePath, ftpPath)
|
||||||
|
channelSftp.exit()
|
||||||
|
|
||||||
|
} catch (e: JSchException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
return false
|
||||||
|
} catch (e: SftpException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
return false
|
||||||
|
} finally {
|
||||||
|
jschSession?.disconnect()
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
153
app/src/main/java/com/zyzf/coffeepreorder/ui/login/Login.kt
Normal file
153
app/src/main/java/com/zyzf/coffeepreorder/ui/login/Login.kt
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
package com.zyzf.coffeepreorder.ui.login
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Add
|
||||||
|
import androidx.compose.material.icons.filled.Check
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.OutlinedTextField
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
|
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import coil.compose.AsyncImage
|
||||||
|
import coil.request.ImageRequest
|
||||||
|
import com.zyzf.coffeepreorder.R
|
||||||
|
import com.zyzf.coffeepreorder.ui.AppViewModelProvider
|
||||||
|
import com.zyzf.coffeepreorder.ui.navigation.Screen
|
||||||
|
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
|
||||||
|
@Composable
|
||||||
|
fun Login(
|
||||||
|
navController: NavController?,
|
||||||
|
viewModel: LoginViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
||||||
|
) {
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val userListUiState = viewModel.userListUiState.collectAsState()
|
||||||
|
var login by remember { mutableStateOf("") }
|
||||||
|
var password by remember { mutableStateOf("") }
|
||||||
|
Scaffold(
|
||||||
|
topBar = {},
|
||||||
|
floatingActionButton = {}
|
||||||
|
) {
|
||||||
|
Column(Modifier.padding(all = 10.dp), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
|
AsyncImage(
|
||||||
|
model = ImageRequest.Builder(context = LocalContext.current)
|
||||||
|
.data("https://zyzf.space/s/YsHjPo3NDmoptSk/download/coffee_image.png")
|
||||||
|
.crossfade(true).build(),
|
||||||
|
error = painterResource(R.drawable.ic_broken_image),
|
||||||
|
placeholder = painterResource(R.drawable.loading_img),
|
||||||
|
contentDescription = "Кофе",
|
||||||
|
contentScale = ContentScale.Crop,
|
||||||
|
modifier = Modifier.size(150.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.padding(all = 20.dp))
|
||||||
|
OutlinedTextField(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
value = login, onValueChange = { login = it },
|
||||||
|
label = {
|
||||||
|
Text(stringResource(id = R.string.profile_login_label))
|
||||||
|
},
|
||||||
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
|
||||||
|
)
|
||||||
|
OutlinedTextField(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
value = password, onValueChange = { password = it },
|
||||||
|
label = {
|
||||||
|
Text(stringResource(id = R.string.profile_passw_label))
|
||||||
|
},
|
||||||
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
|
||||||
|
visualTransformation = PasswordVisualTransformation()
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.padding(all = 20.dp))
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
coroutineScope.launch {
|
||||||
|
if (viewModel.tryLogin(login, password)) {
|
||||||
|
navController?.navigate(Screen.CoffeeList.route)
|
||||||
|
} else {
|
||||||
|
password = ""
|
||||||
|
login = "Неверный логин или пароль"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
shape = CircleShape,
|
||||||
|
modifier = Modifier.fillMaxWidth(fraction = 0.75f),
|
||||||
|
colors = ButtonDefaults.buttonColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||||
|
contentColor = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Check,
|
||||||
|
contentDescription = "Favorite",
|
||||||
|
modifier = Modifier.size(20.dp)
|
||||||
|
)
|
||||||
|
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
|
||||||
|
Text(text = "Войти")
|
||||||
|
}
|
||||||
|
Button(
|
||||||
|
onClick = { navController?.navigate(Screen.Register.route) },
|
||||||
|
shape = CircleShape,
|
||||||
|
modifier = Modifier.fillMaxWidth(fraction = 0.75f),
|
||||||
|
colors = ButtonDefaults.buttonColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.secondaryContainer,
|
||||||
|
contentColor = MaterialTheme.colorScheme.secondary
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Add,
|
||||||
|
contentDescription = "Favorite",
|
||||||
|
modifier = Modifier.size(20.dp)
|
||||||
|
)
|
||||||
|
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
|
||||||
|
Text(text = "Зарегистрироваться")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||||
|
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
@Composable
|
||||||
|
fun LoginPreview() {
|
||||||
|
CoffeePreorderTheme {
|
||||||
|
Surface(
|
||||||
|
color = MaterialTheme.colorScheme.background
|
||||||
|
) {
|
||||||
|
Login(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package com.zyzf.coffeepreorder.ui.login
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.zyzf.coffeepreorder.database.AppDataContainer
|
||||||
|
import com.zyzf.coffeepreorder.database.model.User
|
||||||
|
import com.zyzf.coffeepreorder.database.repository.UserRepository
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
|
||||||
|
class LoginViewModel(
|
||||||
|
private val userRepository: UserRepository
|
||||||
|
) : ViewModel() {
|
||||||
|
val userListUiState: StateFlow<UserListUiState> = userRepository.getAll().map {
|
||||||
|
UserListUiState(it)
|
||||||
|
}.stateIn(
|
||||||
|
scope = viewModelScope,
|
||||||
|
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppDataContainer.TIMEOUT),
|
||||||
|
initialValue = UserListUiState()
|
||||||
|
)
|
||||||
|
var user: User? = null
|
||||||
|
suspend fun tryLogin(login: String, password: String): Boolean {
|
||||||
|
user = userRepository.tryLogin(login, password)
|
||||||
|
return if (user != null) {
|
||||||
|
userRepository.logout()
|
||||||
|
userRepository.setLogined(user!!.uid!!)
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class UserListUiState(val userList: List<User> = listOf())
|
@ -1,10 +1,10 @@
|
|||||||
package com.zyzf.coffeepreorder.composeui.navigation
|
package com.zyzf.coffeepreorder.ui.navigation
|
||||||
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
@ -25,20 +25,17 @@ import androidx.navigation.NavDestination
|
|||||||
import androidx.navigation.NavDestination.Companion.hierarchy
|
import androidx.navigation.NavDestination.Companion.hierarchy
|
||||||
import androidx.navigation.NavGraph.Companion.findStartDestination
|
import androidx.navigation.NavGraph.Companion.findStartDestination
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.NavType
|
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import androidx.navigation.navArgument
|
|
||||||
import com.zyzf.coffeepreorder.R
|
import com.zyzf.coffeepreorder.R
|
||||||
import com.zyzf.coffeepreorder.coffee.composeui.CoffeeList
|
import com.zyzf.coffeepreorder.ui.cart.Cart
|
||||||
import com.zyzf.coffeepreorder.coffee.composeui.CoffeeView
|
import com.zyzf.coffeepreorder.ui.coffee.CoffeeList
|
||||||
import com.zyzf.coffeepreorder.composeui.Cart
|
import com.zyzf.coffeepreorder.ui.login.Login
|
||||||
import com.zyzf.coffeepreorder.composeui.Login
|
import com.zyzf.coffeepreorder.ui.order.Order
|
||||||
import com.zyzf.coffeepreorder.composeui.Order
|
import com.zyzf.coffeepreorder.ui.profile.Profile
|
||||||
import com.zyzf.coffeepreorder.composeui.Profile
|
import com.zyzf.coffeepreorder.ui.register.Register
|
||||||
import com.zyzf.coffeepreorder.composeui.Register
|
|
||||||
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
|
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@ -62,7 +59,7 @@ fun Topbar(
|
|||||||
) {
|
) {
|
||||||
IconButton(onClick = { navController.navigateUp() }) {
|
IconButton(onClick = { navController.navigateUp() }) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Filled.ArrowBack,
|
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
tint = MaterialTheme.colorScheme.onPrimary
|
tint = MaterialTheme.colorScheme.onPrimary
|
||||||
)
|
)
|
||||||
@ -111,16 +108,10 @@ fun Navhost(
|
|||||||
) {
|
) {
|
||||||
composable(Screen.Login.route) { Login(navController) }
|
composable(Screen.Login.route) { Login(navController) }
|
||||||
composable(Screen.Register.route) { Register(navController) }
|
composable(Screen.Register.route) { Register(navController) }
|
||||||
composable(Screen.CoffeeList.route) { CoffeeList(navController) }
|
composable(Screen.CoffeeList.route) { CoffeeList() }
|
||||||
composable(Screen.Profile.route) { Profile(navController) }
|
composable(Screen.Profile.route) { Profile(navController) }
|
||||||
composable(Screen.Cart.route) { Cart(navController) }
|
composable(Screen.Cart.route) { Cart(navController) }
|
||||||
composable(Screen.Order.route) { Order(navController) }
|
composable(Screen.Order.route) { Order(navController) }
|
||||||
composable(
|
|
||||||
Screen.CoffeeView.route,
|
|
||||||
arguments = listOf(navArgument("id") { type = NavType.IntType })
|
|
||||||
) { backStackEntry ->
|
|
||||||
backStackEntry.arguments?.let { CoffeeView(navController, it.getInt("id")) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package com.zyzf.coffeepreorder.composeui.navigation
|
package com.zyzf.coffeepreorder.ui.navigation
|
||||||
|
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
@ -46,7 +46,7 @@ enum class Screen(
|
|||||||
|
|
||||||
fun getItem(route: String): Screen? {
|
fun getItem(route: String): Screen? {
|
||||||
val findRoute = route.split("/").first()
|
val findRoute = route.split("/").first()
|
||||||
return values().find { value -> value.route.startsWith(findRoute) }
|
return entries.find { value -> value.route.startsWith(findRoute) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.zyzf.coffeepreorder.composeui
|
package com.zyzf.coffeepreorder.ui.order
|
||||||
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
@ -41,8 +41,8 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import com.zyzf.coffeepreorder.composeui.navigation.Screen
|
|
||||||
import com.zyzf.coffeepreorder.database.AppDatabase
|
import com.zyzf.coffeepreorder.database.AppDatabase
|
||||||
|
import com.zyzf.coffeepreorder.ui.navigation.Screen
|
||||||
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
|
import com.zyzf.coffeepreorder.ui.theme.CoffeePreorderTheme
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
@ -1,4 +1,4 @@
|
|||||||
package com.zyzf.coffeepreorder.composeui
|
package com.zyzf.coffeepreorder.ui.profile
|
||||||
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import androidx.compose.foundation.BorderStroke
|
import androidx.compose.foundation.BorderStroke
|
||||||
@ -41,9 +41,9 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import com.zyzf.coffeepreorder.R
|
import com.zyzf.coffeepreorder.R
|
||||||
import com.zyzf.coffeepreorder.composeui.navigation.Screen
|
|
||||||
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.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
|
||||||
@ -58,7 +58,7 @@ fun Profile(navController: NavController?) {
|
|||||||
val openDialogExit = remember { mutableStateOf(false) }
|
val openDialogExit = remember { mutableStateOf(false) }
|
||||||
val openDialogDelete = remember { mutableStateOf(false) }
|
val openDialogDelete = remember { mutableStateOf(false) }
|
||||||
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("") }
|
||||||
var userFIO by remember { mutableStateOf("") }
|
var userFIO by remember { mutableStateOf("") }
|
||||||
var userPhone by remember { mutableStateOf("") }
|
var userPhone by remember { mutableStateOf("") }
|
||||||
@ -67,7 +67,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 = AppDatabase.getInstance(context).userDao().getLogined()!!
|
||||||
userLogin = user.login
|
userLogin = user.login
|
||||||
userFIO = user.fio
|
userFIO = user.fio
|
||||||
userPhone = user.phone
|
userPhone = user.phone
|
||||||
@ -195,7 +195,7 @@ fun Profile(navController: NavController?) {
|
|||||||
GlobalScope.launch (Dispatchers.Main) {
|
GlobalScope.launch (Dispatchers.Main) {
|
||||||
if (userOldPsswd.value == user.password && userNewPsswd.value == userNewPsswdConf.value) {
|
if (userOldPsswd.value == user.password && userNewPsswd.value == userNewPsswdConf.value) {
|
||||||
AppDatabase.getInstance(context).userDao().update(user.uid!!, userLogin, userFIO, userPhone, userNewPsswd.value)
|
AppDatabase.getInstance(context).userDao().update(user.uid!!, userLogin, userFIO, userPhone, userNewPsswd.value)
|
||||||
user = AppDatabase.getInstance(context).userDao().getLogined()
|
user = AppDatabase.getInstance(context).userDao().getLogined()!!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
openDialogEdit.value = false
|
openDialogEdit.value = false
|
@ -1,4 +1,4 @@
|
|||||||
package com.zyzf.coffeepreorder.composeui
|
package com.zyzf.coffeepreorder.ui.register
|
||||||
|
|
||||||
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
@ -37,9 +37,9 @@ import androidx.navigation.NavController
|
|||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import com.zyzf.coffeepreorder.R
|
import com.zyzf.coffeepreorder.R
|
||||||
import com.zyzf.coffeepreorder.composeui.navigation.Screen
|
|
||||||
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.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
|
||||||
@ -106,7 +106,7 @@ fun Register(navController: NavController?) {
|
|||||||
var user: User?
|
var user: User?
|
||||||
GlobalScope.launch (Dispatchers.Main) {
|
GlobalScope.launch (Dispatchers.Main) {
|
||||||
if (password == confPassword) {
|
if (password == confPassword) {
|
||||||
AppDatabase.getInstance(context).userDao().insert(User(login, fio, phone, password))
|
AppDatabase.getInstance(context).userDao().insert(User(login, fio, phone, password, "user"))
|
||||||
user = AppDatabase.getInstance(context).userDao().tryLogin(login, password)
|
user = AppDatabase.getInstance(context).userDao().tryLogin(login, password)
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
AppDatabase.getInstance(context).userDao().logout()
|
AppDatabase.getInstance(context).userDao().logout()
|
@ -1,6 +1,6 @@
|
|||||||
// 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.1.4" apply false
|
id("com.android.application") version "8.2.0" apply false
|
||||||
id("org.jetbrains.kotlin.android") version "1.9.10" apply false
|
id("org.jetbrains.kotlin.android") version "1.9.20" apply false
|
||||||
id("com.google.devtools.ksp") version "1.9.10-1.0.13" apply false
|
id("com.google.devtools.ksp") version "1.9.20-1.0.14" apply false
|
||||||
}
|
}
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
|||||||
#Sun Oct 15 15:51:04 GMT+04:00 2023
|
#Sun Oct 15 15:51:04 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.0-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
Loading…
Reference in New Issue
Block a user