mirror of
https://github.com/fromchat-messenger/app.git
synced 2026-09-22 19:15:05 +03:00
Generated
-18
@@ -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
@@ -57,5 +57,7 @@
|
|||||||
<option name="composableFile" value="true" />
|
<option name="composableFile" value="true" />
|
||||||
<option name="previewFile" value="true" />
|
<option name="previewFile" value="true" />
|
||||||
</inspection_tool>
|
</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>
|
</profile>
|
||||||
</component>
|
</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.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
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.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.height
|
|
||||||
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.automirrored.filled.Login
|
import androidx.compose.material.icons.automirrored.filled.Login
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.Card
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -28,30 +26,39 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import ru.fromchat.api.ApiClient
|
import ru.fromchat.api.ApiClient
|
||||||
import ru.fromchat.api.LoginRequest
|
import ru.fromchat.api.LoginRequest
|
||||||
|
import ru.fromchat.ui.RowHeader
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LoginScreen(
|
fun LoginScreen(
|
||||||
onLoginSuccess: () -> Unit,
|
onLoginSuccess: () -> Unit,
|
||||||
onNavigateToRegister: () -> Unit
|
onNavigateToRegister: () -> Unit
|
||||||
) {
|
) {
|
||||||
var username by remember { mutableStateOf("") }
|
Scaffold { innerPadding ->
|
||||||
var password by remember { mutableStateOf("") }
|
var username by remember { mutableStateOf("") }
|
||||||
var alert by remember { mutableStateOf<String?>(null) }
|
var password by remember { mutableStateOf("") }
|
||||||
|
var alert by remember { mutableStateOf<String?>(null) }
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(16.dp),
|
.padding(innerPadding)
|
||||||
verticalArrangement = Arrangement.Center,
|
.padding(16.dp),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
verticalArrangement = Arrangement.Center,
|
||||||
) {
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
Card(modifier = Modifier.padding(16.dp)) {
|
) {
|
||||||
Column(modifier = Modifier.padding(16.dp)) {
|
Column(
|
||||||
RowHeader()
|
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) {
|
if (alert != null) {
|
||||||
Text(text = alert!!, color = MaterialTheme.colorScheme.error)
|
Text(text = alert!!, color = MaterialTheme.colorScheme.error)
|
||||||
Spacer(Modifier.height(8.dp))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
@@ -60,7 +67,7 @@ fun LoginScreen(
|
|||||||
label = { Text("Имя пользователя") },
|
label = { Text("Имя пользователя") },
|
||||||
singleLine = true
|
singleLine = true
|
||||||
)
|
)
|
||||||
Spacer(Modifier.height(8.dp))
|
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = password,
|
value = password,
|
||||||
onValueChange = { password = it },
|
onValueChange = { password = it },
|
||||||
@@ -69,35 +76,32 @@ fun LoginScreen(
|
|||||||
visualTransformation = PasswordVisualTransformation()
|
visualTransformation = PasswordVisualTransformation()
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(Modifier.height(12.dp))
|
FlowRow(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||||
Button(onClick = {
|
Button(
|
||||||
if (username.isBlank() || password.isBlank()) {
|
onClick = {
|
||||||
alert = "Пожалуйста, заполните все поля"
|
if (username.isBlank() || password.isBlank()) {
|
||||||
return@Button
|
alert = "Пожалуйста, заполните все поля"
|
||||||
}
|
return@Button
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
}
|
||||||
try {
|
|
||||||
ApiClient.login(LoginRequest(username, password))
|
|
||||||
onLoginSuccess()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
alert = "Неверное имя пользователя или пароль"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}) { Text("Войти") }
|
|
||||||
|
|
||||||
Spacer(Modifier.height(8.dp))
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
Button(onClick = onNavigateToRegister) { Text("Зарегистрируйтесь") }
|
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
|
package ru.fromchat.ui.auth
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.height
|
|
||||||
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.PersonAdd
|
import androidx.compose.material.icons.filled.PersonAdd
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.Card
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -28,35 +26,39 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import ru.fromchat.api.ApiClient
|
import ru.fromchat.api.ApiClient
|
||||||
import ru.fromchat.api.RegisterRequest
|
import ru.fromchat.api.RegisterRequest
|
||||||
|
import ru.fromchat.ui.RowHeader
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RegisterScreen(
|
fun RegisterScreen(
|
||||||
onRegistered: () -> Unit
|
onRegistered: () -> Unit
|
||||||
) {
|
) {
|
||||||
var username by remember { mutableStateOf("") }
|
Scaffold { innerPadding ->
|
||||||
var password by remember { mutableStateOf("") }
|
var username by remember { mutableStateOf("") }
|
||||||
var confirmPassword by remember { mutableStateOf("") }
|
var password by remember { mutableStateOf("") }
|
||||||
var alert by remember { mutableStateOf<String?>(null) }
|
var confirmPassword by remember { mutableStateOf("") }
|
||||||
|
var alert by remember { mutableStateOf<String?>(null) }
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(16.dp),
|
.padding(innerPadding)
|
||||||
verticalArrangement = Arrangement.Center,
|
.padding(16.dp),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
verticalArrangement = Arrangement.Center,
|
||||||
) {
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
Card(modifier = Modifier.padding(16.dp)) {
|
) {
|
||||||
Column(modifier = Modifier.padding(16.dp)) {
|
Column(
|
||||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
modifier = Modifier.padding(16.dp),
|
||||||
Icon(Icons.Default.PersonAdd, contentDescription = null)
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
Text("Регистрация")
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
Text("Создайте новый аккаунт", style = MaterialTheme.typography.bodyMedium)
|
) {
|
||||||
Spacer(Modifier.height(8.dp))
|
RowHeader(
|
||||||
}
|
icon = Icons.Filled.PersonAdd,
|
||||||
|
title = "Register",
|
||||||
|
subtitle = "Create a new account"
|
||||||
|
)
|
||||||
|
|
||||||
if (alert != null) {
|
if (alert != null) {
|
||||||
Text(text = alert!!, color = MaterialTheme.colorScheme.error)
|
Text(text = alert!!, color = MaterialTheme.colorScheme.error)
|
||||||
Spacer(Modifier.height(8.dp))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
@@ -65,7 +67,7 @@ fun RegisterScreen(
|
|||||||
label = { Text("Имя пользователя") },
|
label = { Text("Имя пользователя") },
|
||||||
singleLine = true
|
singleLine = true
|
||||||
)
|
)
|
||||||
Spacer(Modifier.height(8.dp))
|
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = password,
|
value = password,
|
||||||
onValueChange = { password = it },
|
onValueChange = { password = it },
|
||||||
@@ -73,7 +75,7 @@ fun RegisterScreen(
|
|||||||
singleLine = true,
|
singleLine = true,
|
||||||
visualTransformation = PasswordVisualTransformation()
|
visualTransformation = PasswordVisualTransformation()
|
||||||
)
|
)
|
||||||
Spacer(Modifier.height(8.dp))
|
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = confirmPassword,
|
value = confirmPassword,
|
||||||
onValueChange = { confirmPassword = it },
|
onValueChange = { confirmPassword = it },
|
||||||
@@ -82,33 +84,46 @@ fun RegisterScreen(
|
|||||||
visualTransformation = PasswordVisualTransformation()
|
visualTransformation = PasswordVisualTransformation()
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(Modifier.height(12.dp))
|
Button(
|
||||||
Button(onClick = {
|
onClick = {
|
||||||
if (username.isBlank() || password.isBlank() || confirmPassword.isBlank()) {
|
// Checks
|
||||||
alert = "Пожалуйста, заполните все поля"
|
if (username.isBlank() || password.isBlank() || confirmPassword.isBlank()) {
|
||||||
return@Button
|
alert = "Пожалуйста, заполните все поля"
|
||||||
}
|
return@Button
|
||||||
if (password != confirmPassword) {
|
}
|
||||||
alert = "Пароли не совпадают"
|
if (password != confirmPassword) {
|
||||||
return@Button
|
alert = "Пароли не совпадают"
|
||||||
}
|
return@Button
|
||||||
if (username.length !in 3..20) {
|
}
|
||||||
alert = "Имя пользователя должно быть от 3 до 20 символов"
|
if (username.length !in 3..20) {
|
||||||
return@Button
|
alert = "Имя пользователя должно быть от 3 до 20 символов"
|
||||||
}
|
return@Button
|
||||||
if (password.length !in 5..50) {
|
}
|
||||||
alert = "Пароль должен быть от 5 до 50 символов"
|
if (password.length !in 5..50) {
|
||||||
return@Button
|
alert = "Пароль должен быть от 5 до 50 символов"
|
||||||
}
|
return@Button
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
}
|
||||||
try {
|
|
||||||
ApiClient.register(RegisterRequest(username, password, confirmPassword))
|
// Send the register request
|
||||||
onRegistered()
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
} catch (e: Exception) {
|
try {
|
||||||
alert = "Ошибка при регистрации"
|
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()) {
|
if (composing.isNotBlank()) {
|
||||||
// Note: server schema for public chat messages is not specified in the repo.
|
// Note: server schema for public chat messages is not specified in the repo.
|
||||||
// We send a generic WebSocketMessage to match frontend type contract.
|
// We send a generic WebSocketMessage to match frontend type contract.
|
||||||
|
// TODO correct the payload
|
||||||
val payload = WebSocketMessage(
|
val payload = WebSocketMessage(
|
||||||
type = "public_chat_message",
|
type = "public_chat_message",
|
||||||
data = buildJsonObject {
|
data = buildJsonObject {
|
||||||
|
|||||||
Reference in New Issue
Block a user