diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
deleted file mode 100644
index eba8130..0000000
--- a/.idea/deploymentTargetSelector.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index 7061a0d..a4f8d4a 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -57,5 +57,7 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/java/ru/fromchat/ui/Components.kt b/app/src/main/java/ru/fromchat/ui/Components.kt
new file mode 100644
index 0000000..56af405
--- /dev/null
+++ b/app/src/main/java/ru/fromchat/ui/Components.kt
@@ -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)
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/fromchat/ui/auth/LoginScreen.kt b/app/src/main/java/ru/fromchat/ui/auth/LoginScreen.kt
index 624ee9e..5e0ee7b 100644
--- a/app/src/main/java/ru/fromchat/ui/auth/LoginScreen.kt
+++ b/app/src/main/java/ru/fromchat/ui/auth/LoginScreen.kt
@@ -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(null) }
+ Scaffold { innerPadding ->
+ var username by remember { mutableStateOf("") }
+ var password by remember { mutableStateOf("") }
+ var alert by remember { mutableStateOf(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))
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/ru/fromchat/ui/auth/RegisterScreen.kt b/app/src/main/java/ru/fromchat/ui/auth/RegisterScreen.kt
index ae45386..37b3c18 100644
--- a/app/src/main/java/ru/fromchat/ui/auth/RegisterScreen.kt
+++ b/app/src/main/java/ru/fromchat/ui/auth/RegisterScreen.kt
@@ -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(null) }
+ Scaffold { innerPadding ->
+ var username by remember { mutableStateOf("") }
+ var password by remember { mutableStateOf("") }
+ var confirmPassword by remember { mutableStateOf("") }
+ var alert by remember { mutableStateOf(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("Зарегистрироваться")
+ }
}
}
}
diff --git a/app/src/main/java/ru/fromchat/ui/chat/BaseChat.kt b/app/src/main/java/ru/fromchat/ui/chat/BaseChat.kt
index b0cda8f..a00402a 100644
--- a/app/src/main/java/ru/fromchat/ui/chat/BaseChat.kt
+++ b/app/src/main/java/ru/fromchat/ui/chat/BaseChat.kt
@@ -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 {