Skip to content

Commit

Permalink
fix: Re-fetching of devices certificates on comming back from DeviceD…
Browse files Browse the repository at this point in the history
…etails [WPB-6970] 🍒 (#3105)

Co-authored-by: boris <[email protected]>
  • Loading branch information
github-actions[bot] and borichellow authored Jun 24, 2024
1 parent 1ca3341 commit a669787
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@ import androidx.compose.material.icons.filled.ChevronRight
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.Lifecycle
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootNavGraph
import com.wire.android.R
Expand All @@ -48,6 +50,7 @@ import com.wire.android.ui.destinations.DeviceDetailsScreenDestination
import com.wire.android.ui.settings.devices.model.SelfDevicesState
import com.wire.android.ui.theme.wireColorScheme
import com.wire.android.util.extension.folderWithElements
import com.wire.android.util.lifecycle.rememberLifecycleEvent

@RootNavGraph
@Destination
Expand All @@ -56,6 +59,11 @@ fun SelfDevicesScreen(
navigator: Navigator,
viewModel: SelfDevicesViewModel = hiltViewModel()
) {
val lifecycleEvent = rememberLifecycleEvent()
LaunchedEffect(lifecycleEvent) {
if (lifecycleEvent == Lifecycle.Event.ON_RESUME) viewModel.loadCertificates()
}

SelfDevicesScreenContent(
state = viewModel.state,
onNavigateBack = navigator::navigateBack,
Expand Down Expand Up @@ -97,7 +105,7 @@ fun SelfDevicesScreenContent(
isE2EIEnabled = state.isE2EIEnabled,
onDeviceClick = onDeviceClick,

)
)
}
folderDeviceItems(
header = context.getString(R.string.other_devices_label),
Expand All @@ -113,6 +121,7 @@ fun SelfDevicesScreenContent(
}
)
}

@Suppress("LongParameterList")
private fun LazyListScope.folderDeviceItems(
header: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ import com.wire.kalium.logic.feature.client.ObserveCurrentClientIdUseCase
import com.wire.kalium.logic.feature.e2ei.usecase.GetUserE2eiCertificatesUseCase
import com.wire.kalium.logic.feature.user.IsE2EIEnabledUseCase
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import javax.inject.Inject

Expand All @@ -52,6 +55,9 @@ class SelfDevicesViewModel @Inject constructor(
)
private set

private val refreshE2eiCertificates: MutableSharedFlow<Unit> = MutableSharedFlow<Unit>()
private val observeUserE2eiCertificates = refreshE2eiCertificates.map { getUserE2eiCertificates(currentAccountId) }

init {
// this will cause the list to be refreshed
loadClientsList()
Expand All @@ -60,24 +66,24 @@ class SelfDevicesViewModel @Inject constructor(

private fun observeClientList() {
viewModelScope.launch {
observeClientList(currentAccountId).collect { result ->
state = when (result) {
is ObserveClientsByUserIdUseCase.Result.Failure -> state.copy(isLoadingClientsList = false)
is ObserveClientsByUserIdUseCase.Result.Success -> {
val currentClientId = currentClientIdUseCase().firstOrNull()
val e2eiCertificates = getUserE2eiCertificates(currentAccountId)
state.copy(
isLoadingClientsList = false,
currentDevice = result.clients
.firstOrNull { it.id == currentClientId }
?.let { Device(it, e2eiCertificates[it.id.value]) },
deviceList = result.clients
.filter { it.id != currentClientId }
.map { Device(it, e2eiCertificates[it.id.value]) }
)
observeClientList(currentAccountId).combine(observeUserE2eiCertificates, ::Pair)
.collect { (result, e2eiCertificates) ->
state = when (result) {
is ObserveClientsByUserIdUseCase.Result.Failure -> state.copy(isLoadingClientsList = false)
is ObserveClientsByUserIdUseCase.Result.Success -> {
val currentClientId = currentClientIdUseCase().firstOrNull()
state.copy(
isLoadingClientsList = false,
currentDevice = result.clients
.firstOrNull { it.id == currentClientId }
?.let { Device(it, e2eiCertificates[it.id.value]) },
deviceList = result.clients
.filter { it.id != currentClientId }
.map { Device(it, e2eiCertificates[it.id.value]) }
)
}
}
}
}
}
}

Expand All @@ -86,4 +92,10 @@ class SelfDevicesViewModel @Inject constructor(
fetchSelfClientsFromRemote()
}
}

fun loadCertificates() {
viewModelScope.launch {
refreshE2eiCertificates.emit(Unit)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* 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.util.lifecycle

import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner

@Composable
fun rememberLifecycleEvent(lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current): Lifecycle.Event {
var state by remember { mutableStateOf(Lifecycle.Event.ON_ANY) }
DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
state = event
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
return state
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ import com.wire.kalium.logic.feature.client.ObserveCurrentClientIdUseCase
import com.wire.kalium.logic.feature.client.SelfClientsResult
import com.wire.kalium.logic.feature.e2ei.usecase.GetUserE2eiCertificatesUseCase
import com.wire.kalium.logic.feature.user.IsE2EIEnabledUseCase
import io.mockk.coEvery
import io.mockk.MockKAnnotations
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.impl.annotations.MockK
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
Expand All @@ -49,14 +50,30 @@ class SelfDevicesViewModelTest {
// given
val (_, viewModel) = Arrangement()
.arrange()
val currentDevice = Device(TestClient.CLIENT)

// when
val currentDevice = Device(TestClient.CLIENT)
viewModel.loadCertificates()

// then
assert(!viewModel.state.deviceList.contains(currentDevice))
}

@Test
fun `given a self client id, when loadCertificates is called, then E2EI Certificates is fetched again`() =
runTest {
// given
val (arragne, viewModel) = Arrangement()
.arrange()

// when
viewModel.loadCertificates()
viewModel.loadCertificates()

// then
coVerify(exactly = 2) { arragne.getUserE2eiCertificates(any()) }
}

private class Arrangement {

@MockK
Expand Down

0 comments on commit a669787

Please sign in to comment.