feat: auth screen with auto-redirect, sync favorites/history with backend
This commit is contained in:
@@ -3,16 +3,21 @@ package com.radiola.ui.settings
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.radiola.domain.model.DeeplinkService
|
||||
import com.radiola.domain.model.StationTestStatus
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SettingsScreen(
|
||||
onNavigateToAuth: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
viewModel: SettingsViewModel = hiltViewModel()
|
||||
) {
|
||||
@@ -20,7 +25,14 @@ fun SettingsScreen(
|
||||
val enabledServices by viewModel.enabledServices.collectAsState()
|
||||
val equalizerPreset by viewModel.equalizerPreset.collectAsState()
|
||||
val isRecordingEnabled by viewModel.isRecordingEnabled.collectAsState()
|
||||
val isTesting by viewModel.isTesting.collectAsState()
|
||||
val testProgress by viewModel.testProgress.collectAsState()
|
||||
val testTotal by viewModel.testTotal.collectAsState()
|
||||
val testResults by viewModel.testResults.collectAsState()
|
||||
val isLoggedIn by viewModel.isLoggedIn.collectAsState()
|
||||
val currentUser by viewModel.currentUser.collectAsState()
|
||||
val presets = listOf("Flat", "Rock", "Pop", "Jazz", "Bass")
|
||||
var showReport by remember { mutableStateOf(false) }
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
@@ -39,6 +51,37 @@ fun SettingsScreen(
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
item {
|
||||
Text("Профиль", style = MaterialTheme.typography.titleMedium)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
if (isLoggedIn && currentUser != null) {
|
||||
Column {
|
||||
Text(
|
||||
text = currentUser?.email ?: "",
|
||||
style = MaterialTheme.typography.bodyLarge
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
OutlinedButton(
|
||||
onClick = { viewModel.logout() },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text("Выйти")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Button(
|
||||
onClick = onNavigateToAuth,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text("Войти")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
Divider()
|
||||
}
|
||||
|
||||
item {
|
||||
Text("Таймер сна", style = MaterialTheme.typography.titleMedium)
|
||||
Slider(
|
||||
@@ -99,6 +142,94 @@ fun SettingsScreen(
|
||||
)
|
||||
}
|
||||
}
|
||||
item {
|
||||
Divider()
|
||||
}
|
||||
item {
|
||||
Text("Тестирование станций", style = MaterialTheme.typography.titleMedium)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
if (isTesting) {
|
||||
Column {
|
||||
LinearProgressIndicator(
|
||||
progress = { if (testTotal > 0) testProgress.toFloat() / testTotal else 0f },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Text("Проверено $testProgress из $testTotal")
|
||||
}
|
||||
} else if (testResults.isNotEmpty()) {
|
||||
val ok = testResults.count { it.status == StationTestStatus.OK }
|
||||
val okNoMeta = testResults.count { it.status == StationTestStatus.OK_NO_META }
|
||||
val offline = testResults.count { it.status == StationTestStatus.OFFLINE }
|
||||
val error = testResults.count { it.status == StationTestStatus.ERROR }
|
||||
|
||||
Column {
|
||||
Text("Всего: ${testResults.size}")
|
||||
Text("Работают + метаданные: $ok", color = Color(0xFF4CAF50))
|
||||
Text("Работают без метаданных: $okNoMeta", color = Color(0xFFFF9800))
|
||||
Text("Оффлайн: $offline", color = Color(0xFFFF5252))
|
||||
Text("Ошибки: $error", color = Color(0xFFFF5252))
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Button(onClick = { showReport = true }) {
|
||||
Text("Подробный отчёт")
|
||||
}
|
||||
OutlinedButton(onClick = { viewModel.clearTestResults() }) {
|
||||
Text("Очистить")
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Button(
|
||||
onClick = { viewModel.startTesting() },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text("Провести тестирование")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showReport) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showReport = false },
|
||||
title = { Text("Результаты тестирования") },
|
||||
text = {
|
||||
LazyColumn(modifier = Modifier.heightIn(max = 400.dp)) {
|
||||
items(testResults) { result ->
|
||||
val color = when (result.status) {
|
||||
StationTestStatus.OK -> Color(0xFF4CAF50)
|
||||
StationTestStatus.OK_NO_META -> Color(0xFFFF9800)
|
||||
StationTestStatus.OFFLINE -> Color(0xFFFF5252)
|
||||
StationTestStatus.ERROR -> Color(0xFFFF5252)
|
||||
}
|
||||
Column(modifier = Modifier.padding(vertical = 4.dp)) {
|
||||
Text(
|
||||
text = result.stationName,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = color
|
||||
)
|
||||
Text(
|
||||
text = buildString {
|
||||
append("${result.status.name}")
|
||||
result.httpCode?.let { append(" | HTTP $it") }
|
||||
result.icyTitle?.let { append(" | Icy: $it") }
|
||||
result.nowPlayingTrack?.let { append(" | NP: $it") }
|
||||
result.errorMessage?.let { append(" | $it") }
|
||||
},
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = { showReport = false }) {
|
||||
Text("Закрыть")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user