new server view wip

This commit is contained in:
spaced4ndy
2024-11-21 15:38:45 +04:00
parent bf3db50c55
commit fb02e6d844
4 changed files with 86 additions and 194 deletions
@@ -241,6 +241,10 @@ fun ModalData.NetworkAndServersView(close: () -> Unit) {
SectionCustomFooter {
ServersErrorFooter(serversErr)
}
} else if (serverErrors.value.isNotEmpty()) {
SectionCustomFooter {
ServersErrorFooter(generalGetString(MR.strings.errors_in_servers_configuration))
}
}
SectionDividerSpaced()
@@ -1,43 +1,30 @@
package chat.simplex.common.views.usersettings.networkAndServers
import SectionBottomSpacer
import SectionDividerSpaced
import SectionItemView
import SectionItemViewSpaceBetween
import SectionView
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource
import androidx.compose.ui.unit.dp
import chat.simplex.common.model.*
import chat.simplex.common.model.ServerAddress.Companion.parseServerAddress
import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.newchat.QRCode
import chat.simplex.common.platform.*
import chat.simplex.common.views.usersettings.PreferenceToggle
import chat.simplex.res.MR
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.distinctUntilChanged
@Composable
fun ModalData.NewServerView(
userServers: MutableState<List<UserOperatorServers>>,
serverErrors: MutableState<List<UserServersError>>,
operatorIndex: Int,
rhId: Long?,
close: () -> Unit
) {
val newServer = remember { mutableStateOf(UserServer.empty) }
val testing = remember { mutableStateOf(false) }
val scope = rememberCoroutineScope()
val newServer = remember { mutableStateOf(UserServer.empty) }
ModalView(close = {
addServer(
scope,
newServer.value,
userServers,
serverErrors,
@@ -45,104 +32,42 @@ fun ModalData.NewServerView(
close = close
)
}) {
NewServerLayout(
userServers,
serverErrors,
operatorIndex,
rhId,
newServer,
testing
// testServer = {
// testing = true
// withLongRunningApi {
// val res = testServerConnection(server, m)
// if (isActive) {
// onUpdate(res.first)
// testing = false
// }
// }
// }
)
}
if (testing.value) {
Box(
Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator(
Modifier
.padding(horizontal = 2.dp)
.size(30.dp),
color = MaterialTheme.colors.secondary,
strokeWidth = 2.5.dp
Box {
NewServerLayout(
newServer,
testing.value,
testServer = {
testing.value = true
withLongRunningApi {
val res = testServerConnection(newServer.value, chatModel)
if (isActive) {
newServer.value = res.first
testing.value = false
}
}
},
)
if (testing.value) {
DefaultProgressView(null)
}
}
}
}
@Composable
private fun NewServerLayout(
userServers: MutableState<List<UserOperatorServers>>,
serverErrors: MutableState<List<UserServersError>>,
operatorIndex: Int,
rhId: Long?,
server: MutableState<UserServer>,
testing: MutableState<Boolean>
testing: Boolean,
testServer: () -> Unit,
) {
ColumnWithScrollBar {
AppBarTitle(stringResource(MR.strings.smp_servers_new_server))
CustomServer(userServers, serverErrors, operatorIndex, rhId, server, testing)
CustomServer(server, testing, testServer, onDelete = null)
SectionBottomSpacer()
}
}
@Composable
private fun CustomServer(
userServers: MutableState<List<UserOperatorServers>>,
serverErrors: MutableState<List<UserServersError>>,
operatorIndex: Int,
rhId: Long?,
server: MutableState<UserServer>,
testing: MutableState<Boolean>
) {
val serverAddress = remember { mutableStateOf(server.value.server) }
val valid = remember {
derivedStateOf {
with(parseServerAddress(serverAddress.value)) {
this?.valid == true
}
}
}
SectionView(
stringResource(MR.strings.smp_servers_your_server_address).uppercase(),
icon = painterResource(MR.images.ic_error),
iconTint = if (!valid.value) MaterialTheme.colors.error else Color.Transparent,
) {
val testedPreviously = remember { mutableMapOf<String, Boolean?>() }
TextEditor(
serverAddress,
Modifier.height(144.dp)
)
LaunchedEffect(Unit) {
snapshotFlow { serverAddress.value }
.distinctUntilChanged()
.collect {
testedPreviously[server.value.server] = server.value.tested
server.value = server.value.copy(server = it, tested = testedPreviously[serverAddress.value])
}
}
}
SectionDividerSpaced(maxTopPadding = true)
// UseServerSection(valid.value, testing, server, testServer, onUpdate, onDelete)
if (valid.value) {
SectionDividerSpaced()
SectionView(stringResource(MR.strings.smp_servers_add_to_another_device).uppercase()) {
QRCode(serverAddress.value)
}
}
}
fun serverProtocolAndOperator(
server: UserServer,
userServers: List<UserOperatorServers>
@@ -165,6 +90,7 @@ fun serverProtocolAndOperator(
}
fun addServer(
scope: CoroutineScope,
server: UserServer,
userServers: MutableState<List<UserOperatorServers>>,
serverErrors: MutableState<List<UserServersError>>,
@@ -179,7 +105,6 @@ fun addServer(
// Create a mutable copy of the userServers list
val updatedUserServers = userServers.value.toMutableList()
val operatorServers = updatedUserServers[operatorIndex]
// Create a mutable copy of the smpServers or xftpServers and add the server
when (serverProtocol) {
ServerProtocol.SMP -> {
@@ -187,6 +112,7 @@ fun addServer(
updatedSMPServers.add(server)
updatedUserServers[operatorIndex] = operatorServers.copy(smpServers = updatedSMPServers)
}
ServerProtocol.XFTP -> {
val updatedXFTPServers = operatorServers.xftpServers.toMutableList()
updatedXFTPServers.add(server)
@@ -195,53 +121,25 @@ fun addServer(
}
userServers.value = updatedUserServers
// TODO
// validateServers(rhId, userServers, serverErrors)
close()
scope.launch { validateServers(rhId, userServers, serverErrors) }
matchingOperator?.let { op ->
AlertManager.shared.showAlertMsg(
title = "Operator server",
text = "Server added to operator ${op.tradeName}."
title = generalGetString(MR.strings.operator_server_alert_title),
text = String.format(generalGetString(MR.strings.server_added_to_operator__name), op.tradeName)
)
}
} else { // Shouldn't happen
close()
AlertManager.shared.showAlertMsg(title = "Error adding server")
AlertManager.shared.showAlertMsg(title = generalGetString(MR.strings.error_adding_server))
}
} else {
close()
if (server.server.trim().isNotEmpty()) {
AlertManager.shared.showAlertMsg(
title = "Invalid server address!",
text = "Check server address and try again."
title = generalGetString(MR.strings.smp_servers_invalid_address),
text = generalGetString(MR.strings.smp_servers_check_address)
)
}
}
}
//@Composable
//private fun UseServerSection(
// valid: Boolean,
// testing: MutableState<Boolean>,
// server: UserServer
//) {
// SectionView(stringResource(MR.strings.smp_servers_use_server).uppercase()) {
// SectionItemViewSpaceBetween(testServer, disabled = !valid || testing.value) {
// Text(stringResource(MR.strings.smp_servers_test_server), color = if (valid && !testing.value) MaterialTheme.colors.onBackground else MaterialTheme.colors.secondary)
// ShowTestStatus(server)
// }
//
// val enabled = rememberUpdatedState(server.enabled)
// PreferenceToggle(
// stringResource(MR.strings.smp_servers_use_server_for_new_conn),
// disabled = server.tested != true && !server.preset,
// checked = enabled.value
// ) {
// onUpdate(server.copy(enabled = it))
// }
//
// SectionItemView(onDelete, disabled = testing) {
// Text(stringResource(MR.strings.smp_servers_delete_server), color = if (testing) MaterialTheme.colors.secondary else MaterialTheme.colors.error)
// }
// }
//}
@@ -9,7 +9,6 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import dev.icerock.moko.resources.compose.painterResource
@@ -42,7 +41,7 @@ fun ProtocolServerView(
close: () -> Unit,
rhId: Long?
) {
var testing by remember { mutableStateOf(false) }
val testing = remember { mutableStateOf(false) }
val scope = rememberCoroutineScope()
val draftServer = remember { mutableStateOf(server) }
@@ -70,8 +69,8 @@ fun ProtocolServerView(
)
} else {
onUpdate(draftServer.value)
validateServers(rhId, userServers, serverErrors)
close()
validateServers(rhId, userServers, serverErrors)
}
} else {
close()
@@ -83,37 +82,26 @@ fun ProtocolServerView(
}
}
) {
ProtocolServerLayout(
testing,
draftServer.value,
serverProtocol,
testServer = {
testing = true
withLongRunningApi {
val res = testServerConnection(draftServer.value, m)
if (isActive) {
draftServer.value = res.first
testing = false
Box {
ProtocolServerLayout(
draftServer,
serverProtocol,
testing.value,
testServer = {
testing.value = true
withLongRunningApi {
val res = testServerConnection(draftServer.value, m)
if (isActive) {
draftServer.value = res.first
testing.value = false
}
}
}
},
onUpdate = {
draftServer.value = it
},
onDelete
)
if (testing) {
Box(
Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator(
Modifier
.padding(horizontal = 2.dp)
.size(30.dp),
color = MaterialTheme.colors.secondary,
strokeWidth = 2.5.dp
)
},
onDelete
)
if (testing.value) {
DefaultProgressView(null)
}
}
}
@@ -121,20 +109,19 @@ fun ProtocolServerView(
@Composable
private fun ProtocolServerLayout(
testing: Boolean,
server: UserServer,
server: MutableState<UserServer>,
serverProtocol: ServerProtocol,
testing: Boolean,
testServer: () -> Unit,
onUpdate: (UserServer) -> Unit,
onDelete: () -> Unit,
) {
ColumnWithScrollBar {
AppBarTitle(stringResource(if (serverProtocol == ServerProtocol.XFTP) MR.strings.xftp_server else MR.strings.smp_server))
if (server.preset) {
PresetServer(testing, server, testServer, onUpdate)
if (server.value.preset) {
PresetServer(server, testing, testServer)
} else {
CustomServer(testing, server, serverProtocol, testServer, onUpdate, onDelete)
CustomServer(server, testing, testServer, onDelete)
}
SectionBottomSpacer()
}
@@ -142,15 +129,14 @@ private fun ProtocolServerLayout(
@Composable
private fun PresetServer(
server: MutableState<UserServer>,
testing: Boolean,
server: UserServer,
testServer: () -> Unit,
onUpdate: (UserServer) -> Unit,
testServer: () -> Unit
) {
SectionView(stringResource(MR.strings.smp_servers_preset_address).uppercase()) {
SelectionContainer {
Text(
server.server,
server.value.server,
Modifier.padding(start = DEFAULT_PADDING, top = 5.dp, end = DEFAULT_PADDING, bottom = 10.dp),
style = TextStyle(
fontFamily = FontFamily.Monospace, fontSize = 16.sp,
@@ -160,23 +146,21 @@ private fun PresetServer(
}
}
SectionDividerSpaced()
UseServerSection(true, testing, server, testServer, onUpdate)
UseServerSection(server, true, testing, testServer)
}
@Composable
private fun CustomServer(
fun CustomServer(
server: MutableState<UserServer>,
testing: Boolean,
server: UserServer,
serverProtocol: ServerProtocol,
testServer: () -> Unit,
onUpdate: (UserServer) -> Unit,
onDelete: () -> Unit,
onDelete: (() -> Unit)?,
) {
val serverAddress = remember { mutableStateOf(server.server) }
val serverAddress = remember { mutableStateOf(server.value.server) }
val valid = remember {
derivedStateOf {
with(parseServerAddress(serverAddress.value)) {
this?.valid == true && this.serverProtocol == serverProtocol
this?.valid == true
}
}
}
@@ -194,13 +178,14 @@ private fun CustomServer(
snapshotFlow { serverAddress.value }
.distinctUntilChanged()
.collect {
testedPreviously[server.server] = server.tested
onUpdate(server.copy(server = it, tested = testedPreviously[serverAddress.value]))
testedPreviously[server.value.server] = server.value.tested
server.value = server.value.copy(server = it, tested = testedPreviously[serverAddress.value])
}
}
}
SectionDividerSpaced(maxTopPadding = true)
UseServerSection(valid.value, testing, server, testServer, onUpdate, onDelete)
UseServerSection(server, valid.value, testing, testServer, onDelete)
if (valid.value) {
SectionDividerSpaced()
@@ -212,26 +197,25 @@ private fun CustomServer(
@Composable
private fun UseServerSection(
server: MutableState<UserServer>,
valid: Boolean,
testing: Boolean,
server: UserServer,
testServer: () -> Unit,
onUpdate: (UserServer) -> Unit,
onDelete: (() -> Unit)? = null,
) {
SectionView(stringResource(MR.strings.smp_servers_use_server).uppercase()) {
SectionItemViewSpaceBetween(testServer, disabled = !valid || testing) {
Text(stringResource(MR.strings.smp_servers_test_server), color = if (valid && !testing) MaterialTheme.colors.onBackground else MaterialTheme.colors.secondary)
ShowTestStatus(server)
ShowTestStatus(server.value)
}
val enabled = rememberUpdatedState(server.enabled)
val enabled = rememberUpdatedState(server.value.enabled)
PreferenceToggle(
stringResource(MR.strings.smp_servers_use_server_for_new_conn),
// disabled = server.tested != true && !server.preset,
disabled = testing,
checked = enabled.value
) {
onUpdate(server.copy(enabled = it))
server.value = server.value.copy(enabled = it)
}
if (onDelete != null) {
@@ -117,6 +117,7 @@
<string name="no_media_servers_configured_for_sending">No media &amp; file servers configured for sending.</string>
<string name="no_media_servers_configured_for_private_routing">No media &amp; file servers configured for private routing.</string>
<string name="for_chat_profile">For chat profile %s:</string>
<string name="errors_in_servers_configuration">Errors in servers configuration.</string>
<string name="error_accepting_operator_conditions">Error accepting conditions</string>
<!-- API Error Responses - SimpleXAPI.kt -->
@@ -1735,6 +1736,11 @@
<string name="error_server_protocol_changed">Server protocol changed.</string>
<string name="error_server_operator_changed">Server operator changed.</string>
<!-- NewServerView.kt -->
<string name="operator_server_alert_title">Operator server</string>
<string name="server_added_to_operator__name">Server added to operator %s.</string>
<string name="error_adding_server">Error adding server</string>
<!-- AdvancedNetworkSettings.kt -->
<string name="network_option_tcp_connection">TCP connection</string>
<string name="network_options_reset_to_defaults">Reset to defaults</string>