Benoit Marty
2 weeks ago
20 changed files with 1 additions and 418 deletions
@ -1,25 +0,0 @@
@@ -1,25 +0,0 @@
|
||||
/* |
||||
* Copyright (c) 2023 New Vector Ltd |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package io.element.android.features.roomlist.api.migration |
||||
|
||||
import io.element.android.libraries.matrix.api.core.SessionId |
||||
|
||||
interface MigrationScreenStore { |
||||
fun isMigrationScreenNeeded(sessionId: SessionId): Boolean |
||||
fun setMigrationScreenShown(sessionId: SessionId) |
||||
fun reset() |
||||
} |
@ -1,50 +0,0 @@
@@ -1,50 +0,0 @@
|
||||
/* |
||||
* Copyright (c) 2024 New Vector Ltd |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package io.element.android.features.roomlist.impl.migration |
||||
|
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.runtime.LaunchedEffect |
||||
import androidx.compose.runtime.collectAsState |
||||
import androidx.compose.runtime.getValue |
||||
import androidx.compose.runtime.mutableStateOf |
||||
import androidx.compose.runtime.remember |
||||
import androidx.compose.runtime.setValue |
||||
import io.element.android.features.roomlist.api.migration.MigrationScreenStore |
||||
import io.element.android.libraries.architecture.Presenter |
||||
import io.element.android.libraries.matrix.api.MatrixClient |
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService |
||||
import javax.inject.Inject |
||||
|
||||
class MigrationScreenPresenter @Inject constructor( |
||||
private val matrixClient: MatrixClient, |
||||
private val migrationScreenStore: MigrationScreenStore, |
||||
) : Presenter<MigrationScreenState> { |
||||
@Composable |
||||
override fun present(): MigrationScreenState { |
||||
val roomListState by matrixClient.roomListService.state.collectAsState() |
||||
var needsMigration by remember { mutableStateOf(migrationScreenStore.isMigrationScreenNeeded(matrixClient.sessionId)) } |
||||
if (roomListState == RoomListService.State.Running) { |
||||
LaunchedEffect(Unit) { |
||||
needsMigration = false |
||||
migrationScreenStore.setMigrationScreenShown(matrixClient.sessionId) |
||||
} |
||||
} |
||||
return MigrationScreenState( |
||||
isMigrating = needsMigration && roomListState != RoomListService.State.Running |
||||
) |
||||
} |
||||
} |
@ -1,21 +0,0 @@
@@ -1,21 +0,0 @@
|
||||
/* |
||||
* Copyright (c) 2023 New Vector Ltd |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package io.element.android.features.roomlist.impl.migration |
||||
|
||||
data class MigrationScreenState( |
||||
val isMigrating: Boolean |
||||
) |
@ -1,55 +0,0 @@
@@ -1,55 +0,0 @@
|
||||
/* |
||||
* Copyright (c) 2024 New Vector Ltd |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package io.element.android.features.roomlist.impl.migration |
||||
|
||||
import androidx.compose.animation.AnimatedVisibility |
||||
import androidx.compose.animation.fadeIn |
||||
import androidx.compose.animation.fadeOut |
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.ui.Modifier |
||||
import androidx.compose.ui.res.stringResource |
||||
import io.element.android.features.roomlist.impl.R |
||||
import io.element.android.libraries.designsystem.atomic.pages.SunsetPage |
||||
import io.element.android.libraries.designsystem.preview.ElementPreview |
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight |
||||
|
||||
@Composable |
||||
fun MigrationScreenView( |
||||
isMigrating: Boolean, |
||||
modifier: Modifier = Modifier, |
||||
) { |
||||
AnimatedVisibility( |
||||
visible = isMigrating, |
||||
enter = fadeIn(), |
||||
exit = fadeOut(), |
||||
label = "Migration view fade", |
||||
) { |
||||
SunsetPage( |
||||
modifier = modifier, |
||||
isLoading = true, |
||||
title = stringResource(id = R.string.screen_migration_title), |
||||
subtitle = stringResource(id = R.string.screen_migration_message), |
||||
overallContent = {} |
||||
) |
||||
} |
||||
} |
||||
|
||||
@Composable |
||||
@PreviewsDayNight |
||||
internal fun MigrationScreenViewPreview() = ElementPreview { |
||||
MigrationScreenView(isMigrating = true) |
||||
} |
@ -1,59 +0,0 @@
@@ -1,59 +0,0 @@
|
||||
/* |
||||
* Copyright (c) 2024 New Vector Ltd |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package io.element.android.features.roomlist.impl.migration |
||||
|
||||
import android.content.SharedPreferences |
||||
import androidx.core.content.edit |
||||
import com.squareup.anvil.annotations.ContributesBinding |
||||
import io.element.android.features.roomlist.api.migration.MigrationScreenStore |
||||
import io.element.android.libraries.androidutils.hash.hash |
||||
import io.element.android.libraries.di.AppScope |
||||
import io.element.android.libraries.matrix.api.core.SessionId |
||||
import javax.inject.Inject |
||||
|
||||
@ContributesBinding(AppScope::class) |
||||
class SharedPreferencesMigrationScreenStore @Inject constructor( |
||||
private val sharedPreferences: SharedPreferences, |
||||
) : MigrationScreenStore { |
||||
override fun isMigrationScreenNeeded(sessionId: SessionId): Boolean { |
||||
return sharedPreferences.getBoolean(sessionId.toKey(), false).not() |
||||
} |
||||
|
||||
override fun setMigrationScreenShown(sessionId: SessionId) { |
||||
sharedPreferences.edit().putBoolean(sessionId.toKey(), true).apply() |
||||
} |
||||
|
||||
override fun reset() { |
||||
sharedPreferences.edit { |
||||
sharedPreferences.all.keys |
||||
.filter { it.startsWith(IS_MIGRATION_SCREEN_SHOWN_PREFIX) } |
||||
.forEach { |
||||
remove(it) |
||||
} |
||||
} |
||||
} |
||||
|
||||
private fun SessionId.toKey(): String { |
||||
// Hash the sessionId to get rid of exotic char and take only the first 16 chars, |
||||
// The risk of collision is not high. |
||||
return IS_MIGRATION_SCREEN_SHOWN_PREFIX + value.hash().take(16) |
||||
} |
||||
|
||||
companion object { |
||||
private const val IS_MIGRATION_SCREEN_SHOWN_PREFIX = "is_migration_screen_shown_" |
||||
} |
||||
} |
@ -1,76 +0,0 @@
@@ -1,76 +0,0 @@
|
||||
/* |
||||
* Copyright (c) 2024 New Vector Ltd |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package io.element.android.features.roomlist.impl.migration |
||||
|
||||
import app.cash.molecule.RecompositionMode |
||||
import app.cash.molecule.moleculeFlow |
||||
import app.cash.turbine.test |
||||
import com.google.common.truth.Truth.assertThat |
||||
import io.element.android.features.roomlist.api.migration.MigrationScreenStore |
||||
import io.element.android.libraries.matrix.api.MatrixClient |
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService |
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID |
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient |
||||
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService |
||||
import io.element.android.tests.testutils.WarmUpRule |
||||
import kotlinx.coroutines.test.runTest |
||||
import org.junit.Rule |
||||
import org.junit.Test |
||||
|
||||
class MigrationScreenPresenterTest { |
||||
@get:Rule |
||||
val warmUpRule = WarmUpRule() |
||||
|
||||
@Test |
||||
fun `present - initial`() = runTest { |
||||
val presenter = createPresenter() |
||||
moleculeFlow(RecompositionMode.Immediate) { |
||||
presenter.present() |
||||
}.test { |
||||
val initialState = awaitItem() |
||||
assertThat(initialState.isMigrating).isTrue() |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `present - migration end`() = runTest { |
||||
val matrixClient = FakeMatrixClient() |
||||
val migrationScreenStore = InMemoryMigrationScreenStore() |
||||
val presenter = createPresenter(matrixClient, migrationScreenStore) |
||||
moleculeFlow(RecompositionMode.Immediate) { |
||||
presenter.present() |
||||
}.test { |
||||
val initialState = awaitItem() |
||||
assertThat(initialState.isMigrating).isTrue() |
||||
assertThat(migrationScreenStore.isMigrationScreenNeeded(A_SESSION_ID)).isTrue() |
||||
// Simulate room list loaded |
||||
(matrixClient.roomListService as FakeRoomListService).postState(RoomListService.State.Running) |
||||
val nextState = awaitItem() |
||||
assertThat(nextState.isMigrating).isFalse() |
||||
assertThat(migrationScreenStore.isMigrationScreenNeeded(A_SESSION_ID)).isFalse() |
||||
cancelAndIgnoreRemainingEvents() |
||||
} |
||||
} |
||||
|
||||
private fun createPresenter( |
||||
matrixClient: MatrixClient = FakeMatrixClient(), |
||||
migrationScreenStore: MigrationScreenStore = InMemoryMigrationScreenStore(), |
||||
) = MigrationScreenPresenter( |
||||
matrixClient, |
||||
migrationScreenStore, |
||||
) |
||||
} |
@ -1,28 +0,0 @@
@@ -1,28 +0,0 @@
|
||||
/* |
||||
* Copyright (c) 2024 New Vector Ltd |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
plugins { |
||||
id("io.element.android-library") |
||||
} |
||||
|
||||
android { |
||||
namespace = "io.element.android.features.roomlist.test" |
||||
} |
||||
|
||||
dependencies { |
||||
implementation(projects.features.roomlist.api) |
||||
implementation(projects.libraries.matrix.api) |
||||
implementation(projects.tests.testutils) |
||||
} |
@ -1,40 +0,0 @@
@@ -1,40 +0,0 @@
|
||||
/* |
||||
* Copyright (c) 2024 New Vector Ltd |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package io.element.android.features.roomlist.impl.migration |
||||
|
||||
import io.element.android.features.roomlist.api.migration.MigrationScreenStore |
||||
import io.element.android.libraries.matrix.api.core.SessionId |
||||
|
||||
class InMemoryMigrationScreenStore( |
||||
private val resetLambda: () -> Unit = { } |
||||
) : MigrationScreenStore { |
||||
private val store = mutableMapOf<SessionId, Boolean>() |
||||
|
||||
override fun isMigrationScreenNeeded(sessionId: SessionId): Boolean { |
||||
// If store does not have key return true, else return the opposite of the value |
||||
return store[sessionId]?.not() ?: true |
||||
} |
||||
|
||||
override fun setMigrationScreenShown(sessionId: SessionId) { |
||||
store[sessionId] = true |
||||
} |
||||
|
||||
override fun reset() { |
||||
store.clear() |
||||
resetLambda() |
||||
} |
||||
} |
Loading…
Reference in new issue