Move migration screen to within the room list (#2361)
* Rename migration bg drawable and add night variant * Move `migration` package from `ftue` to `messages:impl` module * Update `SunsetPage` with light and dark modes * Fix bloom colors when nested theme is used * Integrate the migration screen in the room list * Fix `WaitListView` cancel button color * Clear migration store when removing the app's cache --------- Co-authored-by: ElementBot <benoitm+elementbot@element.io>
This commit is contained in:
parent
52dfaea741
commit
d06e5c23cb
62 changed files with 274 additions and 291 deletions
|
|
@ -33,6 +33,7 @@ import io.element.android.features.networkmonitor.api.NetworkMonitor
|
|||
import io.element.android.features.networkmonitor.api.NetworkStatus
|
||||
import io.element.android.features.roomlist.impl.datasource.InviteStateDataSource
|
||||
import io.element.android.features.roomlist.impl.datasource.RoomListDataSource
|
||||
import io.element.android.features.roomlist.impl.migration.MigrationScreenPresenter
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||
|
|
@ -63,6 +64,7 @@ class RoomListPresenter @Inject constructor(
|
|||
private val encryptionService: EncryptionService,
|
||||
private val featureFlagService: FeatureFlagService,
|
||||
private val indicatorService: IndicatorService,
|
||||
private val migrationScreenPresenter: MigrationScreenPresenter,
|
||||
) : Presenter<RoomListState> {
|
||||
@Composable
|
||||
override fun present(): RoomListState {
|
||||
|
|
@ -82,6 +84,8 @@ class RoomListPresenter @Inject constructor(
|
|||
initialLoad(matrixUser)
|
||||
}
|
||||
|
||||
val isMigrating = migrationScreenPresenter.present().isMigrating
|
||||
|
||||
// Session verification status (unknown, not verified, verified)
|
||||
val canVerifySession by sessionVerificationService.canVerifySessionFlow.collectAsState(initial = false)
|
||||
var verificationPromptDismissed by rememberSaveable { mutableStateOf(false) }
|
||||
|
|
@ -148,6 +152,7 @@ class RoomListPresenter @Inject constructor(
|
|||
displaySearchResults = displaySearchResults,
|
||||
contextMenu = contextMenu,
|
||||
leaveRoomState = leaveRoomState,
|
||||
displayMigrationStatus = isMigrating,
|
||||
eventSink = ::handleEvents
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ data class RoomListState(
|
|||
val displaySearchResults: Boolean,
|
||||
val contextMenu: ContextMenu,
|
||||
val leaveRoomState: LeaveRoomState,
|
||||
val displayMigrationStatus: Boolean,
|
||||
val eventSink: (RoomListEvents) -> Unit,
|
||||
) {
|
||||
sealed interface ContextMenu {
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ open class RoomListStateProvider : PreviewParameterProvider<RoomListState> {
|
|||
aRoomListState().copy(displayRecoveryKeyPrompt = true),
|
||||
aRoomListState().copy(roomList = AsyncData.Success(persistentListOf())),
|
||||
aRoomListState().copy(roomList = AsyncData.Loading(prevData = RoomListRoomSummaryFactory.createFakeList())),
|
||||
aRoomListState().copy(matrixUser = null, displayMigrationStatus = true),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -70,6 +71,7 @@ internal fun aRoomListState() = RoomListState(
|
|||
displaySearchResults = false,
|
||||
contextMenu = RoomListState.ContextMenu.Hidden,
|
||||
leaveRoomState = aLeaveRoomState(),
|
||||
displayMigrationStatus = false,
|
||||
eventSink = {}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ import io.element.android.features.roomlist.impl.components.RequestVerificationH
|
|||
import io.element.android.features.roomlist.impl.components.RoomListMenuAction
|
||||
import io.element.android.features.roomlist.impl.components.RoomListTopBar
|
||||
import io.element.android.features.roomlist.impl.components.RoomSummaryRow
|
||||
import io.element.android.features.roomlist.impl.migration.MigrationScreenView
|
||||
import io.element.android.features.roomlist.impl.model.RoomListRoomSummary
|
||||
import io.element.android.features.roomlist.impl.search.RoomListSearchResultView
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
|
|
@ -212,6 +213,7 @@ private fun RoomListContent(
|
|||
onMenuActionClicked = onMenuActionClicked,
|
||||
onOpenSettings = onOpenSettings,
|
||||
scrollBehavior = scrollBehavior,
|
||||
displayMenuItems = !state.displayMigrationStatus,
|
||||
)
|
||||
},
|
||||
content = { padding ->
|
||||
|
|
@ -270,18 +272,22 @@ private fun RoomListContent(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
MigrationScreenView(isMigrating = state.displayMigrationStatus)
|
||||
},
|
||||
floatingActionButton = {
|
||||
FloatingActionButton(
|
||||
// FIXME align on Design system theme
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
onClick = onCreateRoomClicked
|
||||
) {
|
||||
Icon(
|
||||
// Note cannot use Icons.Outlined.EditSquare, it does not exist :/
|
||||
resourceId = CommonDrawables.ic_new_message,
|
||||
contentDescription = stringResource(id = R.string.screen_roomlist_a11y_create_message)
|
||||
)
|
||||
if (!state.displayMigrationStatus) {
|
||||
FloatingActionButton(
|
||||
// FIXME align on Design system theme
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
onClick = onCreateRoomClicked
|
||||
) {
|
||||
Icon(
|
||||
// Note cannot use Icons.Outlined.EditSquare, it does not exist :/
|
||||
resourceId = CommonDrawables.ic_new_message,
|
||||
contentDescription = stringResource(id = R.string.screen_roomlist_a11y_create_message)
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
snackbarHost = { SnackbarHost(snackbarHostState) },
|
||||
|
|
|
|||
|
|
@ -21,8 +21,10 @@ import androidx.compose.foundation.layout.Box
|
|||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.statusBars
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
|
|
@ -68,6 +70,7 @@ import io.element.android.libraries.designsystem.theme.components.HorizontalDivi
|
|||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.IconButton
|
||||
import io.element.android.libraries.designsystem.theme.components.MediumTopAppBar
|
||||
import io.element.android.libraries.designsystem.theme.components.Surface
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
|
|
@ -89,6 +92,7 @@ fun RoomListTopBar(
|
|||
onMenuActionClicked: (RoomListMenuAction) -> Unit,
|
||||
onOpenSettings: () -> Unit,
|
||||
scrollBehavior: TopAppBarScrollBehavior,
|
||||
displayMenuItems: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
fun closeFilter() {
|
||||
|
|
@ -108,6 +112,7 @@ fun RoomListTopBar(
|
|||
onSearchClicked = onToggleSearch,
|
||||
onMenuActionClicked = onMenuActionClicked,
|
||||
scrollBehavior = scrollBehavior,
|
||||
displayMenuItems = displayMenuItems,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
|
@ -122,6 +127,7 @@ private fun DefaultRoomListTopBar(
|
|||
onOpenSettings: () -> Unit,
|
||||
onSearchClicked: () -> Unit,
|
||||
onMenuActionClicked: (RoomListMenuAction) -> Unit,
|
||||
displayMenuItems: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
// We need this to manually clip the top app bar in preview mode
|
||||
|
|
@ -195,79 +201,89 @@ private fun DefaultRoomListTopBar(
|
|||
Text(text = stringResource(id = R.string.screen_roomlist_main_space_title))
|
||||
},
|
||||
navigationIcon = {
|
||||
avatarData?.let {
|
||||
IconButton(
|
||||
modifier = Modifier.testTag(TestTags.homeScreenSettings),
|
||||
onClick = onOpenSettings
|
||||
) {
|
||||
IconButton(
|
||||
modifier = Modifier.testTag(TestTags.homeScreenSettings),
|
||||
onClick = onOpenSettings
|
||||
) {
|
||||
if (avatarData != null) {
|
||||
Avatar(
|
||||
avatarData = it,
|
||||
avatarData = avatarData!!,
|
||||
contentDescription = stringResource(CommonStrings.common_settings),
|
||||
)
|
||||
if (showAvatarIndicator) {
|
||||
RedIndicatorAtom(
|
||||
modifier = Modifier
|
||||
.padding(4.5.dp)
|
||||
.align(Alignment.TopEnd)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// Placeholder avatar until the avatarData is available
|
||||
Surface(
|
||||
modifier = Modifier.size(AvatarSize.CurrentUserTopBar.dp),
|
||||
shape = CircleShape,
|
||||
color = ElementTheme.colors.iconSecondary,
|
||||
content = {}
|
||||
)
|
||||
}
|
||||
if (showAvatarIndicator) {
|
||||
RedIndicatorAtom(
|
||||
modifier = Modifier
|
||||
.padding(4.5.dp)
|
||||
.align(Alignment.TopEnd)
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
IconButton(
|
||||
onClick = onSearchClicked,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.Search,
|
||||
contentDescription = stringResource(CommonStrings.action_search),
|
||||
)
|
||||
}
|
||||
if (RoomListConfig.HAS_DROP_DOWN_MENU) {
|
||||
var showMenu by remember { mutableStateOf(false) }
|
||||
if (displayMenuItems) {
|
||||
IconButton(
|
||||
onClick = { showMenu = !showMenu }
|
||||
onClick = onSearchClicked,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.OverflowVertical,
|
||||
contentDescription = null,
|
||||
imageVector = CompoundIcons.Search,
|
||||
contentDescription = stringResource(CommonStrings.action_search),
|
||||
)
|
||||
}
|
||||
DropdownMenu(
|
||||
expanded = showMenu,
|
||||
onDismissRequest = { showMenu = false }
|
||||
) {
|
||||
if (RoomListConfig.SHOW_INVITE_MENU_ITEM) {
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
showMenu = false
|
||||
onMenuActionClicked(RoomListMenuAction.InviteFriends)
|
||||
},
|
||||
text = { Text(stringResource(id = CommonStrings.action_invite)) },
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.ShareAndroid,
|
||||
tint = ElementTheme.materialColors.secondary,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
if (RoomListConfig.HAS_DROP_DOWN_MENU) {
|
||||
var showMenu by remember { mutableStateOf(false) }
|
||||
IconButton(
|
||||
onClick = { showMenu = !showMenu }
|
||||
) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.OverflowVertical,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
if (RoomListConfig.SHOW_REPORT_PROBLEM_MENU_ITEM) {
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
showMenu = false
|
||||
onMenuActionClicked(RoomListMenuAction.ReportBug)
|
||||
},
|
||||
text = { Text(stringResource(id = CommonStrings.common_report_a_problem)) },
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.ChatProblem,
|
||||
tint = ElementTheme.materialColors.secondary,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
)
|
||||
DropdownMenu(
|
||||
expanded = showMenu,
|
||||
onDismissRequest = { showMenu = false }
|
||||
) {
|
||||
if (RoomListConfig.SHOW_INVITE_MENU_ITEM) {
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
showMenu = false
|
||||
onMenuActionClicked(RoomListMenuAction.InviteFriends)
|
||||
},
|
||||
text = { Text(stringResource(id = CommonStrings.action_invite)) },
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.ShareAndroid,
|
||||
tint = ElementTheme.materialColors.secondary,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
if (RoomListConfig.SHOW_REPORT_PROBLEM_MENU_ITEM) {
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
showMenu = false
|
||||
onMenuActionClicked(RoomListMenuAction.ReportBug)
|
||||
},
|
||||
text = { Text(stringResource(id = CommonStrings.common_report_a_problem)) },
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.ChatProblem,
|
||||
tint = ElementTheme.materialColors.secondary,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -299,6 +315,7 @@ internal fun DefaultRoomListTopBarPreview() = ElementPreview {
|
|||
scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()),
|
||||
onOpenSettings = {},
|
||||
onSearchClicked = {},
|
||||
displayMenuItems = true,
|
||||
onMenuActionClicked = {},
|
||||
)
|
||||
}
|
||||
|
|
@ -314,6 +331,7 @@ internal fun DefaultRoomListTopBarWithIndicatorPreview() = ElementPreview {
|
|||
scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()),
|
||||
onOpenSettings = {},
|
||||
onSearchClicked = {},
|
||||
displayMenuItems = true,
|
||||
onMenuActionClicked = {},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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
|
||||
)
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
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,
|
||||
) {
|
||||
val displayMigrationStatusFadeProgress by animateFloatAsState(
|
||||
targetValue = if (isMigrating) 1f else 0f,
|
||||
animationSpec = tween(durationMillis = 200),
|
||||
label = "Migration view fade"
|
||||
)
|
||||
if (displayMigrationStatusFadeProgress > 0f) {
|
||||
SunsetPage(
|
||||
modifier = modifier.alpha(displayMigrationStatusFadeProgress),
|
||||
isLoading = true,
|
||||
title = stringResource(id = R.string.screen_migration_title),
|
||||
subtitle = stringResource(id = R.string.screen_migration_message),
|
||||
overallContent = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@PreviewsDayNight
|
||||
internal fun MigrationViewPreview() = ElementPreview {
|
||||
MigrationScreenView(isMigrating = true)
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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.di.DefaultPreferences
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import javax.inject.Inject
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class SharedPrefsMigrationScreenStore @Inject constructor(
|
||||
@DefaultPreferences 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_"
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="confirm_recovery_key_banner_message">"Your chat backup is currently out of sync. You need to confirm your recovery key to maintain access to your chat backup."</string>
|
||||
<string name="confirm_recovery_key_banner_title">"Confirm your recovery key"</string>
|
||||
<string name="screen_migration_message">"This is a one time process, thanks for waiting."</string>
|
||||
<string name="screen_migration_title">"Setting up your account."</string>
|
||||
<string name="screen_roomlist_a11y_create_message">"Create a new conversation or room"</string>
|
||||
<string name="screen_roomlist_empty_message">"Get started by messaging someone."</string>
|
||||
<string name="screen_roomlist_empty_title">"No chats yet."</string>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue