Improve the code

Signed-off-by: denis0001-dev <denis0001.dev@ya.ru>
This commit is contained in:
2025-09-07 20:29:32 +03:00
Unverified
parent 1c14053e96
commit aa99f415bf
6 changed files with 159 additions and 115 deletions
-18
View File
@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetSelector">
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-09-06T11:23:47.273402Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="LocalEmulator" identifier="path=/Users/denis0001-dev/.android/avd/Pixel_9_Pro_XL.avd" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState>
</selectionStates>
</component>
</project>
+2
View File
@@ -57,5 +57,7 @@
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PropertyName" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="UnusedSymbol" enabled="true" level="WEAK WARNING" enabled_by_default="true" editorAttributes="INFO_ATTRIBUTES" />
</profile>
</component>
@@ -0,0 +1,40 @@
package ru.fromchat.ui
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.dp
@Composable
fun RowHeader(
icon: ImageVector,
title: String,
subtitle: String
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Icon(
imageVector = icon,
contentDescription = null,
modifier = Modifier
.padding(bottom = 8.dp)
.size(30.dp),
)
Text(
text = title,
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(bottom = 8.dp)
)
Text(
text = subtitle,
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(bottom = 16.dp)
)
}
}
@@ -2,17 +2,15 @@ package ru.fromchat.ui.auth
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Login
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -28,30 +26,39 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import ru.fromchat.api.ApiClient
import ru.fromchat.api.LoginRequest
import ru.fromchat.ui.RowHeader
@Composable
fun LoginScreen(
onLoginSuccess: () -> Unit,
onNavigateToRegister: () -> Unit
) {
var username by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
var alert by remember { mutableStateOf<String?>(null) }
Scaffold { innerPadding ->
var username by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
var alert by remember { mutableStateOf<String?>(null) }
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Card(modifier = Modifier.padding(16.dp)) {
Column(modifier = Modifier.padding(16.dp)) {
RowHeader()
Column(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Column(
modifier = Modifier.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
RowHeader(
icon = Icons.AutoMirrored.Filled.Login,
title = "Welcome!",
subtitle = "Log in to your account"
)
if (alert != null) {
Text(text = alert!!, color = MaterialTheme.colorScheme.error)
Spacer(Modifier.height(8.dp))
}
OutlinedTextField(
@@ -60,7 +67,7 @@ fun LoginScreen(
label = { Text("Имя пользователя") },
singleLine = true
)
Spacer(Modifier.height(8.dp))
OutlinedTextField(
value = password,
onValueChange = { password = it },
@@ -69,35 +76,32 @@ fun LoginScreen(
visualTransformation = PasswordVisualTransformation()
)
Spacer(Modifier.height(12.dp))
Button(onClick = {
if (username.isBlank() || password.isBlank()) {
alert = "Пожалуйста, заполните все поля"
return@Button
}
CoroutineScope(Dispatchers.IO).launch {
try {
ApiClient.login(LoginRequest(username, password))
onLoginSuccess()
} catch (e: Exception) {
alert = "Неверное имя пользователя или пароль"
}
}
}) { Text("Войти") }
FlowRow(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
Button(
onClick = {
if (username.isBlank() || password.isBlank()) {
alert = "Пожалуйста, заполните все поля"
return@Button
}
Spacer(Modifier.height(8.dp))
Button(onClick = onNavigateToRegister) { Text("Зарегистрируйтесь") }
CoroutineScope(Dispatchers.IO).launch {
try {
ApiClient.login(LoginRequest(username, password))
onLoginSuccess()
} catch (e: Exception) {
alert = "Неверное имя пользователя или пароль"
}
}
}
) {
Text("Войти")
}
Button(onClick = onNavigateToRegister) {
Text("Зарегистрируйтесь")
}
}
}
}
}
}
@Composable
private fun RowHeader() {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Icon(Icons.AutoMirrored.Filled.Login, contentDescription = null)
Text("Добро пожаловать!")
Text("Войдите в свой аккаунт", style = MaterialTheme.typography.bodyMedium)
Spacer(Modifier.height(8.dp))
}
}
@@ -1,18 +1,16 @@
package ru.fromchat.ui.auth
import android.util.Log
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.PersonAdd
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -28,35 +26,39 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import ru.fromchat.api.ApiClient
import ru.fromchat.api.RegisterRequest
import ru.fromchat.ui.RowHeader
@Composable
fun RegisterScreen(
onRegistered: () -> Unit
) {
var username by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
var confirmPassword by remember { mutableStateOf("") }
var alert by remember { mutableStateOf<String?>(null) }
Scaffold { innerPadding ->
var username by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
var confirmPassword by remember { mutableStateOf("") }
var alert by remember { mutableStateOf<String?>(null) }
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Card(modifier = Modifier.padding(16.dp)) {
Column(modifier = Modifier.padding(16.dp)) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Icon(Icons.Default.PersonAdd, contentDescription = null)
Text("Регистрация")
Text("Создайте новый аккаунт", style = MaterialTheme.typography.bodyMedium)
Spacer(Modifier.height(8.dp))
}
Column(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Column(
modifier = Modifier.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
RowHeader(
icon = Icons.Filled.PersonAdd,
title = "Register",
subtitle = "Create a new account"
)
if (alert != null) {
Text(text = alert!!, color = MaterialTheme.colorScheme.error)
Spacer(Modifier.height(8.dp))
}
OutlinedTextField(
@@ -65,7 +67,7 @@ fun RegisterScreen(
label = { Text("Имя пользователя") },
singleLine = true
)
Spacer(Modifier.height(8.dp))
OutlinedTextField(
value = password,
onValueChange = { password = it },
@@ -73,7 +75,7 @@ fun RegisterScreen(
singleLine = true,
visualTransformation = PasswordVisualTransformation()
)
Spacer(Modifier.height(8.dp))
OutlinedTextField(
value = confirmPassword,
onValueChange = { confirmPassword = it },
@@ -82,33 +84,46 @@ fun RegisterScreen(
visualTransformation = PasswordVisualTransformation()
)
Spacer(Modifier.height(12.dp))
Button(onClick = {
if (username.isBlank() || password.isBlank() || confirmPassword.isBlank()) {
alert = "Пожалуйста, заполните все поля"
return@Button
}
if (password != confirmPassword) {
alert = "Пароли не совпадают"
return@Button
}
if (username.length !in 3..20) {
alert = "Имя пользователя должно быть от 3 до 20 символов"
return@Button
}
if (password.length !in 5..50) {
alert = "Пароль должен быть от 5 до 50 символов"
return@Button
}
CoroutineScope(Dispatchers.IO).launch {
try {
ApiClient.register(RegisterRequest(username, password, confirmPassword))
onRegistered()
} catch (e: Exception) {
alert = "Ошибка при регистрации"
Button(
onClick = {
// Checks
if (username.isBlank() || password.isBlank() || confirmPassword.isBlank()) {
alert = "Пожалуйста, заполните все поля"
return@Button
}
if (password != confirmPassword) {
alert = "Пароли не совпадают"
return@Button
}
if (username.length !in 3..20) {
alert = "Имя пользователя должно быть от 3 до 20 символов"
return@Button
}
if (password.length !in 5..50) {
alert = "Пароль должен быть от 5 до 50 символов"
return@Button
}
// Send the register request
CoroutineScope(Dispatchers.IO).launch {
try {
ApiClient.register(
RegisterRequest(
username,
password,
confirmPassword
)
)
onRegistered()
} catch (e: Exception) {
Log.d("RegisterScreen", "Error while registering:", e)
alert = "Ошибка при регистрации"
}
}
}
}) { Text("Зарегистрироваться") }
) {
Text("Зарегистрироваться")
}
}
}
}
@@ -67,6 +67,7 @@ fun BaseChat(
if (composing.isNotBlank()) {
// Note: server schema for public chat messages is not specified in the repo.
// We send a generic WebSocketMessage to match frontend type contract.
// TODO correct the payload
val payload = WebSocketMessage(
type = "public_chat_message",
data = buildJsonObject {