Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: handle NoCommonProtocol error while starting conversation #3120

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import com.wire.android.R
import com.wire.android.di.hiltViewModelScoped
import com.wire.android.model.ClickBlockParams
Expand All @@ -51,6 +52,7 @@ import com.wire.android.ui.theme.WireTheme
import com.wire.android.ui.theme.wireTypography
import com.wire.android.util.ui.PreviewMultipleThemes
import com.wire.android.util.ui.stringWithStyledArgs
import com.wire.kalium.logic.CoreFailure
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.user.ConnectionState
import com.wire.kalium.logic.data.user.UserId
Expand Down Expand Up @@ -98,7 +100,7 @@ fun ConnectionActionButton(
loading = viewModel.actionableState().isPerformingAction,
onClick = {
viewModel.onOpenConversation(onOpenConversation) {
unableStartConversationDialogState.show(UnableStartConversationDialogState(fullName))
unableStartConversationDialogState.show(UnableStartConversationDialogState(fullName, it))
}
},
)
Expand Down Expand Up @@ -189,16 +191,60 @@ fun ConnectionActionButton(
@Composable
fun UnableStartConversationDialogContent(dialogState: VisibilityState<UnableStartConversationDialogState>) {
VisibilityState(dialogState) { state ->
val title: String
val text: AnnotatedString

when (state.error) {
is CoreFailure.MissingKeyPackages -> {
title = stringResource(id = R.string.missing_keypackage_dialog_title)
text = LocalContext.current.resources.stringWithStyledArgs(
R.string.missing_keypackage_dialog_body,
MaterialTheme.wireTypography.body01,
MaterialTheme.wireTypography.body02,
colorsScheme().onBackground,
colorsScheme().onBackground,
state.userName
)
}

is CoreFailure.NoCommonProtocolFound.SelfNeedToUpdate -> {
title = stringResource(id = R.string.missing_keypackage_dialog_title)
text = LocalContext.current.resources.stringWithStyledArgs(
R.string.no_common_protocol_dialog_body_self_need_update,
MaterialTheme.wireTypography.body01,
MaterialTheme.wireTypography.body02,
colorsScheme().onBackground,
colorsScheme().onBackground,
state.userName
)
}

is CoreFailure.NoCommonProtocolFound.OtherNeedToUpdate -> {
title = stringResource(id = R.string.missing_keypackage_dialog_title)
text = LocalContext.current.resources.stringWithStyledArgs(
R.string.no_common_protocol_dialog_body_other_need_update,
MaterialTheme.wireTypography.body01,
MaterialTheme.wireTypography.body02,
colorsScheme().onBackground,
colorsScheme().onBackground,
state.userName
)
}

else -> {
title = stringResource(id = R.string.error_unknown_title)
text = LocalContext.current.resources.stringWithStyledArgs(
R.string.error_unknown_message,
MaterialTheme.wireTypography.body01,
MaterialTheme.wireTypography.body02,
colorsScheme().onBackground,
colorsScheme().onBackground
)
}
}
WireDialog(
title = stringResource(id = R.string.missing_keypackage_dialog_title),
text = LocalContext.current.resources.stringWithStyledArgs(
R.string.missing_keypackage_dialog_body,
MaterialTheme.wireTypography.body01,
MaterialTheme.wireTypography.body02,
colorsScheme().onBackground,
colorsScheme().onBackground,
state.userName
),
title = title,
text = text,
onDismiss = dialogState::dismiss,
optionButton1Properties = WireDialogButtonProperties(
onClick = dialogState::dismiss,
Expand All @@ -209,7 +255,7 @@ fun UnableStartConversationDialogContent(dialogState: VisibilityState<UnableStar
}
}

data class UnableStartConversationDialogState(val userName: String)
data class UnableStartConversationDialogState(val userName: String, val error: CoreFailure)

@Composable
@PreviewMultipleThemes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ interface ConnectionActionButtonViewModel {
fun onAcceptConnectionRequest() {}
fun onIgnoreConnectionRequest(onSuccess: (userName: String) -> Unit) {}
fun onUnblockUser() {}
fun onOpenConversation(onSuccess: (conversationId: ConversationId) -> Unit, onMissingKeyPackages: () -> Unit) {}
fun onOpenConversation(onSuccess: (conversationId: ConversationId) -> Unit, onFailure: (CoreFailure) -> Unit) {}
}

@Suppress("LongParameterList", "TooManyFunctions")
Expand Down Expand Up @@ -193,14 +193,17 @@ class ConnectionActionButtonViewModelImpl @Inject constructor(
}
}

override fun onOpenConversation(onSuccess: (conversationId: ConversationId) -> Unit, onMissingKeyPackages: () -> Unit) {
override fun onOpenConversation(
onSuccess: (conversationId: ConversationId) -> Unit,
onFailure: (error: CoreFailure) -> Unit
) {
viewModelScope.launch {
state = state.performAction()
when (val result = withContext(dispatchers.io()) { getOrCreateOneToOneConversation(userId) }) {
is CreateConversationResult.Failure -> {
appLogger.d(("Couldn't retrieve or create the conversation"))
appLogger.d(("Couldn't retrieve or create the conversation. Error ${result.coreFailure}"))
state = state.finishAction()
if (result.coreFailure is CoreFailure.MissingKeyPackages) onMissingKeyPackages()
onFailure(result.coreFailure)
}

is CreateConversationResult.Success -> onSuccess(result.conversation.id)
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,10 @@
<!-- Missing keyPackages dialog -->
<string name="missing_keypackage_dialog_title">Unable to start conversation</string>
<string name="missing_keypackage_dialog_body">You can’t start the conversation with %1$s right now. %1$s needs to open Wire or log in again first. Please try again later.</string>
<!-- No Common protocol dialog -->
<string name="no_common_protocol_dialog_title">Unable to start conversation</string>
<string name="no_common_protocol_dialog_body_self_need_update">You can’t communicate with %1$s, as your device doesn’t support the suitable protocol. Download the latest MLS Wire version, to call, and send messages and files.</string>
<string name="no_common_protocol_dialog_body_other_need_update">You can’t communicate with %1$s, as you two use different protocols. When %1$s gets an update, you can call and send messages and files.</string>
<!-- Gallery -->
<string name="media_gallery_default_title_name">Media Gallery</string>
<string name="media_gallery_on_image_downloaded">Saved to Downloads folder</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,14 +248,14 @@ class ConnectionActionButtonViewModelTest {
.arrange()

// when
viewModel.onOpenConversation(arrangement.onOpenConversation, arrangement.onMissingKeyPackages)
viewModel.onOpenConversation(arrangement.onOpenConversation, arrangement.onStartConversationError)

// then
coVerify {
arrangement.getOrCreateOneToOneConversation(TestUser.USER_ID)
}
verify { arrangement.onOpenConversation(any()) }
verify { arrangement.onMissingKeyPackages wasNot Called }
verify { arrangement.onStartConversationError wasNot Called }
}

@Test
Expand All @@ -267,33 +267,34 @@ class ConnectionActionButtonViewModelTest {
.arrange()

// when
viewModel.onOpenConversation(arrangement.onOpenConversation, arrangement.onMissingKeyPackages)
viewModel.onOpenConversation(arrangement.onOpenConversation, arrangement.onStartConversationError)

// then
coVerify {
arrangement.getOrCreateOneToOneConversation(TestUser.USER_ID)
}
verify { arrangement.onOpenConversation wasNot Called }
verify { arrangement.onMissingKeyPackages wasNot Called }
verify { arrangement.onStartConversationError(eq(failure)) }
}

@Test
fun `given a conversationId, when trying to open the conversation and fails with MissingKeyPackages, then call MissingKeyPackage()`() =
runTest {
// given
val errorResult = CoreFailure.MissingKeyPackages(setOf())
val (arrangement, viewModel) = ConnectionActionButtonHiltArrangement()
.withGetOneToOneConversation(CreateConversationResult.Failure(CoreFailure.MissingKeyPackages(setOf())))
.withGetOneToOneConversation(CreateConversationResult.Failure(errorResult))
.arrange()

// when
viewModel.onOpenConversation(arrangement.onOpenConversation, arrangement.onMissingKeyPackages)
viewModel.onOpenConversation(arrangement.onOpenConversation, arrangement.onStartConversationError)

// then
coVerify {
arrangement.getOrCreateOneToOneConversation(TestUser.USER_ID)
}
verify { arrangement.onOpenConversation wasNot Called }
verify { arrangement.onMissingKeyPackages() }
verify { arrangement.onStartConversationError(eq(errorResult)) }
}

companion object {
Expand Down Expand Up @@ -337,7 +338,7 @@ internal class ConnectionActionButtonHiltArrangement {
lateinit var onOpenConversation: (conversationId: ConversationId) -> Unit

@MockK(relaxed = true)
lateinit var onMissingKeyPackages: () -> Unit
lateinit var onStartConversationError: (CoreFailure) -> Unit

private val viewModel by lazy {
ConnectionActionButtonViewModelImpl(
Expand Down
2 changes: 1 addition & 1 deletion kalium
Submodule kalium updated 22 files
+10 −1 logic/src/commonMain/kotlin/com/wire/kalium/logic/CoreFailure.kt
+6 −2 logic/src/commonMain/kotlin/com/wire/kalium/logic/configuration/E2EISettings.kt
+30 −22 logic/src/commonMain/kotlin/com/wire/kalium/logic/data/e2ei/CertificateRevocationListRepository.kt
+6 −2 logic/src/commonMain/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigMapper.kt
+3 −1 logic/src/commonMain/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigModel.kt
+2 −1 logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt
+4 −2 logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/featureConfig/handler/E2EIConfigHandler.kt
+2 −1 logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/protocol/OneOnOneProtocolSelector.kt
+84 −1 logic/src/commonTest/kotlin/com/wire/kalium/logic/data/e2ei/CertificateRevocationListRepositoryTest.kt
+4 −4 logic/src/commonTest/kotlin/com/wire/kalium/logic/data/e2ei/E2EIRepositoryTest.kt
+1 −1 logic/src/commonTest/kotlin/com/wire/kalium/logic/data/event/FeatureConfigMapperTest.kt
+2 −2 logic/src/commonTest/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigRepositoryTest.kt
+1 −1 logic/src/commonTest/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigTest.kt
+1 −1 logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/client/ObserveE2EIRequiredUseCaseTest.kt
+1 −1 logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/client/RegisterMLSClientUseCaseTest.kt
+1 −1 .../commonTest/kotlin/com/wire/kalium/logic/feature/conversation/GetOrCreateOneToOneConversationUseCaseTest.kt
+1 −1 logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/featureConfig/SyncFeatureConfigsUseCaseTest.kt
+16 −1 logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/protocol/OneOnOneProtocolSelectorTest.kt
+4 −0 ...rc/commonMain/kotlin/com/wire/kalium/network/api/base/authenticated/featureConfigs/FeatureConfigResponse.kt
+5 −5 network/src/commonMain/kotlin/com/wire/kalium/network/api/base/unbound/acme/ACMEApi.kt
+1 −1 network/src/commonTest/kotlin/com/wire/kalium/model/FeatureConfigJson.kt
+2 −0 persistence/src/commonMain/kotlin/com/wire/kalium/persistence/config/UserConfigStorage.kt
Loading