diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/NetworkAndServers.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/NetworkAndServers.kt index f65c3f5a81..e12d1a6888 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/NetworkAndServers.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/NetworkAndServers.kt @@ -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() diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/NewServerView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/NewServerView.kt index 2852b00961..566600f7bf 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/NewServerView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/NewServerView.kt @@ -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>, serverErrors: MutableState>, - 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>, - serverErrors: MutableState>, - operatorIndex: Int, - rhId: Long?, server: MutableState, - testing: MutableState + 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>, - serverErrors: MutableState>, - operatorIndex: Int, - rhId: Long?, - server: MutableState, - testing: MutableState -) { - 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() } - 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 @@ -165,6 +90,7 @@ fun serverProtocolAndOperator( } fun addServer( + scope: CoroutineScope, server: UserServer, userServers: MutableState>, serverErrors: MutableState>, @@ -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, -// 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) -// } -// } -//} diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/ProtocolServerView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/ProtocolServerView.kt index 3c872755bd..e3a1c85a5a 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/ProtocolServerView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/ProtocolServerView.kt @@ -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, 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, 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, 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, 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) { diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml index e201c91d03..100c9f9d16 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml @@ -117,6 +117,7 @@ No media & file servers configured for sending. No media & file servers configured for private routing. For chat profile %s: + Errors in servers configuration. Error accepting conditions @@ -1735,6 +1736,11 @@ Server protocol changed. Server operator changed. + + Operator server + Server added to operator %s. + Error adding server + TCP connection Reset to defaults