[#1492] Restore success dialog refactor (#1507)

* [#1492] Restore success dialog refactor

Closes #1492

* [#1492] Code cleanup

Closes #1492

* Create a separate Android layer

* Changelog update

* Move restore success string resources

* Rename logic related variables

+ File a follow-up issue

* Resources rename

* Move drawable to its package

---------

Co-authored-by: Honza <rychnovsky.honza@gmail.com>
This commit is contained in:
Milan 2024-07-19 10:17:45 +02:00 committed by GitHub
parent 1a0b634ab6
commit 0bc7757aa2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 352 additions and 55 deletions

View File

@ -14,6 +14,8 @@ and this application adheres to [Semantic Versioning](https://semver.org/spec/v2
### Changed
- The About screen has been redesigned to align with the new design guidelines
- `StyledBalance` text styles have been refactored from `Pair` into `BalanceTextStyle`
- The Restore Success dialog has been reworked into a separate screen, allowing users to opt out of the Keep screen
on while restoring option
## [1.1.3 (682)] - 2024-07-03

View File

@ -14,3 +14,5 @@ directly impact users rather than highlighting other key architectural updates.*
### Changed
- The About screen has been redesigned to align with the new design guidelines
- The Restore Success dialog has been reworked into a separate screen, allowing users to opt out of the Keep screen
on while restoring option

View File

@ -14,6 +14,8 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import java.util.concurrent.Executors
@ -30,6 +32,8 @@ class AndroidPreferenceProvider(
private val sharedPreferences: SharedPreferences,
private val dispatcher: CoroutineDispatcher
) : PreferenceProvider {
private val mutex = Mutex()
/*
* Implementation note: EncryptedSharedPreferences are not thread-safe, so this implementation
* confines them to a single background thread.
@ -45,13 +49,15 @@ class AndroidPreferenceProvider(
key: PreferenceKey,
value: String?
) = withContext(dispatcher) {
val editor = sharedPreferences.edit()
mutex.withLock {
val editor = sharedPreferences.edit()
editor.putString(key.key, value)
editor.putString(key.key, value)
editor.commit()
editor.commit()
Unit
Unit
}
}
override suspend fun getString(key: PreferenceKey) =

View File

@ -3,7 +3,6 @@ package co.electriccoin.zcash.ui.design.component
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Checkbox
import androidx.compose.material3.CheckboxDefaults
@ -76,7 +75,6 @@ fun LabeledCheckBox(
modifier =
modifier.then(
Modifier
.wrapContentSize()
.clip(RoundedCornerShape(ZcashTheme.dimens.regularRippleEffectCorner))
.clickable {
setCheckedState(!checkedState)

View File

@ -44,6 +44,7 @@ android {
"src/main/res/ui/onboarding",
"src/main/res/ui/receive",
"src/main/res/ui/restore",
"src/main/res/ui/restore_success",
"src/main/res/ui/scan",
"src/main/res/ui/security_warning",
"src/main/res/ui/seed_recovery",

View File

@ -33,9 +33,9 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
booleanStateFlow(StandardPreferenceKeys.IS_KEEP_SCREEN_ON_DURING_SYNC)
/**
* A flow of whether the app presented the user with an initial restoring dialog
* A flow of whether the app presented the user with restore success screem
*/
val isRestoringInitialWarningSeen: StateFlow<Boolean?> =
val isRestoreSuccessSeen: StateFlow<Boolean?> =
booleanStateFlow(StandardPreferenceKeys.IS_RESTORING_INITIAL_WARNING_SEEN)
fun setRestoringInitialWarningSeen() {

View File

@ -55,8 +55,6 @@ internal fun WrapHome(
val isKeepScreenOnWhileSyncing = homeViewModel.isKeepScreenOnWhileSyncing.collectAsStateWithLifecycle().value
val isRestoringInitialWarningSeen = homeViewModel.isRestoringInitialWarningSeen.collectAsStateWithLifecycle().value
val walletSnapshot = walletViewModel.walletSnapshot.collectAsStateWithLifecycle().value
val walletRestoringState = walletViewModel.walletRestoringState.collectAsStateWithLifecycle().value
@ -66,20 +64,24 @@ internal fun WrapHome(
walletViewModel.persistWalletRestoringState(WalletRestoringState.SYNCING)
}
var isShowingRestoreInitDialog by rememberSaveable { mutableStateOf(false) }
val setShowingRestoreInitDialog = {
// TODO [#1523]: Refactor RestoreSuccess screen navigation
// TODO [#1523]: https://github.com/Electric-Coin-Company/zashi-android/issues/1523
val isRestoreSuccessSeen = homeViewModel.isRestoreSuccessSeen.collectAsStateWithLifecycle().value
var isShowingRestoreSuccess by rememberSaveable { mutableStateOf(false) }
val setShowingRestoreSuccess = {
homeViewModel.setRestoringInitialWarningSeen()
isShowingRestoreInitDialog = false
isShowingRestoreSuccess = false
}
// Show initial restoring warn dialog
isRestoringInitialWarningSeen?.let { restoringWarningSeen ->
if (!restoringWarningSeen && walletRestoringState == WalletRestoringState.RESTORING) {
LaunchedEffect(key1 = isShowingRestoreInitDialog) {
// Adding an extra little delay before displaying the dialog for a better UX
// Show initial restore success screen
isRestoreSuccessSeen?.let { restoreSuccessSeen ->
if (!restoreSuccessSeen && walletRestoringState == WalletRestoringState.RESTORING) {
LaunchedEffect(key1 = isShowingRestoreSuccess) {
// Adding an extra little delay before displaying for a better UX
@Suppress("MagicNumber")
delay(1500)
isShowingRestoreInitDialog = true
isShowingRestoreSuccess = true
}
}
}
@ -90,9 +92,9 @@ internal fun WrapHome(
goSettings = goSettings,
goMultiTrxSubmissionFailure = goMultiTrxSubmissionFailure,
isKeepScreenOnWhileSyncing = isKeepScreenOnWhileSyncing,
isShowingRestoreInitDialog = isShowingRestoreInitDialog,
isShowingRestoreSuccess = isShowingRestoreSuccess,
sendArguments = sendArguments,
setShowingRestoreInitDialog = setShowingRestoreInitDialog,
setShowingRestoreSuccess = setShowingRestoreSuccess,
walletSnapshot = walletSnapshot
)
}
@ -106,9 +108,9 @@ internal fun WrapHome(
goScan: () -> Unit,
goSendConfirmation: (ZecSend) -> Unit,
isKeepScreenOnWhileSyncing: Boolean?,
isShowingRestoreInitDialog: Boolean,
isShowingRestoreSuccess: Boolean,
sendArguments: SendArguments,
setShowingRestoreInitDialog: () -> Unit,
setShowingRestoreSuccess: () -> Unit,
walletSnapshot: WalletSnapshot?,
) {
val focusManager = LocalFocusManager.current
@ -209,8 +211,8 @@ internal fun WrapHome(
Home(
subScreens = tabs,
isKeepScreenOnWhileSyncing = isKeepScreenOnWhileSyncing,
isShowingRestoreInitDialog = isShowingRestoreInitDialog,
setShowingRestoreInitDialog = setShowingRestoreInitDialog,
isShowingRestoreSuccess = isShowingRestoreSuccess,
setShowingRestoreSuccess = setShowingRestoreSuccess,
walletSnapshot = walletSnapshot,
pagerState = pagerState,
)

View File

@ -19,21 +19,19 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension
import cash.z.ecc.android.sdk.Synchronizer
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.compose.DisableScreenTimeout
import co.electriccoin.zcash.ui.common.model.WalletSnapshot
import co.electriccoin.zcash.ui.design.component.AppAlertDialog
import co.electriccoin.zcash.ui.design.component.BlankSurface
import co.electriccoin.zcash.ui.design.component.NavigationTabText
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.fixture.WalletSnapshotFixture
import co.electriccoin.zcash.ui.screen.home.model.TabItem
import co.electriccoin.zcash.ui.screen.restoresuccess.WrapRestoreSuccess
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.launch
@ -46,8 +44,8 @@ private fun ComposablePreview() {
BlankSurface {
Home(
isKeepScreenOnWhileSyncing = false,
isShowingRestoreInitDialog = false,
setShowingRestoreInitDialog = {},
isShowingRestoreSuccess = false,
setShowingRestoreSuccess = {},
subScreens = persistentListOf(),
walletSnapshot = WalletSnapshotFixture.new(),
)
@ -60,8 +58,8 @@ private fun ComposablePreview() {
@Composable
fun Home(
isKeepScreenOnWhileSyncing: Boolean?,
isShowingRestoreInitDialog: Boolean,
setShowingRestoreInitDialog: () -> Unit,
isShowingRestoreSuccess: Boolean,
setShowingRestoreSuccess: () -> Unit,
subScreens: ImmutableList<TabItem>,
walletSnapshot: WalletSnapshot?,
pagerState: PagerState =
@ -76,8 +74,8 @@ fun Home(
subScreens = subScreens,
)
if (isShowingRestoreInitDialog) {
HomeRestoringInitialDialog(setShowingRestoreInitDialog)
if (isShowingRestoreSuccess) {
WrapRestoreSuccess(onDone = setShowingRestoreSuccess)
}
if (isKeepScreenOnWhileSyncing == true &&
@ -182,13 +180,3 @@ private fun HomeContent(
}
}
}
@Composable
fun HomeRestoringInitialDialog(setShowingRestoreInitDialog: () -> Unit) {
AppAlertDialog(
title = stringResource(id = R.string.restoring_initial_dialog_title),
text = stringResource(id = R.string.restoring_initial_dialog_description),
confirmButtonText = stringResource(id = R.string.restoring_initial_dialog_positive_button),
onConfirmButtonClick = setShowingRestoreInitDialog
)
}

View File

@ -0,0 +1,27 @@
package co.electriccoin.zcash.ui.screen.restoresuccess
import androidx.activity.viewModels
import androidx.compose.runtime.Composable
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.ui.common.compose.LocalActivity
import co.electriccoin.zcash.ui.screen.restoresuccess.view.RestoreSuccess
import co.electriccoin.zcash.ui.screen.restoresuccess.viewmodel.RestoreSuccessViewModel
@Composable
fun WrapRestoreSuccess(onDone: () -> Unit) {
val activity = LocalActivity.current
val viewModel by activity.viewModels<RestoreSuccessViewModel>()
val state = viewModel.state.collectAsStateWithLifecycle().value
RestoreSuccess(
state =
state.copy(
onPositiveClick = {
state.onPositiveClick()
onDone()
}
)
)
}

View File

@ -0,0 +1,164 @@
package co.electriccoin.zcash.ui.screen.restoresuccess.view
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Devices
import androidx.compose.ui.tooling.preview.Preview
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.design.component.BlankSurface
import co.electriccoin.zcash.ui.design.component.GridBgScaffold
import co.electriccoin.zcash.ui.design.component.LabeledCheckBox
import co.electriccoin.zcash.ui.design.component.PrimaryButton
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
@Composable
fun RestoreSuccess(state: RestoreSuccessViewState) {
GridBgScaffold { paddingValues ->
RestoreSuccessContent(
state = state,
modifier =
Modifier
.fillMaxSize()
.padding(
top = paddingValues.calculateTopPadding(),
bottom = paddingValues.calculateBottomPadding()
)
.verticalScroll(rememberScrollState())
)
}
}
@Composable
@Suppress("LongMethod")
private fun RestoreSuccessContent(
state: RestoreSuccessViewState,
modifier: Modifier = Modifier
) {
Column(
modifier =
modifier.then(
Modifier.padding(
start = ZcashTheme.dimens.screenHorizontalSpacingBig,
end = ZcashTheme.dimens.screenHorizontalSpacingBig
)
),
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingBig))
Text(
text = stringResource(id = R.string.restore_success_title),
style = ZcashTheme.typography.secondary.headlineMedium
)
Spacer(Modifier.height(ZcashTheme.dimens.spacingUpLarge))
Image(
painter = painterResource(id = R.drawable.img_success_dialog),
contentDescription = stringResource(id = R.string.restore_success_subtitle),
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onBackground)
)
Spacer(Modifier.height(ZcashTheme.dimens.spacingUpLarge))
Text(
text = stringResource(id = R.string.restore_success_subtitle),
textAlign = TextAlign.Center,
style = ZcashTheme.typography.secondary.headlineSmall,
fontWeight = FontWeight.SemiBold
)
Spacer(Modifier.height(ZcashTheme.dimens.spacingUpLarge))
Text(
text = stringResource(id = R.string.restore_success_description),
style = ZcashTheme.typography.secondary.bodySmall,
)
Spacer(Modifier.height(ZcashTheme.dimens.spacingLarge))
LabeledCheckBox(
modifier = Modifier.align(Alignment.Start),
checked = state.isKeepScreenOnChecked,
onCheckedChange = { state.onCheckboxClick() },
text = stringResource(id = R.string.restoring_initial_dialog_checkbox)
)
Spacer(Modifier.height(ZcashTheme.dimens.spacingLarge))
Text(
text =
buildAnnotatedString {
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
append(stringResource(id = R.string.restore_success_note_part_1))
}
append(" ")
append(stringResource(id = R.string.restore_success_note_part_2))
},
style = ZcashTheme.extendedTypography.footnote,
)
Spacer(Modifier.height(ZcashTheme.dimens.spacingBig))
Spacer(Modifier.weight(1f))
PrimaryButton(
onClick = state.onPositiveClick,
text = stringResource(id = R.string.restore_success_button)
)
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingBig))
}
}
data class RestoreSuccessViewState(
val isKeepScreenOnChecked: Boolean,
val onCheckboxClick: () -> Unit,
val onPositiveClick: () -> Unit,
)
@Composable
private fun RestoreSuccessViewPreview() =
BlankSurface {
RestoreSuccess(
state =
RestoreSuccessViewState(
isKeepScreenOnChecked = true,
onCheckboxClick = {},
onPositiveClick = {},
)
)
}
@Preview(device = Devices.PIXEL_7_PRO)
@Composable
private fun RestoreSuccessViewPreviewLight() =
ZcashTheme(false) {
RestoreSuccessViewPreview()
}
@Preview(device = Devices.PIXEL_7_PRO)
@Composable
private fun RestoreSuccessViewPreviewDark() =
ZcashTheme(true) {
RestoreSuccessViewPreview()
}

View File

@ -0,0 +1,53 @@
package co.electriccoin.zcash.ui.screen.restoresuccess.viewmodel
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import co.electriccoin.zcash.preference.model.entry.BooleanPreferenceDefault
import co.electriccoin.zcash.ui.common.ANDROID_STATE_FLOW_TIMEOUT
import co.electriccoin.zcash.ui.preference.StandardPreferenceKeys
import co.electriccoin.zcash.ui.preference.StandardPreferenceSingleton
import co.electriccoin.zcash.ui.screen.restoresuccess.view.RestoreSuccessViewState
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.WhileSubscribed
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
class RestoreSuccessViewModel(application: Application) : AndroidViewModel(application) {
private val keepScreenOn = MutableStateFlow(DEFAULT_KEEP_SCREEN_ON)
val state =
keepScreenOn
.map { createState(keepScreenOn = it) }
.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
createState(keepScreenOn = DEFAULT_KEEP_SCREEN_ON)
)
private fun createState(keepScreenOn: Boolean) =
RestoreSuccessViewState(
isKeepScreenOnChecked = keepScreenOn,
onCheckboxClick = { this.keepScreenOn.update { !keepScreenOn } },
onPositiveClick = {
setKeepScreenOnWhileSyncing(keepScreenOn)
}
)
private fun setKeepScreenOnWhileSyncing(enabled: Boolean) {
setBooleanPreference(StandardPreferenceKeys.IS_KEEP_SCREEN_ON_DURING_SYNC, enabled)
}
private fun setBooleanPreference(
default: BooleanPreferenceDefault,
newState: Boolean
) = viewModelScope.launch {
val prefs = StandardPreferenceSingleton.getInstance(getApplication())
default.putValue(prefs, newState)
}
}
private const val DEFAULT_KEEP_SCREEN_ON = true

View File

@ -14,12 +14,8 @@ import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
class SettingsViewModel(application: Application) : AndroidViewModel(application) {
private val mutex = Mutex()
val isAnalyticsEnabled: StateFlow<Boolean?> = booleanStateFlow(StandardPreferenceKeys.IS_ANALYTICS_ENABLED)
val isBackgroundSync: StateFlow<Boolean?> = booleanStateFlow(StandardPreferenceKeys.IS_BACKGROUND_SYNC_ENABLED)
@ -51,9 +47,7 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
) {
viewModelScope.launch {
val prefs = StandardPreferenceSingleton.getInstance(getApplication())
mutex.withLock {
default.putValue(prefs, newState)
}
default.putValue(prefs, newState)
}
}
}

View File

@ -3,8 +3,4 @@
<string name="home_tab_send">Send</string>
<string name="home_tab_receive">Receive</string>
<string name="home_tab_balances">Balances</string>
<string name="restoring_initial_dialog_title">Success</string>
<string name="restoring_initial_dialog_description">Your wallet has been successfully restored! During the initial sync, your funds cannot be spent or sent. Depending on the age of your wallet, it may take a few hours to fully sync.</string>
<string name="restoring_initial_dialog_positive_button">OK</string>
</resources>

View File

@ -0,0 +1,54 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="78dp"
android:height="175dp"
android:viewportWidth="78"
android:viewportHeight="175">
<group>
<clip-path
android:pathData="M40.14,128.56l-6.2,2.5l4,9.72l6.2,-2.5z"/>
<path
android:pathData="M42.35,133.92L39.75,134.96C39.66,135 39.58,135.08 39.55,135.17C39.51,135.26 39.52,135.36 39.57,135.45L41.84,139.22L35.84,135.68L38.53,134.59C38.62,134.55 38.69,134.48 38.73,134.39C38.76,134.3 38.76,134.2 38.71,134.11L36.44,130.06L42.35,133.92Z"
android:fillColor="#ffffff"/>
</group>
<path
android:pathData="M44.49,69.32L35.54,65.32L42.3,59.57L30.59,44.77L48.54,54.48L49.7,58.58L41.31,62.95L44.49,69.33V69.32ZM36.78,65.29L43.34,68.22L40.83,63.18L36.78,65.29ZM48.17,55.14L38.01,64.04L49.06,58.29L48.17,55.14ZM32.63,46.47L42.68,59.18L47.83,54.7L32.63,46.47Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M12.93,21.03L19.85,13.15C19.96,13.03 19.95,12.83 19.83,12.73C19.7,12.62 19.5,12.63 19.39,12.76L17.67,14.72C17.15,14.51 16.62,14.4 16.07,14.4C13.55,14.4 11.79,16.74 11.72,16.83C11.63,16.94 11.64,17.07 11.72,17.19C12.4,18.03 13.11,18.65 13.85,19.04L12.46,20.63C12.35,20.75 12.36,20.95 12.48,21.05C12.55,21.1 12.61,21.12 12.68,21.12C12.77,21.12 12.85,21.08 12.92,21.01L12.93,21.03ZM12.36,17C12.81,16.48 14.24,15 16.09,15C16.48,15 16.88,15.06 17.25,15.19L16.78,15.74C16.56,15.6 16.3,15.54 16.02,15.54C15.21,15.54 14.54,16.21 14.54,17.01C14.54,17.36 14.66,17.68 14.86,17.93L14.28,18.6C13.62,18.26 12.99,17.73 12.37,17H12.36ZM20.32,16.81C20.41,16.93 20.41,17.07 20.32,17.19C20.25,17.29 18.49,19.61 15.96,19.61C15.67,19.61 15.36,19.57 15.07,19.5L15.54,18.97C15.69,18.99 15.84,18.99 15.98,18.99C17.8,18.99 19.23,17.52 19.68,17C19.24,16.49 18.79,16.08 18.34,15.76L18.74,15.31C19.27,15.69 19.8,16.18 20.31,16.8L20.32,16.81Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M65.52,1.35H12.47C6.28,1.35 1.25,6.37 1.25,12.57V134.04C1.25,140.23 6.28,145.25 12.47,145.25H65.52C71.71,145.25 76.73,140.23 76.73,134.04V12.57C76.73,6.37 71.71,1.35 65.52,1.35Z"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#ffffff"/>
<path
android:pathData="M39.74,78.89C38.1,78.89 36.45,78.7 34.84,78.34C31.2,77.51 27.81,75.77 25.03,73.27V73.14L24.32,72.45C21.88,70.06 20.02,67.12 18.92,63.89L23.81,66.45L22.28,69.67L23.26,70.83C27.37,75.68 33.38,78.46 39.77,78.46C44.87,78.46 49.83,76.66 53.71,73.37C57.24,70.37 59.77,66.25 60.81,61.73C60.83,61.62 60.92,61.54 61.03,61.54C61.13,61.54 61.21,61.63 61.21,61.73C58.95,71.68 49.91,78.89 39.74,78.89ZM18.24,51.92C18.14,51.91 18.06,51.81 18.06,51.71C20.35,42.28 28.79,35.3 38.56,34.81C38.92,34.79 39.27,34.78 39.62,34.78C45.06,34.78 50.26,36.76 54.31,40.37V40.5L55.01,41.19C57.47,43.59 59.32,46.54 60.43,49.78L55.42,47.19L56.92,43.97L55.94,42.82C52.97,39.32 48.86,36.82 44.38,35.77C42.77,35.4 41.12,35.2 39.48,35.2C29.41,35.2 20.77,42.04 18.45,51.83C18.45,51.86 18.35,51.91 18.27,51.91H18.23L18.24,51.92Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M43.13,145.25H43.63C43.91,145.25 44.13,145.47 44.13,145.75V156.75C44.13,157.58 43.46,158.25 42.63,158.25H42.13V167.03C42.13,167.31 41.91,167.53 41.63,167.53C41.35,167.53 41.13,167.31 41.13,167.03V157.75C41.13,157.47 41.35,157.25 41.63,157.25H42.63C42.91,157.25 43.13,157.03 43.13,156.75V146.25H36.13V156.75C36.13,157.03 36.35,157.25 36.63,157.25H37.63C37.91,157.25 38.13,157.47 38.13,157.75V171.03C38.13,171.31 37.91,171.53 37.63,171.53C37.35,171.53 37.13,171.31 37.13,171.03V158.25H36.63C35.8,158.25 35.13,157.58 35.13,156.75V145.75C35.13,145.47 35.35,145.25 35.63,145.25H36.13"
android:fillColor="#ffffff"/>
<path
android:strokeWidth="1"
android:pathData="M34.78,174.03L45.76,163.05"
android:fillColor="#00000000"
android:strokeColor="#ffffff"/>
<path
android:pathData="M27.9,152.55C28.11,152.45 28.19,152.18 28.07,151.94C27.96,151.7 27.7,151.58 27.5,151.68L22.77,153.91C22.56,154.01 22.49,154.28 22.6,154.52C22.71,154.76 22.97,154.88 23.18,154.78L27.9,152.55Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M51.36,152.55C51.15,152.45 51.07,152.18 51.19,151.94C51.3,151.7 51.56,151.58 51.76,151.68L56.49,153.91C56.7,154.01 56.78,154.28 56.66,154.52C56.55,154.76 56.29,154.88 56.08,154.78L51.36,152.55Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M26.63,148.85C26.87,148.84 27.03,148.62 27,148.35C26.99,148.3 26.98,148.24 26.95,148.2C26.87,148.02 26.7,147.89 26.51,147.91L23.11,148.05C22.87,148.06 22.71,148.28 22.74,148.54C22.78,148.8 23,149.01 23.23,149L26.63,148.85Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M52.63,148.85C52.39,148.84 52.23,148.62 52.27,148.35C52.28,148.3 52.28,148.24 52.31,148.2C52.39,148.02 52.56,147.89 52.75,147.91L56.16,148.05C56.39,148.06 56.55,148.28 56.52,148.54C56.48,148.8 56.26,149.01 56.03,149L52.63,148.85Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M27.9,158.43L30.33,155.71C30.45,155.58 30.47,155.36 30.39,155.19C30.36,155.13 30.34,155.09 30.29,155.05C30.12,154.85 29.84,154.84 29.69,155.01L27.26,157.73C27.1,157.91 27.11,158.21 27.29,158.4C27.47,158.59 27.74,158.6 27.9,158.43Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M51.37,158.43L48.93,155.71C48.81,155.58 48.79,155.36 48.87,155.19C48.9,155.13 48.92,155.09 48.97,155.05C49.14,154.85 49.42,154.84 49.57,155.01L52.01,157.73C52.17,157.91 52.15,158.21 51.97,158.4C51.8,158.59 51.52,158.6 51.37,158.43Z"
android:fillColor="#ffffff"/>
</vector>

View File

@ -0,0 +1,10 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="restore_success_title">Keep Zashi open!</string>
<string name="restore_success_subtitle">Your wallet has been successfully restored and is now syncing</string>
<string name="restore_success_description">To prevent interruption, keep your screen awake, plug your device into a power source, and keep it in a secure place.</string>
<string name="restoring_initial_dialog_checkbox">Keep screen on while restoring.</string>
<string name="restore_success_note_part_1">Note:</string>
<string name="restore_success_note_part_2">During the initial sync your funds cannot be sent or
spent. Depending on the age of your wallet, it may take a few hours to fully sync.</string>
<string name="restore_success_button">GOT IT!</string>
</resources>