diff --git a/app/src/main/kotlin/com/wire/android/ui/WireActivityDialogs.kt b/app/src/main/kotlin/com/wire/android/ui/WireActivityDialogs.kt
index d1aa189674e..8b8ee6e66b3 100644
--- a/app/src/main/kotlin/com/wire/android/ui/WireActivityDialogs.kt
+++ b/app/src/main/kotlin/com/wire/android/ui/WireActivityDialogs.kt
@@ -55,8 +55,10 @@ import com.wire.android.ui.common.bottomsheet.WireSheetValue
import com.wire.android.ui.common.bottomsheet.rememberWireModalSheetState
import com.wire.android.ui.common.button.WireButtonState
import com.wire.android.ui.common.button.WireSecondaryButton
-import com.wire.android.ui.common.dialogs.CustomServerDialog
-import com.wire.android.ui.common.dialogs.CustomServerDialogState
+import com.wire.android.ui.common.dialogs.CustomServerDetailsDialog
+import com.wire.android.ui.common.dialogs.CustomServerDetailsDialogState
+import com.wire.android.ui.common.dialogs.CustomServerInvalidJsonDialog
+import com.wire.android.ui.common.dialogs.CustomServerInvalidJsonDialogState
import com.wire.android.ui.common.dialogs.MaxAccountAllowedDialogContent
import com.wire.android.ui.common.dimensions
import com.wire.android.ui.common.wireDialogPropertiesBuilder
@@ -244,12 +246,24 @@ fun CustomBackendDialog(
onDismiss: () -> Unit,
onConfirm: () -> Unit
) {
- if (globalAppState.customBackendDialog != null) {
- CustomServerDialog(
- serverLinks = globalAppState.customBackendDialog.serverLinks,
- onDismiss = onDismiss,
- onConfirm = onConfirm
- )
+ when (globalAppState.customBackendDialog) {
+ is CustomServerDetailsDialogState -> {
+ CustomServerDetailsDialog(
+ serverLinks = globalAppState.customBackendDialog.serverLinks,
+ onDismiss = onDismiss,
+ onConfirm = onConfirm
+ )
+ }
+
+ is CustomServerInvalidJsonDialogState -> {
+ CustomServerInvalidJsonDialog(
+ onDismiss = onDismiss
+ )
+ }
+
+ else -> {
+ // nop
+ }
}
}
@@ -562,10 +576,13 @@ fun PreviewCustomBackendDialog() {
WireTheme {
CustomBackendDialog(
GlobalAppState(
- customBackendDialog = CustomServerDialogState(
+ customBackendDialog = CustomServerDetailsDialogState(
ServerConfig.STAGING
)
- ), {}, {})
+ ),
+ {},
+ {}
+ )
}
}
diff --git a/app/src/main/kotlin/com/wire/android/ui/WireActivityViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/WireActivityViewModel.kt
index 1f3073d4a9e..4fefdb9e261 100644
--- a/app/src/main/kotlin/com/wire/android/ui/WireActivityViewModel.kt
+++ b/app/src/main/kotlin/com/wire/android/ui/WireActivityViewModel.kt
@@ -40,7 +40,9 @@ import com.wire.android.feature.SwitchAccountResult
import com.wire.android.migration.MigrationManager
import com.wire.android.services.ServicesManager
import com.wire.android.ui.authentication.devices.model.displayName
+import com.wire.android.ui.common.dialogs.CustomServerDetailsDialogState
import com.wire.android.ui.common.dialogs.CustomServerDialogState
+import com.wire.android.ui.common.dialogs.CustomServerInvalidJsonDialogState
import com.wire.android.ui.joinConversation.JoinConversationViaCodeState
import com.wire.android.ui.theme.ThemeOption
import com.wire.android.util.CurrentScreen
@@ -378,10 +380,11 @@ class WireActivityViewModel @Inject constructor(
}
fun customBackendDialogProceedButtonClicked(onProceed: () -> Unit) {
- if (globalAppState.customBackendDialog != null) {
+ val backendDialogState = globalAppState.customBackendDialog
+ if (backendDialogState is CustomServerDetailsDialogState) {
viewModelScope.launch {
authServerConfigProvider.get()
- .updateAuthServer(globalAppState.customBackendDialog!!.serverLinks)
+ .updateAuthServer(backendDialogState.serverLinks)
dismissCustomBackendDialog()
if (checkNumberOfSessions() >= BuildConfig.MAX_ACCOUNTS) {
globalAppState = globalAppState.copy(maxAccountDialog = true)
@@ -456,7 +459,6 @@ class WireActivityViewModel @Inject constructor(
private suspend fun loadServerConfig(url: String): ServerConfig.Links? =
when (val result = getServerConfigUseCase.get().invoke(url)) {
is GetServerConfigResult.Success -> result.serverConfigLinks
- // TODO: show error message on failure
is GetServerConfigResult.Failure.Generic -> {
appLogger.e("something went wrong during handling the custom server deep link: ${result.genericFailure}")
null
@@ -464,13 +466,12 @@ class WireActivityViewModel @Inject constructor(
}
private suspend fun onCustomServerConfig(result: DeepLinkResult.CustomServerConfig) {
- loadServerConfig(result.url)?.let { serverLinks ->
- globalAppState = globalAppState.copy(
- customBackendDialog = CustomServerDialogState(
- serverLinks = serverLinks
- )
- )
- }
+ val customBackendDialogData = loadServerConfig(result.url)?.let { serverLinks ->
+ CustomServerDetailsDialogState(serverLinks = serverLinks)
+ } ?: CustomServerInvalidJsonDialogState
+ globalAppState = globalAppState.copy(
+ customBackendDialog = customBackendDialogData
+ )
}
private suspend fun onConversationInviteDeepLink(
diff --git a/app/src/main/kotlin/com/wire/android/ui/authentication/login/sso/LoginSSOScreen.kt b/app/src/main/kotlin/com/wire/android/ui/authentication/login/sso/LoginSSOScreen.kt
index b91861b35fa..ddc8ece1858 100644
--- a/app/src/main/kotlin/com/wire/android/ui/authentication/login/sso/LoginSSOScreen.kt
+++ b/app/src/main/kotlin/com/wire/android/ui/authentication/login/sso/LoginSSOScreen.kt
@@ -47,7 +47,7 @@ import com.wire.android.ui.authentication.login.LoginErrorDialog
import com.wire.android.ui.authentication.login.LoginState
import com.wire.android.ui.common.button.WireButtonState
import com.wire.android.ui.common.button.WirePrimaryButton
-import com.wire.android.ui.common.dialogs.CustomServerDialog
+import com.wire.android.ui.common.dialogs.CustomServerDetailsDialog
import com.wire.android.ui.common.textfield.WireTextField
import com.wire.android.ui.common.textfield.WireTextFieldState
import com.wire.android.ui.theme.WireTheme
@@ -142,7 +142,7 @@ private fun LoginSSOContent(
}
if (loginSSOState.customServerDialogState != null) {
- CustomServerDialog(
+ CustomServerDetailsDialog(
serverLinks = loginSSOState.customServerDialogState.serverLinks,
onDismiss = onCustomServerDialogDismiss,
onConfirm = onCustomServerDialogConfirm
diff --git a/app/src/main/kotlin/com/wire/android/ui/authentication/login/sso/LoginSSOState.kt b/app/src/main/kotlin/com/wire/android/ui/authentication/login/sso/LoginSSOState.kt
index 9b0de0a845d..43e6f626f84 100644
--- a/app/src/main/kotlin/com/wire/android/ui/authentication/login/sso/LoginSSOState.kt
+++ b/app/src/main/kotlin/com/wire/android/ui/authentication/login/sso/LoginSSOState.kt
@@ -18,10 +18,10 @@
package com.wire.android.ui.authentication.login.sso
import com.wire.android.ui.authentication.login.LoginState
-import com.wire.android.ui.common.dialogs.CustomServerDialogState
+import com.wire.android.ui.common.dialogs.CustomServerDetailsDialogState
data class LoginSSOState(
val loginEnabled: Boolean = false,
val flowState: LoginState = LoginState.Default,
- val customServerDialogState: CustomServerDialogState? = null,
+ val customServerDialogState: CustomServerDetailsDialogState? = null,
)
diff --git a/app/src/main/kotlin/com/wire/android/ui/authentication/login/sso/LoginSSOViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/authentication/login/sso/LoginSSOViewModel.kt
index e8b1c513476..e3d1be22f99 100644
--- a/app/src/main/kotlin/com/wire/android/ui/authentication/login/sso/LoginSSOViewModel.kt
+++ b/app/src/main/kotlin/com/wire/android/ui/authentication/login/sso/LoginSSOViewModel.kt
@@ -33,7 +33,7 @@ import com.wire.android.di.KaliumCoreLogic
import com.wire.android.ui.authentication.login.LoginState
import com.wire.android.ui.authentication.login.LoginViewModel
import com.wire.android.ui.authentication.login.toLoginError
-import com.wire.android.ui.common.dialogs.CustomServerDialogState
+import com.wire.android.ui.common.dialogs.CustomServerDetailsDialogState
import com.wire.android.ui.common.textfield.textAsFlow
import com.wire.android.util.EMPTY
import com.wire.android.util.deeplink.DeepLinkResult
@@ -190,7 +190,7 @@ class LoginSSOViewModel @Inject constructor(
}
is DomainLookupUseCase.Result.Success -> {
- loginState = loginState.copy(customServerDialogState = CustomServerDialogState(it.serverLinks))
+ loginState = loginState.copy(customServerDialogState = CustomServerDetailsDialogState(it.serverLinks))
updateSSOFlowState(LoginState.Default)
}
}
diff --git a/app/src/main/kotlin/com/wire/android/ui/common/dialogs/CustomServerDialog.kt b/app/src/main/kotlin/com/wire/android/ui/common/dialogs/CustomServerDialog.kt
index aa130cbc332..04205ae6d1c 100644
--- a/app/src/main/kotlin/com/wire/android/ui/common/dialogs/CustomServerDialog.kt
+++ b/app/src/main/kotlin/com/wire/android/ui/common/dialogs/CustomServerDialog.kt
@@ -49,7 +49,7 @@ import com.wire.android.util.ui.PreviewMultipleThemes
import com.wire.kalium.logic.configuration.server.ServerConfig
@Composable
-internal fun CustomServerDialog(
+internal fun CustomServerDetailsDialog(
serverLinks: ServerConfig.Links,
onDismiss: () -> Unit,
onConfirm: () -> Unit
@@ -163,12 +163,14 @@ private fun CustomServerPropertyInfo(
VerticalSpace.x16()
}
-data class CustomServerDialogState(val serverLinks: ServerConfig.Links)
+sealed class CustomServerDialogState
+
+data class CustomServerDetailsDialogState(val serverLinks: ServerConfig.Links) : CustomServerDialogState()
@PreviewMultipleThemes
@Composable
fun PreviewCustomServerDialog() = WireTheme {
- CustomServerDialog(
+ CustomServerDetailsDialog(
serverLinks = ServerConfig.DEFAULT,
onConfirm = { },
onDismiss = { }
diff --git a/app/src/main/kotlin/com/wire/android/ui/common/dialogs/CustomServerInvalidJsonDialog.kt b/app/src/main/kotlin/com/wire/android/ui/common/dialogs/CustomServerInvalidJsonDialog.kt
new file mode 100644
index 00000000000..356d9b12f17
--- /dev/null
+++ b/app/src/main/kotlin/com/wire/android/ui/common/dialogs/CustomServerInvalidJsonDialog.kt
@@ -0,0 +1,56 @@
+/*
+ * Wire
+ * Copyright (C) 2024 Wire Swiss GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/.
+ */
+package com.wire.android.ui.common.dialogs
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.stringResource
+import com.wire.android.R
+import com.wire.android.ui.common.WireDialog
+import com.wire.android.ui.common.WireDialogButtonProperties
+import com.wire.android.ui.common.WireDialogButtonType
+import com.wire.android.ui.common.button.WireButtonState
+import com.wire.android.ui.theme.WireTheme
+import com.wire.android.util.ui.PreviewMultipleThemes
+
+@Composable
+internal fun CustomServerInvalidJsonDialog(
+ onDismiss: () -> Unit
+) {
+ WireDialog(
+ title = stringResource(R.string.custom_backend_invalid_deeplink_data_title),
+ text = stringResource(R.string.custom_backend_invalid_deeplink_data_body),
+ onDismiss = onDismiss,
+ optionButton1Properties = WireDialogButtonProperties(
+ onClick = onDismiss,
+ text = stringResource(id = R.string.label_ok),
+ type = WireDialogButtonType.Primary,
+ state =
+ WireButtonState.Default
+ ),
+ )
+}
+
+data object CustomServerInvalidJsonDialogState : CustomServerDialogState()
+
+@PreviewMultipleThemes
+@Composable
+fun PreviewCustomServerInvalidJsonDialog() = WireTheme {
+ CustomServerInvalidJsonDialog(
+ onDismiss = { }
+ )
+}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b20e3344f83..39fe5dfc118 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1083,6 +1083,8 @@ In group conversations, the group admin can overwrite this setting.
Accounts URL:
Website URL:
Backend WSURL:
+ An error occurred
+ Redirecting to an on-premises backend was not possible, as there was an invalid configuration in the JSON file.\n\nContact your admin or check the deeplink that brought you here.
Receiving new messages
Text copied to clipboard
Logs
diff --git a/app/src/test/kotlin/com/wire/android/ui/WireActivityViewModelTest.kt b/app/src/test/kotlin/com/wire/android/ui/WireActivityViewModelTest.kt
index 8585edc2ee4..4b492a219b3 100644
--- a/app/src/test/kotlin/com/wire/android/ui/WireActivityViewModelTest.kt
+++ b/app/src/test/kotlin/com/wire/android/ui/WireActivityViewModelTest.kt
@@ -36,6 +36,8 @@ import com.wire.android.framework.TestClient
import com.wire.android.framework.TestUser
import com.wire.android.migration.MigrationManager
import com.wire.android.services.ServicesManager
+import com.wire.android.ui.common.dialogs.CustomServerDetailsDialogState
+import com.wire.android.ui.common.dialogs.CustomServerInvalidJsonDialogState
import com.wire.android.ui.common.topappbar.CommonTopAppBarViewModelTest
import com.wire.android.ui.joinConversation.JoinConversationViaCodeState
import com.wire.android.ui.theme.ThemeOption
@@ -45,6 +47,7 @@ import com.wire.android.util.deeplink.DeepLinkProcessor
import com.wire.android.util.deeplink.DeepLinkResult
import com.wire.android.util.newServerConfig
import com.wire.kalium.logic.CoreLogic
+import com.wire.kalium.logic.NetworkFailure
import com.wire.kalium.logic.data.auth.AccountInfo
import com.wire.kalium.logic.data.auth.PersistentWebSocketStatus
import com.wire.kalium.logic.data.call.Call
@@ -89,6 +92,7 @@ import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
import org.amshove.kluent.internal.assertEquals
import org.amshove.kluent.`should be equal to`
+import org.junit.jupiter.api.Assertions.assertInstanceOf
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
@@ -133,6 +137,42 @@ class WireActivityViewModelTest {
verify(exactly = 1) { arrangement.onDeepLinkResult(result) }
}
+ @Test
+ fun `given Intent with malformed ServerConfig json, when currentSessions is present, then initialAppState is LOGGED_IN and customBackEndInvalidJson dialog is shown`() =
+ runTest {
+ val result = DeepLinkResult.CustomServerConfig("url")
+ val (arrangement, viewModel) = Arrangement()
+ .withSomeCurrentSession()
+ .withDeepLinkResult(result)
+ .withMalformedServerJson()
+ .withNoOngoingCall()
+ .arrange()
+
+ viewModel.handleDeepLink(mockedIntent(), {}, {}, arrangement.onDeepLinkResult, {}, {}, {}, {})
+
+ assertEquals(InitialAppState.LOGGED_IN, viewModel.initialAppState())
+ verify(exactly = 0) { arrangement.onDeepLinkResult(any()) }
+ assertInstanceOf(CustomServerInvalidJsonDialogState::class.java, viewModel.globalAppState.customBackendDialog)
+ }
+
+ @Test
+ fun `given Intent with malformed ServerConfig json, when currentSessions is present, then initialAppState is NOT_LOGGED_IN and customBackEndInvalidJson dialog is shown`() =
+ runTest {
+ val result = DeepLinkResult.CustomServerConfig("url")
+ val (arrangement, viewModel) = Arrangement()
+ .withNoCurrentSession()
+ .withDeepLinkResult(result)
+ .withMalformedServerJson()
+ .withNoOngoingCall()
+ .arrange()
+
+ viewModel.handleDeepLink(mockedIntent(), {}, {}, arrangement.onDeepLinkResult, {}, {}, {}, {})
+
+ assertEquals(InitialAppState.NOT_LOGGED_IN, viewModel.initialAppState())
+ verify(exactly = 0) { arrangement.onDeepLinkResult(any()) }
+ assertInstanceOf(CustomServerInvalidJsonDialogState::class.java, viewModel.globalAppState.customBackendDialog)
+ }
+
@Test
fun `given Intent with ServerConfig, when currentSession is present, then initialAppState is LOGGED_IN and customBackEnd dialog is shown`() =
runTest {
@@ -147,7 +187,11 @@ class WireActivityViewModelTest {
assertEquals(InitialAppState.LOGGED_IN, viewModel.initialAppState())
verify(exactly = 0) { arrangement.onDeepLinkResult(any()) }
- assertEquals(newServerConfig(1).links, viewModel.globalAppState.customBackendDialog!!.serverLinks)
+ assertInstanceOf(CustomServerDetailsDialogState::class.java, viewModel.globalAppState.customBackendDialog)
+ assertEquals(
+ newServerConfig(1).links,
+ (viewModel.globalAppState.customBackendDialog as CustomServerDetailsDialogState).serverLinks
+ )
}
@Test
@@ -163,7 +207,11 @@ class WireActivityViewModelTest {
assertEquals(InitialAppState.NOT_LOGGED_IN, viewModel.initialAppState())
verify(exactly = 0) { arrangement.onDeepLinkResult(any()) }
- assertEquals(newServerConfig(1).links, viewModel.globalAppState.customBackendDialog!!.serverLinks)
+ assertInstanceOf(CustomServerDetailsDialogState::class.java, viewModel.globalAppState.customBackendDialog)
+ assertEquals(
+ newServerConfig(1).links,
+ (viewModel.globalAppState.customBackendDialog as CustomServerDetailsDialogState).serverLinks
+ )
}
@Test
@@ -820,6 +868,11 @@ class WireActivityViewModelTest {
coEvery { coreLogic.getSessionScope(TEST_ACCOUNT_INFO.userId).observeIfE2EIRequiredDuringLogin() } returns flowOf(false)
}
+ fun withMalformedServerJson() = apply {
+ coEvery { getServerConfigUseCase(any()) } returns
+ GetServerConfigResult.Failure.Generic(NetworkFailure.NoNetworkConnection(null))
+ }
+
suspend fun withScreenshotCensoringConfig(result: ObserveScreenshotCensoringConfigResult) = apply {
coEvery { observeScreenshotCensoringConfigUseCase() } returns flowOf(result)
}
diff --git a/app/src/test/kotlin/com/wire/android/ui/authentication/login/sso/LoginSSOViewModelTest.kt b/app/src/test/kotlin/com/wire/android/ui/authentication/login/sso/LoginSSOViewModelTest.kt
index db95f56468c..c0895b990ec 100644
--- a/app/src/test/kotlin/com/wire/android/ui/authentication/login/sso/LoginSSOViewModelTest.kt
+++ b/app/src/test/kotlin/com/wire/android/ui/authentication/login/sso/LoginSSOViewModelTest.kt
@@ -28,7 +28,7 @@ import com.wire.android.di.AuthServerConfigProvider
import com.wire.android.di.ClientScopeProvider
import com.wire.android.framework.TestClient
import com.wire.android.ui.authentication.login.LoginState
-import com.wire.android.ui.common.dialogs.CustomServerDialogState
+import com.wire.android.ui.common.dialogs.CustomServerDetailsDialogState
import com.wire.android.util.EMPTY
import com.wire.android.util.deeplink.DeepLinkResult
import com.wire.android.util.deeplink.SSOFailureCodes
@@ -423,7 +423,7 @@ class LoginSSOViewModelTest {
@Test
fun `given backend switch confirmed, then auth server provider is updated`() = runTest {
val expected = newServerConfig(2).links
- loginViewModel.loginState = loginViewModel.loginState.copy(customServerDialogState = CustomServerDialogState(expected))
+ loginViewModel.loginState = loginViewModel.loginState.copy(customServerDialogState = CustomServerDetailsDialogState(expected))
coEvery { fetchSSOSettings.invoke() } returns FetchSSOSettingsUseCase.Result.Success("ssoCode")
coEvery { ssoInitiateLoginUseCase(any()) } returns SSOInitiateLoginResult.Success("url")
@@ -440,7 +440,7 @@ class LoginSSOViewModelTest {
coEvery { fetchSSOSettings.invoke() } returns FetchSSOSettingsUseCase.Result.Success("ssoCode")
coEvery { ssoInitiateLoginUseCase(any()) } returns SSOInitiateLoginResult.Success("url")
- loginViewModel.loginState = loginViewModel.loginState.copy(customServerDialogState = CustomServerDialogState(expected))
+ loginViewModel.loginState = loginViewModel.loginState.copy(customServerDialogState = CustomServerDetailsDialogState(expected))
loginViewModel.onCustomServerDialogConfirm()
@@ -457,7 +457,7 @@ class LoginSSOViewModelTest {
val expected = newServerConfig(2).links
every { validateEmailUseCase(any()) } returns true
coEvery { fetchSSOSettings.invoke() } returns FetchSSOSettingsUseCase.Result.Success(null)
- loginViewModel.loginState = loginViewModel.loginState.copy(customServerDialogState = CustomServerDialogState(expected))
+ loginViewModel.loginState = loginViewModel.loginState.copy(customServerDialogState = CustomServerDetailsDialogState(expected))
loginViewModel.onCustomServerDialogConfirm()
@@ -473,7 +473,7 @@ class LoginSSOViewModelTest {
val expected = newServerConfig(2).links
every { validateEmailUseCase(any()) } returns true
coEvery { fetchSSOSettings.invoke() } returns FetchSSOSettingsUseCase.Result.Failure(CoreFailure.Unknown(IOException()))
- loginViewModel.loginState = loginViewModel.loginState.copy(customServerDialogState = CustomServerDialogState(expected))
+ loginViewModel.loginState = loginViewModel.loginState.copy(customServerDialogState = CustomServerDetailsDialogState(expected))
loginViewModel.onCustomServerDialogConfirm()