Refresh Element Classic state each time ClassicFlowNode is resumed.
This ensure that Element X is always up to date regarding Element Classic state.
This commit is contained in:
parent
76de9db94e
commit
fd3c4c2b2b
14 changed files with 30 additions and 119 deletions
|
|
@ -43,7 +43,6 @@ interface ElementClassicConnection {
|
||||||
fun start()
|
fun start()
|
||||||
fun stop()
|
fun stop()
|
||||||
fun requestSession()
|
fun requestSession()
|
||||||
fun requestAvatar(userId: UserId)
|
|
||||||
val stateFlow: StateFlow<ElementClassicConnectionState>
|
val stateFlow: StateFlow<ElementClassicConnectionState>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -174,7 +173,7 @@ class DefaultElementClassicConnection(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun requestAvatar(userId: UserId) {
|
private fun requestAvatar(userId: UserId) {
|
||||||
Timber.tag(loggerTag.value).d("requestAvatar()")
|
Timber.tag(loggerTag.value).d("requestAvatar()")
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
val finalMessenger = messenger
|
val finalMessenger = messenger
|
||||||
|
|
@ -225,6 +224,11 @@ class DefaultElementClassicConnection(
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
val updatedState = ensureHomeserverIsSupported(state)
|
val updatedState = ensureHomeserverIsSupported(state)
|
||||||
emitState(updatedState)
|
emitState(updatedState)
|
||||||
|
val userId = (updatedState as? ElementClassicConnectionState.ElementClassicReady)?.elementClassicSession?.userId
|
||||||
|
if (userId != null) {
|
||||||
|
// Step 2, request the avatar
|
||||||
|
requestAvatar(userId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -241,11 +245,15 @@ class DefaultElementClassicConnection(
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
val avatar = BundleCompat.getParcelable(data, KEY_USER_AVATAR_PARCELABLE, Bitmap::class.java)
|
val avatar = BundleCompat.getParcelable(data, KEY_USER_AVATAR_PARCELABLE, Bitmap::class.java)
|
||||||
val updatedState = currentState.copy(
|
// If the avatar is identical to the current one, do not emit a new state to avoid unnecessary recompositions
|
||||||
avatar = avatar,
|
// and blink on the avatar image
|
||||||
)
|
if (avatar == null || !avatar.sameAs(currentState.avatar)) {
|
||||||
coroutineScope.launch {
|
val updatedState = currentState.copy(
|
||||||
emitState(updatedState)
|
avatar = avatar,
|
||||||
|
)
|
||||||
|
coroutineScope.launch {
|
||||||
|
emitState(updatedState)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -343,6 +351,10 @@ class DefaultElementClassicConnection(
|
||||||
append(doesContainBackupKey)
|
append(doesContainBackupKey)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
// Ensure avatar is not lost when refreshing the data
|
||||||
|
val currentAvatar = (stateFlow.value as? ElementClassicConnectionState.ElementClassicReady)
|
||||||
|
?.takeIf { it.elementClassicSession.userId == userId }
|
||||||
|
?.avatar
|
||||||
ElementClassicConnectionState.ElementClassicReady(
|
ElementClassicConnectionState.ElementClassicReady(
|
||||||
elementClassicSession = ElementClassicSession(
|
elementClassicSession = ElementClassicSession(
|
||||||
userId = userId,
|
userId = userId,
|
||||||
|
|
@ -352,7 +364,7 @@ class DefaultElementClassicConnection(
|
||||||
doesContainBackupKey = doesContainBackupKey,
|
doesContainBackupKey = doesContainBackupKey,
|
||||||
),
|
),
|
||||||
displayName = displayName,
|
displayName = displayName,
|
||||||
avatar = null,
|
avatar = currentAvatar,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import android.os.Parcelable
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.bumble.appyx.core.lifecycle.subscribe
|
||||||
import com.bumble.appyx.core.modality.BuildContext
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
import com.bumble.appyx.core.node.Node
|
import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
|
|
@ -75,6 +76,11 @@ class ClassicFlowNode(
|
||||||
override fun onBuilt() {
|
override fun onBuilt() {
|
||||||
super.onBuilt()
|
super.onBuilt()
|
||||||
observeElementClassicConnection()
|
observeElementClassicConnection()
|
||||||
|
lifecycle.subscribe(
|
||||||
|
onResume = {
|
||||||
|
classicFlowNodeHelper.onResume()
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeElementClassicConnection() {
|
private fun observeElementClassicConnection() {
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,10 @@ class ClassicFlowNodeHelper(
|
||||||
private val elementClassicConnection: ElementClassicConnection,
|
private val elementClassicConnection: ElementClassicConnection,
|
||||||
private val sessionStore: SessionStore,
|
private val sessionStore: SessionStore,
|
||||||
) {
|
) {
|
||||||
|
fun onResume() {
|
||||||
|
elementClassicConnection.requestSession()
|
||||||
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
fun navigationEventFlow(): Flow<NavigationEvent> {
|
fun navigationEventFlow(): Flow<NavigationEvent> {
|
||||||
return elementClassicConnection.stateFlow
|
return elementClassicConnection.stateFlow
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@
|
||||||
package io.element.android.features.login.impl.screens.classic.loginwithclassic
|
package io.element.android.features.login.impl.screens.classic.loginwithclassic
|
||||||
|
|
||||||
sealed interface LoginWithClassicEvent {
|
sealed interface LoginWithClassicEvent {
|
||||||
data object RefreshData : LoginWithClassicEvent
|
|
||||||
data object Submit : LoginWithClassicEvent
|
data object Submit : LoginWithClassicEvent
|
||||||
data object ClearError : LoginWithClassicEvent
|
data object ClearError : LoginWithClassicEvent
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,13 +56,6 @@ class LoginWithClassicPresenter(
|
||||||
|
|
||||||
fun handleEvent(event: LoginWithClassicEvent) {
|
fun handleEvent(event: LoginWithClassicEvent) {
|
||||||
when (event) {
|
when (event) {
|
||||||
LoginWithClassicEvent.RefreshData -> {
|
|
||||||
// Request the avatar if not known yet
|
|
||||||
val currentState = elementClassicConnection.stateFlow.value
|
|
||||||
if ((currentState as? ElementClassicConnectionState.ElementClassicReady)?.avatar == null) {
|
|
||||||
elementClassicConnection.requestAvatar(userId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LoginWithClassicEvent.Submit -> {
|
LoginWithClassicEvent.Submit -> {
|
||||||
val currentState = elementClassicConnection.stateFlow.value
|
val currentState = elementClassicConnection.stateFlow.value
|
||||||
if (currentState is ElementClassicConnectionState.ElementClassicReady) {
|
if (currentState is ElementClassicConnectionState.ElementClassicReady) {
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,6 @@ import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.compose.LifecycleEventEffect
|
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.features.login.impl.R
|
import io.element.android.features.login.impl.R
|
||||||
import io.element.android.features.login.impl.login.LoginModeView
|
import io.element.android.features.login.impl.login.LoginModeView
|
||||||
|
|
@ -67,10 +65,6 @@ fun LoginWithClassicView(
|
||||||
onCreateAccountContinue: (url: String) -> Unit,
|
onCreateAccountContinue: (url: String) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
LifecycleEventEffect(Lifecycle.Event.ON_RESUME) {
|
|
||||||
state.eventSink(LoginWithClassicEvent.RefreshData)
|
|
||||||
}
|
|
||||||
|
|
||||||
val isLoading by remember(state.loginMode) {
|
val isLoading by remember(state.loginMode) {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
state.loginMode is AsyncData.Loading
|
state.loginMode is AsyncData.Loading
|
||||||
|
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2026 Element Creations Ltd.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
|
||||||
* Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.element.android.features.login.impl.screens.classic.missingkeybackup
|
|
||||||
|
|
||||||
sealed interface MissingKeyBackupEvent {
|
|
||||||
data object OnResume : MissingKeyBackupEvent
|
|
||||||
}
|
|
||||||
|
|
@ -8,38 +8,18 @@
|
||||||
package io.element.android.features.login.impl.screens.classic.missingkeybackup
|
package io.element.android.features.login.impl.screens.classic.missingkeybackup
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.Inject
|
||||||
import io.element.android.features.login.impl.classic.ElementClassicConnection
|
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.core.meta.BuildMeta
|
import io.element.android.libraries.core.meta.BuildMeta
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
class MissingKeyBackupPresenter(
|
class MissingKeyBackupPresenter(
|
||||||
private val buildMeta: BuildMeta,
|
private val buildMeta: BuildMeta,
|
||||||
private val elementClassicConnection: ElementClassicConnection,
|
|
||||||
) : Presenter<MissingKeyBackupState> {
|
) : Presenter<MissingKeyBackupState> {
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): MissingKeyBackupState {
|
override fun present(): MissingKeyBackupState {
|
||||||
var resumeCounter by remember { mutableIntStateOf(0) }
|
|
||||||
fun handleEvent(event: MissingKeyBackupEvent) {
|
|
||||||
when (event) {
|
|
||||||
MissingKeyBackupEvent.OnResume -> {
|
|
||||||
resumeCounter++
|
|
||||||
if (resumeCounter > 1) {
|
|
||||||
// The user has returned to this screen, we can assume they have gone to the backup flow and are now back here
|
|
||||||
elementClassicConnection.requestSession()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return MissingKeyBackupState(
|
return MissingKeyBackupState(
|
||||||
appName = buildMeta.applicationName,
|
appName = buildMeta.applicationName,
|
||||||
eventSink = ::handleEvent,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,5 +9,4 @@ package io.element.android.features.login.impl.screens.classic.missingkeybackup
|
||||||
|
|
||||||
data class MissingKeyBackupState(
|
data class MissingKeyBackupState(
|
||||||
val appName: String,
|
val appName: String,
|
||||||
val eventSink: (MissingKeyBackupEvent) -> Unit
|
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,6 @@ open class MissingKeyBackupStateProvider : PreviewParameterProvider<MissingKeyBa
|
||||||
|
|
||||||
fun aMissingKeyBackupState(
|
fun aMissingKeyBackupState(
|
||||||
appName: String = "AppName",
|
appName: String = "AppName",
|
||||||
eventSink: (MissingKeyBackupEvent) -> Unit = {},
|
|
||||||
) = MissingKeyBackupState(
|
) = MissingKeyBackupState(
|
||||||
appName = appName,
|
appName = appName,
|
||||||
eventSink = eventSink
|
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||||
import io.element.android.features.login.impl.R
|
import io.element.android.features.login.impl.R
|
||||||
import io.element.android.libraries.designsystem.atomic.organisms.NumberedListOrganism
|
import io.element.android.libraries.designsystem.atomic.organisms.NumberedListOrganism
|
||||||
|
|
@ -25,7 +24,6 @@ import io.element.android.libraries.designsystem.components.BigIcon
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
import io.element.android.libraries.designsystem.theme.components.Button
|
import io.element.android.libraries.designsystem.theme.components.Button
|
||||||
import io.element.android.libraries.designsystem.utils.OnLifecycleEvent
|
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -35,11 +33,6 @@ fun MissingKeyBackupView(
|
||||||
onOpenClassicClick: () -> Unit,
|
onOpenClassicClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
OnLifecycleEvent { _, event ->
|
|
||||||
if (event == Lifecycle.Event.ON_RESUME) {
|
|
||||||
state.eventSink.invoke(MissingKeyBackupEvent.OnResume)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FlowStepPage(
|
FlowStepPage(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
onBackClick = onBackClick,
|
onBackClick = onBackClick,
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@
|
||||||
|
|
||||||
package io.element.android.features.login.impl.classic
|
package io.element.android.features.login.impl.classic
|
||||||
|
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
|
||||||
import io.element.android.tests.testutils.lambda.lambdaError
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
@ -17,13 +16,11 @@ class FakeElementClassicConnection(
|
||||||
private val startResult: () -> Unit = { lambdaError() },
|
private val startResult: () -> Unit = { lambdaError() },
|
||||||
private val stopResult: () -> Unit = { lambdaError() },
|
private val stopResult: () -> Unit = { lambdaError() },
|
||||||
private val requestSessionResult: () -> Unit = { lambdaError() },
|
private val requestSessionResult: () -> Unit = { lambdaError() },
|
||||||
private val requestAvatarResult: (UserId) -> Unit = { lambdaError() },
|
|
||||||
initialState: ElementClassicConnectionState = ElementClassicConnectionState.Idle
|
initialState: ElementClassicConnectionState = ElementClassicConnectionState.Idle
|
||||||
) : ElementClassicConnection {
|
) : ElementClassicConnection {
|
||||||
override fun start() = startResult()
|
override fun start() = startResult()
|
||||||
override fun stop() = stopResult()
|
override fun stop() = stopResult()
|
||||||
override fun requestSession() = requestSessionResult()
|
override fun requestSession() = requestSessionResult()
|
||||||
override fun requestAvatar(userId: UserId) = requestAvatarResult(userId)
|
|
||||||
private val mutableStateFlow = MutableStateFlow(initialState)
|
private val mutableStateFlow = MutableStateFlow(initialState)
|
||||||
override val stateFlow: StateFlow<ElementClassicConnectionState> = mutableStateFlow.asStateFlow()
|
override val stateFlow: StateFlow<ElementClassicConnectionState> = mutableStateFlow.asStateFlow()
|
||||||
suspend fun emitState(state: ElementClassicConnectionState) {
|
suspend fun emitState(state: ElementClassicConnectionState) {
|
||||||
|
|
|
||||||
|
|
@ -57,36 +57,6 @@ class LoginWithClassicPresenterTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `present - refresh data invokes the expected methods`() = runTest {
|
|
||||||
val requestAvatarResult = lambdaRecorder<UserId, Unit> { }
|
|
||||||
val elementClassicConnection = FakeElementClassicConnection(
|
|
||||||
startResult = {},
|
|
||||||
requestAvatarResult = requestAvatarResult,
|
|
||||||
)
|
|
||||||
val presenter = createPresenter(
|
|
||||||
elementClassicConnection = elementClassicConnection,
|
|
||||||
)
|
|
||||||
presenter.test {
|
|
||||||
skipItems(1)
|
|
||||||
elementClassicConnection.emitState(
|
|
||||||
anElementClassicReady(
|
|
||||||
elementClassicSession = anElementClassicSession(
|
|
||||||
userId = A_USER_ID,
|
|
||||||
secrets = A_SECRET,
|
|
||||||
roomKeysVersion = ROOM_KEYS_VERSION,
|
|
||||||
),
|
|
||||||
displayName = A_USER_NAME,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
val readyState = awaitItem()
|
|
||||||
assertThat(readyState.userId).isEqualTo(A_USER_ID)
|
|
||||||
assertThat(readyState.displayName).isEqualTo(A_USER_NAME)
|
|
||||||
readyState.eventSink(LoginWithClassicEvent.RefreshData)
|
|
||||||
requestAvatarResult.assertions().isCalledOnce()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - start login with correct state - user can login`() = runTest {
|
fun `present - start login with correct state - user can login`() = runTest {
|
||||||
val authenticationService = FakeMatrixAuthenticationService(
|
val authenticationService = FakeMatrixAuthenticationService(
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,9 @@
|
||||||
package io.element.android.features.login.impl.screens.classic.missingkeybackup
|
package io.element.android.features.login.impl.screens.classic.missingkeybackup
|
||||||
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.login.impl.classic.ElementClassicConnection
|
|
||||||
import io.element.android.features.login.impl.classic.FakeElementClassicConnection
|
|
||||||
import io.element.android.libraries.core.meta.BuildMeta
|
import io.element.android.libraries.core.meta.BuildMeta
|
||||||
import io.element.android.libraries.matrix.test.AN_APPLICATION_NAME
|
import io.element.android.libraries.matrix.test.AN_APPLICATION_NAME
|
||||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
|
||||||
import io.element.android.tests.testutils.test
|
import io.element.android.tests.testutils.test
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
@ -27,29 +24,10 @@ class MissingKeyBackupPresenterTest {
|
||||||
assertThat(initialState.appName).isEqualTo(AN_APPLICATION_NAME)
|
assertThat(initialState.appName).isEqualTo(AN_APPLICATION_NAME)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `present - when the screen is resumed twice, the start over method is called`() = runTest {
|
|
||||||
val requestSessionResult = lambdaRecorder<Unit> { }
|
|
||||||
val presenter = createPresenter(
|
|
||||||
elementClassicConnection = FakeElementClassicConnection(
|
|
||||||
requestSessionResult = requestSessionResult,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
presenter.test {
|
|
||||||
val initialState = awaitItem()
|
|
||||||
initialState.eventSink(MissingKeyBackupEvent.OnResume)
|
|
||||||
expectNoEvents()
|
|
||||||
initialState.eventSink(MissingKeyBackupEvent.OnResume)
|
|
||||||
requestSessionResult.assertions().isCalledOnce()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createPresenter(
|
private fun createPresenter(
|
||||||
buildMeta: BuildMeta = aBuildMeta(applicationName = AN_APPLICATION_NAME),
|
buildMeta: BuildMeta = aBuildMeta(applicationName = AN_APPLICATION_NAME),
|
||||||
elementClassicConnection: ElementClassicConnection = FakeElementClassicConnection(),
|
|
||||||
) = MissingKeyBackupPresenter(
|
) = MissingKeyBackupPresenter(
|
||||||
buildMeta = buildMeta,
|
buildMeta = buildMeta,
|
||||||
elementClassicConnection = elementClassicConnection,
|
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue