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="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,12 +26,14 @@ 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
|
||||
) {
|
||||
Scaffold { innerPadding ->
|
||||
var username by remember { mutableStateOf("") }
|
||||
var password by remember { mutableStateOf("") }
|
||||
var alert by remember { mutableStateOf<String?>(null) }
|
||||
@@ -41,17 +41,24 @@ fun LoginScreen(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(innerPadding)
|
||||
.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.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,12 +76,14 @@ fun LoginScreen(
|
||||
visualTransformation = PasswordVisualTransformation()
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(12.dp))
|
||||
Button(onClick = {
|
||||
FlowRow(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||
Button(
|
||||
onClick = {
|
||||
if (username.isBlank() || password.isBlank()) {
|
||||
alert = "Пожалуйста, заполните все поля"
|
||||
return@Button
|
||||
}
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
ApiClient.login(LoginRequest(username, password))
|
||||
@@ -83,21 +92,16 @@ fun LoginScreen(
|
||||
alert = "Неверное имя пользователя или пароль"
|
||||
}
|
||||
}
|
||||
}) { Text("Войти") }
|
||||
}
|
||||
) {
|
||||
Text("Войти")
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(8.dp))
|
||||
Button(onClick = onNavigateToRegister) { 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,11 +26,13 @@ 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
|
||||
) {
|
||||
Scaffold { innerPadding ->
|
||||
var username by remember { mutableStateOf("") }
|
||||
var password by remember { mutableStateOf("") }
|
||||
var confirmPassword by remember { mutableStateOf("") }
|
||||
@@ -41,22 +41,24 @@ fun RegisterScreen(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(innerPadding)
|
||||
.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.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,8 +84,9 @@ fun RegisterScreen(
|
||||
visualTransformation = PasswordVisualTransformation()
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(12.dp))
|
||||
Button(onClick = {
|
||||
Button(
|
||||
onClick = {
|
||||
// Checks
|
||||
if (username.isBlank() || password.isBlank() || confirmPassword.isBlank()) {
|
||||
alert = "Пожалуйста, заполните все поля"
|
||||
return@Button
|
||||
@@ -100,15 +103,27 @@ fun RegisterScreen(
|
||||
alert = "Пароль должен быть от 5 до 50 символов"
|
||||
return@Button
|
||||
}
|
||||
|
||||
// Send the register request
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
ApiClient.register(RegisterRequest(username, password, confirmPassword))
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user