Skip to content

Commit

Permalink
feat: display error dialog when handling deeplinks #WPB-282 (#3486)
Browse files Browse the repository at this point in the history
  • Loading branch information
m-zagorski authored Oct 11, 2024
1 parent ffd5445 commit 4256b55
Show file tree
Hide file tree
Showing 10 changed files with 167 additions and 36 deletions.
37 changes: 27 additions & 10 deletions app/src/main/kotlin/com/wire/android/ui/WireActivityDialogs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
}
}

Expand Down Expand Up @@ -562,10 +576,13 @@ fun PreviewCustomBackendDialog() {
WireTheme {
CustomBackendDialog(
GlobalAppState(
customBackendDialog = CustomServerDialogState(
customBackendDialog = CustomServerDetailsDialogState(
ServerConfig.STAGING
)
), {}, {})
),
{},
{}
)
}
}

Expand Down
21 changes: 11 additions & 10 deletions app/src/main/kotlin/com/wire/android/ui/WireActivityViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -456,21 +459,19 @@ 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
}
}

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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -142,7 +142,7 @@ private fun LoginSSOContent(
}

if (loginSSOState.customServerDialogState != null) {
CustomServerDialog(
CustomServerDetailsDialog(
serverLinks = loginSSOState.customServerDialogState.serverLinks,
onDismiss = onCustomServerDialogDismiss,
onConfirm = onCustomServerDialogConfirm
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 = { }
Expand Down
Original file line number Diff line number Diff line change
@@ -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 = { }
)
}
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1077,6 +1077,8 @@ In group conversations, the group admin can overwrite this setting.</string>
<string name="custom_backend_dialog_body_backend_accounts">Accounts URL:</string>
<string name="custom_backend_dialog_body_backend_website">Website URL:</string>
<string name="custom_backend_dialog_body_backend_websocket">Backend WSURL:</string>
<string name="custom_backend_invalid_deeplink_data_title">An error occurred</string>
<string name="custom_backend_invalid_deeplink_data_body">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.</string>
<string name="label_fetching_your_messages">Receiving new messages</string>
<string name="label_text_copied">Text copied to clipboard</string>
<string name="label_logs_option_title">Logs</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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 {
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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)
}
Expand Down
Loading

0 comments on commit 4256b55

Please sign in to comment.