Issue 1617 not enough space and update redesign (#1652)
* [#1617] Update redesign Closes ##1617 * [#1617] Not enough space redesign Closes ##1617 * [#1617] Code cleanup Closes ##1617 * [#1617] Documentation update Closes ##1617 * [#1617] Sensitive settings disabled during needed update Closes ##1617 * Code cleanup * Test hotfix * Test hotfix * Test hotfix * Address review comments * Fix broken in-app update logic - It was broken since we introduced Koin - Now fixed and tested using AppUpdateCheckerMock * Changelogs update --------- Co-authored-by: Honza <rychnovsky.honza@gmail.com>
This commit is contained in:
parent
d97956de44
commit
c6350641e3
|
@ -11,10 +11,14 @@ and this application adheres to [Semantic Versioning](https://semver.org/spec/v2
|
|||
- Zashi app now supports Spanish language
|
||||
- The Flexa SDK has been adopted to enable payments using the embedded Flexa UI
|
||||
|
||||
### Changed
|
||||
- The Not enough space and In-app udpate screens have been redesigned
|
||||
|
||||
### Fixed
|
||||
- Address book toast now correctly shows on send screen when adding both new and known addresses to text field
|
||||
- The application now correctly navigates to the homepage after deleting the current wallet and creating a new or
|
||||
recovering an older one
|
||||
- The in-app update logic has been fixed and is now correctly requested with every app launch
|
||||
|
||||
## [1.2.1 (760)] - 2024-10-22
|
||||
|
||||
|
|
|
@ -14,10 +14,14 @@ directly impact users rather than highlighting other key architectural updates.*
|
|||
- Zashi app now supports Spanish language
|
||||
- The Flexa SDK has been adopted to enable payments using the embedded Flexa UI
|
||||
|
||||
### Changed
|
||||
- The Not enough space and In-app udpate screens have been redesigned
|
||||
|
||||
### Fixed
|
||||
- Address book toast now correctly shows on send screen when adding both new and known addresses to text field
|
||||
- The application now correctly navigates to the homepage after deleting the current wallet and creating a new or
|
||||
recovering an older one
|
||||
- The in-app update logic has been fixed and is now correctly requested with every app launch
|
||||
|
||||
## [1.2.1 (760)] - 2024-10-22
|
||||
|
||||
|
|
|
@ -18,3 +18,7 @@ directly impact users rather than highlighting other key architectural updates.*
|
|||
- Address book toast now correctly shows on send screen when adding both new and known addresses to text field
|
||||
- The application now correctly navigates to the homepage after deleting the current wallet and creating a new or
|
||||
recovering an older one
|
||||
- The in-app update logic has been fixed and is now correctly requested with every app launch
|
||||
|
||||
### Changed
|
||||
- The Not enough space and In-app udpate screens have been redesigned
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
package co.electriccoin.zcash.ui.design.component
|
||||
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import co.electriccoin.zcash.ui.design.R
|
||||
import co.electriccoin.zcash.ui.design.util.orDark
|
||||
|
||||
@Composable
|
||||
fun ZashiTopAppBarBackNavigation(
|
||||
backContentDescriptionText: String = stringResource(R.string.back_navigation_content_description),
|
||||
painter: Painter =
|
||||
painterResource(
|
||||
R.drawable.ic_zashi_navigation_back orDark R.drawable.ic_zashi_navigation_back_dark
|
||||
),
|
||||
onBack: () -> Unit
|
||||
) {
|
||||
Row {
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
IconButton(onClick = onBack) {
|
||||
Icon(
|
||||
painter = painter,
|
||||
contentDescription = backContentDescriptionText,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package co.electriccoin.zcash.ui.design.component
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import co.electriccoin.zcash.ui.design.R
|
||||
import co.electriccoin.zcash.ui.design.theme.colors.ZashiColors
|
||||
|
||||
@Composable
|
||||
fun ZashiTopAppBarBackNavigation(
|
||||
onBack: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) = ZashiTopAppBarNavigation(
|
||||
modifier = modifier,
|
||||
backContentDescriptionText = stringResource(R.string.back_navigation_content_description),
|
||||
drawableRes = R.drawable.ic_zashi_navigation_back,
|
||||
onBack = onBack
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun ZashiTopAppBarCloseNavigation(
|
||||
onBack: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) = ZashiTopAppBarNavigation(
|
||||
modifier = modifier,
|
||||
backContentDescriptionText = stringResource(R.string.back_navigation_content_description),
|
||||
drawableRes = R.drawable.ic_navigation_close,
|
||||
onBack = onBack,
|
||||
tint = ZashiColors.Text.textPrimary
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun ZashiTopAppBarHamburgerNavigation(onBack: () -> Unit) =
|
||||
ZashiTopAppBarNavigation(
|
||||
backContentDescriptionText = stringResource(R.string.back_navigation_content_description),
|
||||
drawableRes = R.drawable.ic_navigation_hamburger,
|
||||
onBack = onBack,
|
||||
tint = ZashiColors.Text.textPrimary
|
||||
)
|
||||
|
||||
@Composable
|
||||
private fun ZashiTopAppBarNavigation(
|
||||
backContentDescriptionText: String,
|
||||
@DrawableRes drawableRes: Int,
|
||||
onBack: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
tint: Color? = null,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
) {
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
IconButton(onClick = onBack) {
|
||||
Icon(
|
||||
painter = painterResource(drawableRes),
|
||||
contentDescription = backContentDescriptionText,
|
||||
tint = tint ?: LocalContentColor.current
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package co.electriccoin.zcash.ui.design.component
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import co.electriccoin.zcash.ui.design.theme.colors.ZashiColors
|
||||
import co.electriccoin.zcash.ui.design.util.orDark
|
||||
|
||||
@Composable
|
||||
fun zashiVerticalGradient(
|
||||
startColor: Color = ZashiColors.Utility.WarningYellow.utilityOrange100,
|
||||
endColor: Color = ZashiColors.Surfaces.bgPrimary
|
||||
) = Brush.verticalGradient(
|
||||
START_STOP to startColor,
|
||||
(END_STOP_LIGHT orDark END_STOP_DARK) to endColor,
|
||||
)
|
||||
|
||||
private const val START_STOP = .0f
|
||||
private const val END_STOP_DARK = .35f
|
||||
private const val END_STOP_LIGHT = .4f
|
|
@ -0,0 +1,13 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="40dp"
|
||||
android:height="40dp"
|
||||
android:viewportWidth="40"
|
||||
android:viewportHeight="40">
|
||||
<path
|
||||
android:pathData="M26,14L14,26M14,14L26,26"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#231F20"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
|
@ -0,0 +1,13 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="40dp"
|
||||
android:height="40dp"
|
||||
android:viewportWidth="40"
|
||||
android:viewportHeight="40">
|
||||
<path
|
||||
android:pathData="M11,20H29M11,14H29M11,26H29"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#231F20"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
|
@ -35,7 +35,7 @@ class UpdateViewModelTest : UiTestPrerequisites() {
|
|||
|
||||
@Before
|
||||
fun setup() {
|
||||
checker = AppUpdateCheckerMock.new()
|
||||
checker = AppUpdateCheckerMock()
|
||||
|
||||
initialUpdateInfo =
|
||||
UpdateInfoFixture.new(
|
||||
|
|
|
@ -106,7 +106,6 @@ class SettingsViewTestSetup(
|
|||
Settings(
|
||||
state =
|
||||
SettingsState(
|
||||
isLoading = false,
|
||||
version = stringRes("app_version"),
|
||||
debugMenu = settingsTroubleshootingState,
|
||||
onBack = {
|
||||
|
|
|
@ -41,13 +41,13 @@ class UpdateViewAndroidTest : UiTestPrerequisites() {
|
|||
)
|
||||
newTestSetup(updateInfo)
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.update_header), ignoreCase = true).also {
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.update_title_available), ignoreCase = true).also {
|
||||
it.assertExists()
|
||||
}
|
||||
|
||||
Espresso.pressBack()
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.update_header), ignoreCase = true).also {
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.update_title_available), ignoreCase = true).also {
|
||||
it.assertDoesNotExist()
|
||||
}
|
||||
}
|
||||
|
@ -64,13 +64,13 @@ class UpdateViewAndroidTest : UiTestPrerequisites() {
|
|||
)
|
||||
newTestSetup(updateInfo)
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.update_critical_header), ignoreCase = true).also {
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.update_title_required), ignoreCase = true).also {
|
||||
it.assertExists()
|
||||
}
|
||||
|
||||
Espresso.pressBack()
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.update_critical_header), ignoreCase = true).also {
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.update_title_required), ignoreCase = true).also {
|
||||
it.assertExists()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ class UpdateViewAndroidTestSetup(
|
|||
UpdateViewModel(
|
||||
application = composeTestRule.activity.application,
|
||||
updateInfo = updateInfo,
|
||||
appUpdateChecker = AppUpdateCheckerMock.new()
|
||||
appUpdateChecker = AppUpdateCheckerMock()
|
||||
)
|
||||
|
||||
@Composable
|
||||
|
@ -32,7 +32,8 @@ class UpdateViewAndroidTestSetup(
|
|||
updateInfo = updateInfo,
|
||||
checkForUpdate = viewModel::checkForAppUpdate,
|
||||
remindLater = viewModel::remindLater,
|
||||
goForUpdate = {}
|
||||
goForUpdate = {},
|
||||
onSettings = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import androidx.compose.ui.test.onNodeWithTag
|
|||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.onRoot
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.compose.ui.test.performScrollTo
|
||||
import androidx.test.filters.MediumTest
|
||||
import co.electriccoin.zcash.test.UiTestPrerequisites
|
||||
import co.electriccoin.zcash.ui.R
|
||||
|
@ -17,6 +16,7 @@ import co.electriccoin.zcash.ui.screen.update.model.UpdateInfo
|
|||
import co.electriccoin.zcash.ui.screen.update.model.UpdateState
|
||||
import co.electriccoin.zcash.ui.test.getStringResource
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Ignore
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
|
@ -38,14 +38,7 @@ class UpdateViewTest : UiTestPrerequisites() {
|
|||
newTestSetup(updateInfo)
|
||||
|
||||
composeTestRule.onNodeWithText(
|
||||
text = getStringResource(R.string.update_critical_header),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.assertExists()
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(
|
||||
text = getStringResource(R.string.update_later_disabled_button),
|
||||
text = getStringResource(R.string.update_title_required),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.assertExists()
|
||||
|
@ -85,14 +78,7 @@ class UpdateViewTest : UiTestPrerequisites() {
|
|||
newTestSetup(updateInfo)
|
||||
|
||||
composeTestRule.onNodeWithText(
|
||||
text = getStringResource(R.string.update_header),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.assertExists()
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(
|
||||
text = getStringResource(R.string.update_later_enabled_button),
|
||||
text = getStringResource(R.string.update_title_available),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.assertExists()
|
||||
|
@ -101,11 +87,11 @@ class UpdateViewTest : UiTestPrerequisites() {
|
|||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun later_btn_force_update_test() {
|
||||
fun later_btn_update_test() {
|
||||
val updateInfo =
|
||||
UpdateInfoFixture.new(
|
||||
priority = AppUpdateChecker.Priority.HIGH,
|
||||
force = true,
|
||||
priority = AppUpdateChecker.Priority.LOW,
|
||||
force = false,
|
||||
appUpdateInfo = null,
|
||||
state = UpdateState.Prepared
|
||||
)
|
||||
|
@ -115,7 +101,7 @@ class UpdateViewTest : UiTestPrerequisites() {
|
|||
|
||||
composeTestRule.clickLater()
|
||||
|
||||
assertEquals(0, testSetup.getOnLaterCount())
|
||||
assertEquals(1, testSetup.getOnLaterCount())
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -127,10 +113,6 @@ class UpdateViewTest : UiTestPrerequisites() {
|
|||
|
||||
assertEquals(0, testSetup.getOnDownloadCount())
|
||||
|
||||
composeTestRule.onNodeWithText(UpdateTag.PROGRESSBAR_DOWNLOADING).also {
|
||||
it.assertDoesNotExist()
|
||||
}
|
||||
|
||||
composeTestRule.clickDownload()
|
||||
|
||||
assertEquals(1, testSetup.getOnDownloadCount())
|
||||
|
@ -138,6 +120,7 @@ class UpdateViewTest : UiTestPrerequisites() {
|
|||
|
||||
@Test
|
||||
@MediumTest
|
||||
@Ignore("Disable the test for now -> we have no way to click a clickable span right now")
|
||||
fun play_store_ref_test() {
|
||||
val updateInfo = UpdateInfoFixture.new(appUpdateInfo = null)
|
||||
|
||||
|
@ -146,9 +129,8 @@ class UpdateViewTest : UiTestPrerequisites() {
|
|||
assertEquals(0, testSetup.getOnReferenceCount())
|
||||
composeTestRule.onRoot().assertExists()
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.update_link_text)).also {
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.update_link_text), substring = true,).also {
|
||||
it.assertExists()
|
||||
it.performScrollTo()
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,8 @@ class UpdateViewTestSetup(
|
|||
},
|
||||
onReference = {
|
||||
onReferenceCount.incrementAndGet()
|
||||
}
|
||||
},
|
||||
onSettings = {}
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ import co.electriccoin.zcash.ui.common.usecase.PersistEndpointUseCase
|
|||
import co.electriccoin.zcash.ui.common.usecase.RefreshFastestServersUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.RescanBlockchainUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.SaveContactUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.SensitiveSettingsVisibleUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.ShareImageUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.UpdateContactUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.ValidateContactAddressUseCase
|
||||
|
@ -69,4 +70,5 @@ val useCaseModule =
|
|||
singleOf(::ObserveWalletStateUseCase)
|
||||
singleOf(::IsCoinbaseAvailableUseCase)
|
||||
singleOf(::GetSpendingKeyUseCase)
|
||||
singleOf(::SensitiveSettingsVisibleUseCase)
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import co.electriccoin.zcash.ui.screen.sendconfirmation.viewmodel.CreateTransact
|
|||
import co.electriccoin.zcash.ui.screen.settings.viewmodel.ScreenBrightnessViewModel
|
||||
import co.electriccoin.zcash.ui.screen.settings.viewmodel.SettingsViewModel
|
||||
import co.electriccoin.zcash.ui.screen.support.viewmodel.SupportViewModel
|
||||
import co.electriccoin.zcash.ui.screen.update.model.UpdateInfo
|
||||
import co.electriccoin.zcash.ui.screen.update.viewmodel.UpdateViewModel
|
||||
import co.electriccoin.zcash.ui.screen.warning.viewmodel.StorageCheckViewModel
|
||||
import co.electriccoin.zcash.ui.screen.whatsnew.viewmodel.WhatsNewViewModel
|
||||
|
@ -50,7 +51,13 @@ val viewModelModule =
|
|||
viewModelOf(::CreateTransactionsViewModel)
|
||||
viewModelOf(::RestoreSuccessViewModel)
|
||||
viewModelOf(::WhatsNewViewModel)
|
||||
viewModelOf(::UpdateViewModel)
|
||||
viewModel { (updateInfo: UpdateInfo) ->
|
||||
UpdateViewModel(
|
||||
application = get(),
|
||||
updateInfo = updateInfo,
|
||||
appUpdateChecker = get(),
|
||||
)
|
||||
}
|
||||
viewModelOf(::ChooseServerViewModel)
|
||||
viewModel { (args: AddressBookArgs) ->
|
||||
AddressBookViewModel(
|
||||
|
|
|
@ -510,7 +510,7 @@ private fun fillInHandleForPaymentRequest(
|
|||
handle[PAYMENT_REQUEST_URI] = zip321
|
||||
}
|
||||
|
||||
private fun NavHostController.navigateJustOnce(
|
||||
fun NavHostController.navigateJustOnce(
|
||||
route: String,
|
||||
navOptionsBuilder: (NavOptionsBuilder.() -> Unit)? = null
|
||||
) {
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package co.electriccoin.zcash.ui.common.usecase
|
||||
|
||||
import android.content.Context
|
||||
import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT
|
||||
import co.electriccoin.zcash.ui.screen.update.AppUpdateChecker
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.WhileSubscribed
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
class SensitiveSettingsVisibleUseCase(
|
||||
appUpdateChecker: AppUpdateChecker,
|
||||
context: Context
|
||||
) {
|
||||
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
|
||||
|
||||
private val flow =
|
||||
appUpdateChecker.newCheckForUpdateAvailabilityFlow(context)
|
||||
.map { it.isForce.not() }
|
||||
.stateIn(
|
||||
scope = scope,
|
||||
started = SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
|
||||
initialValue = true
|
||||
)
|
||||
|
||||
operator fun invoke() = flow
|
||||
}
|
|
@ -14,7 +14,6 @@ import co.electriccoin.zcash.ui.screen.advancedsettings.viewmodel.AdvancedSettin
|
|||
import kotlinx.collections.immutable.toImmutableList
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
internal fun WrapAdvancedSettings(
|
||||
goDeleteWallet: () -> Unit,
|
||||
|
|
|
@ -2,55 +2,71 @@ package co.electriccoin.zcash.ui.screen.advancedsettings.viewmodel
|
|||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT
|
||||
import co.electriccoin.zcash.ui.NavigationTargets
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.common.usecase.SensitiveSettingsVisibleUseCase
|
||||
import co.electriccoin.zcash.ui.design.component.ButtonState
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiSettingsListItemState
|
||||
import co.electriccoin.zcash.ui.design.util.stringRes
|
||||
import co.electriccoin.zcash.ui.screen.advancedsettings.model.AdvancedSettingsState
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.WhileSubscribed
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class AdvancedSettingsViewModel : ViewModel() {
|
||||
val state =
|
||||
MutableStateFlow(
|
||||
AdvancedSettingsState(
|
||||
onBack = ::onBack,
|
||||
items =
|
||||
persistentListOf(
|
||||
ZashiSettingsListItemState(
|
||||
text = stringRes(R.string.advanced_settings_recovery),
|
||||
icon = R.drawable.ic_advanced_settings_recovery,
|
||||
onClick = {}
|
||||
),
|
||||
ZashiSettingsListItemState(
|
||||
text = stringRes(R.string.advanced_settings_export),
|
||||
icon = R.drawable.ic_advanced_settings_export,
|
||||
onClick = {}
|
||||
),
|
||||
ZashiSettingsListItemState(
|
||||
text = stringRes(R.string.advanced_settings_choose_server),
|
||||
icon =
|
||||
R.drawable.ic_advanced_settings_choose_server,
|
||||
onClick = ::onChooseServerClick
|
||||
),
|
||||
ZashiSettingsListItemState(
|
||||
text = stringRes(R.string.advanced_settings_currency_conversion),
|
||||
icon =
|
||||
R.drawable.ic_advanced_settings_currency_conversion,
|
||||
onClick = ::onCurrencyConversionClick
|
||||
)
|
||||
),
|
||||
deleteButton =
|
||||
ButtonState(
|
||||
stringRes(R.string.advanced_settings_delete_button),
|
||||
onClick = {}
|
||||
)
|
||||
class AdvancedSettingsViewModel(
|
||||
isSensitiveSettingsVisible: SensitiveSettingsVisibleUseCase
|
||||
) : ViewModel() {
|
||||
val state: StateFlow<AdvancedSettingsState> =
|
||||
isSensitiveSettingsVisible()
|
||||
.map { isSensitiveSettingsVisible ->
|
||||
createState(isSensitiveSettingsVisible)
|
||||
}
|
||||
.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
|
||||
initialValue = createState(isSensitiveSettingsVisible().value)
|
||||
)
|
||||
).asStateFlow()
|
||||
|
||||
private fun createState(isSensitiveSettingsVisible: Boolean) =
|
||||
AdvancedSettingsState(
|
||||
onBack = ::onBack,
|
||||
items =
|
||||
listOfNotNull(
|
||||
ZashiSettingsListItemState(
|
||||
text = stringRes(R.string.advanced_settings_recovery),
|
||||
icon = R.drawable.ic_advanced_settings_recovery,
|
||||
onClick = {}
|
||||
),
|
||||
ZashiSettingsListItemState(
|
||||
text = stringRes(R.string.advanced_settings_export),
|
||||
icon = R.drawable.ic_advanced_settings_export,
|
||||
onClick = {}
|
||||
),
|
||||
ZashiSettingsListItemState(
|
||||
text = stringRes(R.string.advanced_settings_choose_server),
|
||||
icon =
|
||||
R.drawable.ic_advanced_settings_choose_server,
|
||||
onClick = ::onChooseServerClick
|
||||
).takeIf { isSensitiveSettingsVisible },
|
||||
ZashiSettingsListItemState(
|
||||
text = stringRes(R.string.advanced_settings_currency_conversion),
|
||||
icon =
|
||||
R.drawable.ic_advanced_settings_currency_conversion,
|
||||
onClick = ::onCurrencyConversionClick
|
||||
).takeIf { isSensitiveSettingsVisible }
|
||||
).toImmutableList(),
|
||||
deleteButton =
|
||||
ButtonState(
|
||||
stringRes(R.string.advanced_settings_delete_button),
|
||||
onClick = {}
|
||||
),
|
||||
)
|
||||
|
||||
val navigationCommand = MutableSharedFlow<String>()
|
||||
val backNavigationCommand = MutableSharedFlow<Unit>()
|
||||
|
|
|
@ -36,10 +36,8 @@ internal fun WrapSettings() {
|
|||
settingsViewModel.onBack()
|
||||
}
|
||||
|
||||
state?.let {
|
||||
Settings(
|
||||
state = it,
|
||||
topAppBarSubTitleState = walletState,
|
||||
)
|
||||
}
|
||||
Settings(
|
||||
state = state,
|
||||
topAppBarSubTitleState = walletState,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import kotlinx.collections.immutable.ImmutableList
|
|||
|
||||
data class SettingsState(
|
||||
val version: StringResource,
|
||||
val isLoading: Boolean,
|
||||
val onBack: () -> Unit,
|
||||
val debugMenu: SettingsTroubleshootingState?,
|
||||
val items: ImmutableList<ZashiSettingsListItemState>,
|
||||
|
|
|
@ -29,7 +29,6 @@ import androidx.compose.ui.unit.dp
|
|||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
|
||||
import co.electriccoin.zcash.ui.design.component.BlankBgScaffold
|
||||
import co.electriccoin.zcash.ui.design.component.CircularScreenProgressIndicator
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiHorizontalDivider
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiSettingsListItem
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiSettingsListItemState
|
||||
|
@ -59,31 +58,27 @@ fun Settings(
|
|||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
if (state.isLoading) {
|
||||
CircularScreenProgressIndicator()
|
||||
} else {
|
||||
Column(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(
|
||||
top = paddingValues.calculateTopPadding(),
|
||||
bottom = paddingValues.calculateBottomPadding() + ZashiDimensions.Spacing.spacing3xl,
|
||||
start = 4.dp,
|
||||
end = 4.dp
|
||||
),
|
||||
) {
|
||||
state.items.forEachIndexed { index, item ->
|
||||
ZashiSettingsListItem(state = item)
|
||||
if (index != state.items.lastIndex) {
|
||||
ZashiHorizontalDivider()
|
||||
}
|
||||
Column(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(
|
||||
top = paddingValues.calculateTopPadding(),
|
||||
bottom = paddingValues.calculateBottomPadding() + ZashiDimensions.Spacing.spacing3xl,
|
||||
start = 4.dp,
|
||||
end = 4.dp
|
||||
),
|
||||
) {
|
||||
state.items.forEachIndexed { index, item ->
|
||||
ZashiSettingsListItem(state = item)
|
||||
if (index != state.items.lastIndex) {
|
||||
ZashiHorizontalDivider()
|
||||
}
|
||||
Spacer(modifier = Modifier.height(ZashiDimensions.Spacing.spacingXl))
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
ZashiVersion(modifier = Modifier.align(CenterHorizontally), version = state.version)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(ZashiDimensions.Spacing.spacingXl))
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
ZashiVersion(modifier = Modifier.align(CenterHorizontally), version = state.version)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -197,53 +192,6 @@ private fun PreviewSettings() {
|
|||
Settings(
|
||||
state =
|
||||
SettingsState(
|
||||
isLoading = false,
|
||||
version = stringRes("Version 1.2"),
|
||||
debugMenu = null,
|
||||
onBack = {},
|
||||
items =
|
||||
persistentListOf(
|
||||
ZashiSettingsListItemState(
|
||||
text = stringRes(R.string.settings_address_book),
|
||||
icon = R.drawable.ic_settings_address_book,
|
||||
onClick = { },
|
||||
),
|
||||
ZashiSettingsListItemState(
|
||||
text = stringRes(R.string.settings_integrations),
|
||||
icon = R.drawable.ic_settings_integrations,
|
||||
onClick = { },
|
||||
titleIcons = persistentListOf(R.drawable.ic_integrations_coinbase)
|
||||
),
|
||||
ZashiSettingsListItemState(
|
||||
text = stringRes(R.string.settings_advanced_settings),
|
||||
icon = R.drawable.ic_advanced_settings,
|
||||
onClick = { },
|
||||
),
|
||||
ZashiSettingsListItemState(
|
||||
text = stringRes(R.string.settings_about_us),
|
||||
icon = R.drawable.ic_settings_info,
|
||||
onClick = { },
|
||||
),
|
||||
ZashiSettingsListItemState(
|
||||
text = stringRes(R.string.settings_feedback),
|
||||
icon = R.drawable.ic_settings_feedback,
|
||||
onClick = { },
|
||||
),
|
||||
),
|
||||
),
|
||||
topAppBarSubTitleState = TopAppBarSubTitleState.None,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewScreens
|
||||
@Composable
|
||||
private fun PreviewSettingsLoading() {
|
||||
ZcashTheme {
|
||||
Settings(
|
||||
state =
|
||||
SettingsState(
|
||||
isLoading = true,
|
||||
version = stringRes("Version 1.2"),
|
||||
debugMenu = null,
|
||||
onBack = {},
|
||||
|
|
|
@ -14,6 +14,7 @@ import co.electriccoin.zcash.ui.common.provider.GetVersionInfoProvider
|
|||
import co.electriccoin.zcash.ui.common.usecase.IsFlexaAvailableUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.ObserveConfigurationUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.RescanBlockchainUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.SensitiveSettingsVisibleUseCase
|
||||
import co.electriccoin.zcash.ui.configuration.ConfigurationEntries
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiSettingsListItemState
|
||||
import co.electriccoin.zcash.ui.design.util.stringRes
|
||||
|
@ -22,14 +23,12 @@ import co.electriccoin.zcash.ui.screen.addressbook.AddressBookArgs
|
|||
import co.electriccoin.zcash.ui.screen.settings.model.SettingsState
|
||||
import co.electriccoin.zcash.ui.screen.settings.model.SettingsTroubleshootingState
|
||||
import co.electriccoin.zcash.ui.screen.settings.model.TroubleshootingItemState
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.WhileSubscribed
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
@ -38,6 +37,7 @@ import kotlinx.coroutines.launch
|
|||
@Suppress("TooManyFunctions")
|
||||
class SettingsViewModel(
|
||||
observeConfiguration: ObserveConfigurationUseCase,
|
||||
isSensitiveSettingsVisible: SensitiveSettingsVisibleUseCase,
|
||||
private val standardPreferenceProvider: StandardPreferenceProvider,
|
||||
private val getVersionInfo: GetVersionInfoProvider,
|
||||
private val rescanBlockchain: RescanBlockchainUseCase,
|
||||
|
@ -50,15 +50,6 @@ class SettingsViewModel(
|
|||
private val isKeepScreenOnWhileSyncingEnabled =
|
||||
booleanStateFlow(StandardPreferenceKeys.IS_KEEP_SCREEN_ON_DURING_SYNC)
|
||||
|
||||
private val isLoading =
|
||||
combine(
|
||||
isAnalyticsEnabled,
|
||||
isBackgroundSyncEnabled,
|
||||
isKeepScreenOnWhileSyncingEnabled
|
||||
) { isAnalyticsEnabled, isBackgroundSync, isKeepScreenOnWhileSyncing ->
|
||||
isAnalyticsEnabled == null || isBackgroundSync == null || isKeepScreenOnWhileSyncing == null
|
||||
}.distinctUntilChanged()
|
||||
|
||||
@Suppress("ComplexCondition")
|
||||
private val troubleshootingState =
|
||||
combine(
|
||||
|
@ -98,48 +89,63 @@ class SettingsViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
val state: StateFlow<SettingsState?> =
|
||||
combine(isLoading, troubleshootingState) { isLoading, troubleshootingState ->
|
||||
SettingsState(
|
||||
isLoading = isLoading,
|
||||
debugMenu = troubleshootingState,
|
||||
onBack = ::onBack,
|
||||
items =
|
||||
persistentListOf(
|
||||
ZashiSettingsListItemState(
|
||||
text = stringRes(R.string.settings_address_book),
|
||||
icon = R.drawable.ic_settings_address_book,
|
||||
onClick = ::onAddressBookClick
|
||||
),
|
||||
ZashiSettingsListItemState(
|
||||
text = stringRes(R.string.settings_integrations),
|
||||
icon = R.drawable.ic_settings_integrations,
|
||||
onClick = ::onIntegrationsClick,
|
||||
titleIcons =
|
||||
listOfNotNull(
|
||||
R.drawable.ic_integrations_coinbase,
|
||||
R.drawable.ic_integrations_flexa.takeIf { isFlexaAvailable() }
|
||||
).toImmutableList()
|
||||
),
|
||||
ZashiSettingsListItemState(
|
||||
text = stringRes(R.string.settings_advanced_settings),
|
||||
icon = R.drawable.ic_advanced_settings,
|
||||
onClick = ::onAdvancedSettingsClick
|
||||
),
|
||||
ZashiSettingsListItemState(
|
||||
text = stringRes(R.string.settings_about_us),
|
||||
icon = R.drawable.ic_settings_info,
|
||||
onClick = ::onAboutUsClick
|
||||
),
|
||||
ZashiSettingsListItemState(
|
||||
text = stringRes(R.string.settings_feedback),
|
||||
icon = R.drawable.ic_settings_feedback,
|
||||
onClick = ::onSendUsFeedbackClick
|
||||
),
|
||||
),
|
||||
version = stringRes(R.string.settings_version, versionInfo.versionName)
|
||||
)
|
||||
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), null)
|
||||
val state: StateFlow<SettingsState> =
|
||||
combine(
|
||||
troubleshootingState,
|
||||
isSensitiveSettingsVisible()
|
||||
) { troubleshootingState, isSensitiveSettingsVisible ->
|
||||
createState(troubleshootingState, isSensitiveSettingsVisible)
|
||||
}.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
|
||||
initialValue =
|
||||
createState(
|
||||
troubleshootingState = null,
|
||||
isSensitiveSettingsVisible = isSensitiveSettingsVisible().value
|
||||
)
|
||||
)
|
||||
|
||||
private fun createState(
|
||||
troubleshootingState: SettingsTroubleshootingState?,
|
||||
isSensitiveSettingsVisible: Boolean
|
||||
) = SettingsState(
|
||||
debugMenu = troubleshootingState,
|
||||
onBack = ::onBack,
|
||||
items =
|
||||
listOfNotNull(
|
||||
ZashiSettingsListItemState(
|
||||
text = stringRes(R.string.settings_address_book),
|
||||
icon = R.drawable.ic_settings_address_book,
|
||||
onClick = ::onAddressBookClick
|
||||
),
|
||||
ZashiSettingsListItemState(
|
||||
text = stringRes(R.string.settings_integrations),
|
||||
icon = R.drawable.ic_settings_integrations,
|
||||
onClick = ::onIntegrationsClick,
|
||||
titleIcons =
|
||||
listOfNotNull(
|
||||
R.drawable.ic_integrations_coinbase,
|
||||
R.drawable.ic_integrations_flexa.takeIf { isFlexaAvailable() }
|
||||
).toImmutableList()
|
||||
).takeIf { isSensitiveSettingsVisible },
|
||||
ZashiSettingsListItemState(
|
||||
text = stringRes(R.string.settings_advanced_settings),
|
||||
icon = R.drawable.ic_advanced_settings,
|
||||
onClick = ::onAdvancedSettingsClick
|
||||
),
|
||||
ZashiSettingsListItemState(
|
||||
text = stringRes(R.string.settings_about_us),
|
||||
icon = R.drawable.ic_settings_info,
|
||||
onClick = ::onAboutUsClick
|
||||
),
|
||||
ZashiSettingsListItemState(
|
||||
text = stringRes(R.string.settings_feedback),
|
||||
icon = R.drawable.ic_settings_feedback,
|
||||
onClick = ::onSendUsFeedbackClick
|
||||
),
|
||||
).toImmutableList(),
|
||||
version = stringRes(R.string.settings_version, versionInfo.versionName)
|
||||
)
|
||||
|
||||
val navigationCommand = MutableSharedFlow<String>()
|
||||
val backNavigationCommand = MutableSharedFlow<Unit>()
|
||||
|
|
|
@ -10,9 +10,12 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import co.electriccoin.zcash.di.koinActivityViewModel
|
||||
import co.electriccoin.zcash.ui.NavigationTargets.SETTINGS
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.common.compose.LocalActivity
|
||||
import co.electriccoin.zcash.ui.common.compose.LocalNavController
|
||||
import co.electriccoin.zcash.ui.common.viewmodel.CheckUpdateViewModel
|
||||
import co.electriccoin.zcash.ui.navigateJustOnce
|
||||
import co.electriccoin.zcash.ui.screen.update.model.UpdateInfo
|
||||
import co.electriccoin.zcash.ui.screen.update.model.UpdateState
|
||||
import co.electriccoin.zcash.ui.screen.update.view.Update
|
||||
|
@ -29,12 +32,19 @@ internal fun WrapCheckForUpdate() {
|
|||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
val checkUpdateViewModel = koinActivityViewModel<CheckUpdateViewModel>()
|
||||
|
||||
// Check for an app update asynchronously. We create an effect that matches the activity
|
||||
// lifecycle. If the wrapping compose recomposes, the check shouldn't run again.
|
||||
LaunchedEffect(true) {
|
||||
checkUpdateViewModel.checkForAppUpdate()
|
||||
}
|
||||
|
||||
val activity = LocalActivity.current
|
||||
|
||||
val inputUpdateInfo = checkUpdateViewModel.updateInfo.collectAsStateWithLifecycle().value ?: return
|
||||
|
||||
val viewModel = koinActivityViewModel<UpdateViewModel> { parametersOf(inputUpdateInfo) }
|
||||
val updateInfo = viewModel.updateInfo.collectAsStateWithLifecycle().value
|
||||
val navController = LocalNavController.current
|
||||
|
||||
if (updateInfo.appUpdateInfo != null && updateInfo.state == UpdateState.Prepared) {
|
||||
WrapUpdate(
|
||||
|
@ -46,15 +56,12 @@ internal fun WrapCheckForUpdate() {
|
|||
activity = activity,
|
||||
appUpdateInfo = updateInfo.appUpdateInfo
|
||||
)
|
||||
},
|
||||
onSettings = {
|
||||
navController.navigateJustOnce(SETTINGS)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// Check for an app update asynchronously. We create an effect that matches the activity
|
||||
// lifecycle. If the wrapping compose recomposes, the check shouldn't run again.
|
||||
LaunchedEffect(true) {
|
||||
checkUpdateViewModel.checkForAppUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
@ -64,6 +71,7 @@ internal fun WrapUpdate(
|
|||
checkForUpdate: () -> Unit,
|
||||
remindLater: () -> Unit,
|
||||
goForUpdate: () -> Unit,
|
||||
onSettings: () -> Unit
|
||||
) {
|
||||
val activity = LocalActivity.current
|
||||
|
||||
|
@ -111,7 +119,8 @@ internal fun WrapUpdate(
|
|||
snackbarHostState,
|
||||
scope
|
||||
)
|
||||
}
|
||||
},
|
||||
onSettings = onSettings
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -19,12 +19,10 @@ import kotlinx.coroutines.flow.callbackFlow
|
|||
import kotlinx.coroutines.flow.flow
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
class AppUpdateCheckerMock private constructor() : AppUpdateChecker {
|
||||
class AppUpdateCheckerMock : AppUpdateChecker {
|
||||
companion object {
|
||||
private const val DEFAULT_STALENESS_DAYS = 3
|
||||
|
||||
fun new() = AppUpdateCheckerMock()
|
||||
|
||||
// Used mostly for tests
|
||||
val resultUpdateInfo =
|
||||
UpdateInfoFixture.new(
|
||||
|
|
|
@ -6,5 +6,4 @@ package co.electriccoin.zcash.ui.screen.update
|
|||
object UpdateTag {
|
||||
const val BTN_LATER = "btn_later"
|
||||
const val BTN_DOWNLOAD = "btn_download"
|
||||
const val PROGRESSBAR_DOWNLOADING = "progressbar_downloading"
|
||||
}
|
||||
|
|
|
@ -2,327 +2,197 @@ package co.electriccoin.zcash.ui.screen.update.view
|
|||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
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.CircularProgressIndicator
|
||||
import androidx.compose.material3.DividerDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.LinkAnnotation
|
||||
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.tooling.preview.Preview
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.text.withLink
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.design.component.BlankBgScaffold
|
||||
import co.electriccoin.zcash.ui.design.component.Body
|
||||
import co.electriccoin.zcash.ui.design.component.Header
|
||||
import co.electriccoin.zcash.ui.design.component.Reference
|
||||
import co.electriccoin.zcash.ui.design.component.SmallTopAppBar
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiButton
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiSmallTopAppBar
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiTopAppBarCloseNavigation
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiTopAppBarHamburgerNavigation
|
||||
import co.electriccoin.zcash.ui.design.component.zashiVerticalGradient
|
||||
import co.electriccoin.zcash.ui.design.newcomponent.PreviewScreens
|
||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||
import co.electriccoin.zcash.ui.design.theme.dimensions.ZashiDimensions
|
||||
import co.electriccoin.zcash.ui.design.theme.colors.ZashiColors
|
||||
import co.electriccoin.zcash.ui.design.theme.typography.ZashiTypography
|
||||
import co.electriccoin.zcash.ui.design.util.scaffoldPadding
|
||||
import co.electriccoin.zcash.ui.fixture.UpdateInfoFixture
|
||||
import co.electriccoin.zcash.ui.screen.update.UpdateTag
|
||||
import co.electriccoin.zcash.ui.screen.update.UpdateTag.BTN_DOWNLOAD
|
||||
import co.electriccoin.zcash.ui.screen.update.UpdateTag.BTN_LATER
|
||||
import co.electriccoin.zcash.ui.screen.update.model.UpdateInfo
|
||||
import co.electriccoin.zcash.ui.screen.update.model.UpdateState
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun UpdatePreview() {
|
||||
ZcashTheme(forceDarkMode = false) {
|
||||
Update(
|
||||
snackbarHostState = SnackbarHostState(),
|
||||
UpdateInfoFixture.new(appUpdateInfo = null),
|
||||
onDownload = {},
|
||||
onLater = {},
|
||||
onReference = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun UpdateRequiredPreview() {
|
||||
ZcashTheme(forceDarkMode = false) {
|
||||
Update(
|
||||
snackbarHostState = SnackbarHostState(),
|
||||
UpdateInfoFixture.new(force = true),
|
||||
onDownload = {},
|
||||
onLater = {},
|
||||
onReference = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun UpdateAvailableDarkPreview() {
|
||||
ZcashTheme(forceDarkMode = true) {
|
||||
Update(
|
||||
snackbarHostState = SnackbarHostState(),
|
||||
UpdateInfoFixture.new(appUpdateInfo = null),
|
||||
onDownload = {},
|
||||
onLater = {},
|
||||
onReference = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun UpdateRequiredDarkPreview() {
|
||||
ZcashTheme(forceDarkMode = true) {
|
||||
Update(
|
||||
snackbarHostState = SnackbarHostState(),
|
||||
UpdateInfoFixture.new(force = true),
|
||||
onDownload = {},
|
||||
onLater = {},
|
||||
onReference = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Update(
|
||||
snackbarHostState: SnackbarHostState,
|
||||
updateInfo: UpdateInfo,
|
||||
onDownload: (state: UpdateState) -> Unit,
|
||||
onLater: () -> Unit,
|
||||
onReference: () -> Unit
|
||||
onReference: () -> Unit,
|
||||
onSettings: () -> Unit
|
||||
) {
|
||||
BlankBgScaffold(
|
||||
topBar = {
|
||||
UpdateTopAppBar(updateInfo = updateInfo)
|
||||
},
|
||||
snackbarHost = {
|
||||
SnackbarHost(snackbarHostState)
|
||||
},
|
||||
bottomBar = {
|
||||
UpdateBottomAppBar(
|
||||
updateInfo,
|
||||
onDownload,
|
||||
onLater,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
UpdateContent(
|
||||
onReference = onReference,
|
||||
updateInfo = updateInfo,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.scaffoldPadding(paddingValues)
|
||||
)
|
||||
}
|
||||
UpdateOverlayRunning(updateInfo)
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Composable
|
||||
fun UpdateOverlayRunning(updateInfo: UpdateInfo) {
|
||||
if (updateInfo.state == UpdateState.Running) {
|
||||
Column(
|
||||
Modifier
|
||||
.background(ZcashTheme.colors.overlay.copy(0.65f))
|
||||
.fillMaxSize()
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = null // Set indication to null to disable ripple effect
|
||||
) {}
|
||||
.testTag(UpdateTag.PROGRESSBAR_DOWNLOADING),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
CircularProgressIndicator(color = ZcashTheme.colors.overlayProgressBar)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UpdateTopAppBar(updateInfo: UpdateInfo) {
|
||||
SmallTopAppBar(
|
||||
titleText =
|
||||
stringResource(
|
||||
updateInfo.isForce.let { force ->
|
||||
if (force) {
|
||||
R.string.update_critical_header
|
||||
Box(
|
||||
modifier =
|
||||
Modifier.background(
|
||||
zashiVerticalGradient(
|
||||
if (updateInfo.isForce) {
|
||||
ZashiColors.Utility.WarningYellow.utilityOrange100
|
||||
} else {
|
||||
R.string.update_header
|
||||
ZashiColors.Utility.Purple.utilityPurple100
|
||||
}
|
||||
}
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Suppress("LongMethod")
|
||||
private fun UpdateBottomAppBar(
|
||||
updateInfo: UpdateInfo,
|
||||
onDownload: (state: UpdateState) -> Unit,
|
||||
onLater: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
HorizontalDivider(
|
||||
thickness = DividerDefaults.Thickness,
|
||||
color = ZcashTheme.colors.primaryDividerColor
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(
|
||||
top = ZashiDimensions.Spacing.spacingLg,
|
||||
bottom = ZashiDimensions.Spacing.spacing3xl,
|
||||
start = ZashiDimensions.Spacing.spacing3xl,
|
||||
end = ZashiDimensions.Spacing.spacing3xl
|
||||
),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
ZashiButton(
|
||||
onClick = { onDownload(UpdateState.Running) },
|
||||
text = stringResource(R.string.update_download_button),
|
||||
modifier =
|
||||
Modifier
|
||||
.testTag(UpdateTag.BTN_DOWNLOAD)
|
||||
.fillMaxWidth(),
|
||||
enabled = updateInfo.state != UpdateState.Running,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault))
|
||||
|
||||
if (updateInfo.isForce) {
|
||||
Text(
|
||||
text = stringResource(R.string.update_later_disabled_button),
|
||||
textAlign = TextAlign.Center,
|
||||
style = ZcashTheme.typography.primary.bodyLarge,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = ZcashTheme.colors.textPrimary,
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(all = ZcashTheme.dimens.spacingDefault)
|
||||
.testTag(UpdateTag.BTN_LATER)
|
||||
)
|
||||
} else {
|
||||
Reference(
|
||||
text = stringResource(R.string.update_later_enabled_button),
|
||||
onClick = {
|
||||
if (updateInfo.state != UpdateState.Running) {
|
||||
onLater()
|
||||
} else {
|
||||
// Keep current state
|
||||
)
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
ZashiSmallTopAppBar(
|
||||
title = null,
|
||||
subtitle = null,
|
||||
colors = ZcashTheme.colors.topAppBarColors.copyColors(containerColor = Color.Transparent),
|
||||
navigationAction = {
|
||||
if (updateInfo.isForce.not()) {
|
||||
ZashiTopAppBarCloseNavigation(modifier = Modifier.testTag(BTN_LATER), onBack = onLater)
|
||||
}
|
||||
},
|
||||
hamburgerMenuActions = {
|
||||
if (updateInfo.isForce) {
|
||||
ZashiTopAppBarHamburgerNavigation(onSettings)
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
snackbarHost = {
|
||||
SnackbarHost(snackbarHostState)
|
||||
},
|
||||
containerColor = Color.Transparent
|
||||
) {
|
||||
Column(modifier = Modifier.scaffoldPadding(it)) {
|
||||
@Suppress("MagicNumber")
|
||||
Spacer(Modifier.weight(.75f))
|
||||
Image(
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||
painter =
|
||||
painterResource(
|
||||
if (updateInfo.isForce) {
|
||||
R.drawable.ic_update_required
|
||||
} else {
|
||||
R.drawable.ic_update
|
||||
}
|
||||
),
|
||||
contentDescription = ""
|
||||
)
|
||||
Spacer(Modifier.height(24.dp))
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
textAlign = TextAlign.Center,
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(all = ZcashTheme.dimens.spacingDefault)
|
||||
.testTag(UpdateTag.BTN_LATER)
|
||||
text =
|
||||
if (updateInfo.isForce) {
|
||||
stringResource(id = R.string.update_title_required)
|
||||
} else {
|
||||
stringResource(id = R.string.update_title_available)
|
||||
},
|
||||
style = ZashiTypography.header6,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = ZashiColors.Text.textPrimary
|
||||
)
|
||||
Spacer(Modifier.height(12.dp))
|
||||
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text =
|
||||
buildAnnotatedString {
|
||||
append(
|
||||
if (updateInfo.isForce) {
|
||||
stringResource(id = R.string.update_description_required)
|
||||
} else {
|
||||
stringResource(id = R.string.update_description_available)
|
||||
}
|
||||
)
|
||||
appendLine()
|
||||
appendLine()
|
||||
|
||||
withStyle(
|
||||
style =
|
||||
SpanStyle(
|
||||
textDecoration = TextDecoration.Underline
|
||||
)
|
||||
) {
|
||||
withLink(
|
||||
LinkAnnotation.Clickable(CLICKABLE_TAG) {
|
||||
if (updateInfo.state != UpdateState.Running) {
|
||||
onReference()
|
||||
}
|
||||
}
|
||||
) {
|
||||
append(stringResource(R.string.update_link_text))
|
||||
}
|
||||
}
|
||||
},
|
||||
style = ZashiTypography.textSm,
|
||||
textAlign = TextAlign.Center,
|
||||
color = ZashiColors.Text.textPrimary,
|
||||
)
|
||||
Spacer(Modifier.weight(1f))
|
||||
ZashiButton(
|
||||
modifier = Modifier.fillMaxWidth().testTag(BTN_DOWNLOAD),
|
||||
text = stringResource(R.string.update_download_button),
|
||||
onClick = { onDownload(UpdateState.Running) },
|
||||
enabled = updateInfo.state != UpdateState.Running,
|
||||
isLoading = updateInfo.state == UpdateState.Running
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewScreens
|
||||
@Composable
|
||||
@Suppress("LongMethod")
|
||||
private fun UpdateContent(
|
||||
onReference: () -> Unit,
|
||||
updateInfo: UpdateInfo,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val appName = stringResource(id = R.string.app_name)
|
||||
|
||||
Column(
|
||||
modifier =
|
||||
modifier.then(
|
||||
Modifier
|
||||
.fillMaxHeight()
|
||||
.verticalScroll(
|
||||
rememberScrollState()
|
||||
)
|
||||
),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Image(
|
||||
imageVector =
|
||||
if (updateInfo.isForce) {
|
||||
ImageVector.vectorResource(R.drawable.ic_zashi_logo_sign_warn)
|
||||
} else {
|
||||
ImageVector.vectorResource(R.drawable.ic_zashi_logo_update_available)
|
||||
},
|
||||
colorFilter = ColorFilter.tint(color = ZcashTheme.colors.secondaryColor),
|
||||
contentDescription = null
|
||||
private fun UpdatePreview() =
|
||||
ZcashTheme {
|
||||
Update(
|
||||
snackbarHostState = SnackbarHostState(),
|
||||
updateInfo = UpdateInfoFixture.new(appUpdateInfo = null),
|
||||
onDownload = {},
|
||||
onLater = {},
|
||||
onReference = {},
|
||||
onSettings = {}
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingBig))
|
||||
|
||||
Header(
|
||||
text =
|
||||
if (updateInfo.isForce) {
|
||||
stringResource(id = R.string.update_title_required)
|
||||
} else {
|
||||
stringResource(id = R.string.update_title_available, appName)
|
||||
},
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge))
|
||||
|
||||
Body(
|
||||
text =
|
||||
if (updateInfo.isForce) {
|
||||
stringResource(id = R.string.update_description_required, appName)
|
||||
} else {
|
||||
stringResource(id = R.string.update_description_available, appName)
|
||||
},
|
||||
textAlign = TextAlign.Center,
|
||||
color = ZcashTheme.colors.textDescriptionDark
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault))
|
||||
|
||||
Reference(
|
||||
text = stringResource(id = R.string.update_link_text),
|
||||
onClick = {
|
||||
if (updateInfo.state != UpdateState.Running) {
|
||||
onReference()
|
||||
} else {
|
||||
// Keep current state
|
||||
}
|
||||
},
|
||||
fontWeight = FontWeight.Normal,
|
||||
textStyle = ZcashTheme.typography.primary.bodyMedium,
|
||||
textAlign = TextAlign.Center,
|
||||
color = ZcashTheme.colors.textDescriptionDark,
|
||||
modifier = Modifier.padding(all = ZcashTheme.dimens.spacingDefault)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault))
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewScreens
|
||||
@Composable
|
||||
private fun UpdateRequiredPreview() =
|
||||
ZcashTheme {
|
||||
Update(
|
||||
snackbarHostState = SnackbarHostState(),
|
||||
updateInfo = UpdateInfoFixture.new(force = true),
|
||||
onDownload = {},
|
||||
onLater = {},
|
||||
onReference = {},
|
||||
onSettings = {}
|
||||
)
|
||||
}
|
||||
|
||||
private const val CLICKABLE_TAG = "clickable"
|
||||
|
|
|
@ -12,8 +12,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||
import co.electriccoin.zcash.di.koinActivityViewModel
|
||||
import co.electriccoin.zcash.ui.MainActivity
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
|
||||
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
|
||||
import co.electriccoin.zcash.ui.screen.warning.view.NotEnoughSpaceView
|
||||
import co.electriccoin.zcash.ui.screen.warning.viewmodel.StorageCheckViewModel
|
||||
import co.electriccoin.zcash.ui.util.SettingsUtil
|
||||
|
@ -24,12 +22,8 @@ fun MainActivity.WrapNotEnoughSpace(
|
|||
goPrevious: () -> Unit,
|
||||
goSettings: () -> Unit
|
||||
) {
|
||||
val walletViewModel = koinActivityViewModel<WalletViewModel>()
|
||||
|
||||
val storageCheckViewModel = koinActivityViewModel<StorageCheckViewModel>()
|
||||
|
||||
val walletState = walletViewModel.walletStateInformation.collectAsStateWithLifecycle().value
|
||||
|
||||
val isEnoughFreeSpace = storageCheckViewModel.isEnoughSpace.collectAsStateWithLifecycle().value
|
||||
if (isEnoughFreeSpace == true) {
|
||||
goPrevious()
|
||||
|
@ -46,7 +40,6 @@ fun MainActivity.WrapNotEnoughSpace(
|
|||
goSettings = goSettings,
|
||||
spaceAvailableMegabytes = spaceAvailableMegabytes.value ?: 0,
|
||||
requiredStorageSpaceGigabytes = requiredStorageSpaceGigabytes,
|
||||
walletState = walletState,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -55,7 +48,6 @@ private fun WrapNotEnoughFreeSpace(
|
|||
goSettings: () -> Unit,
|
||||
requiredStorageSpaceGigabytes: Int,
|
||||
spaceAvailableMegabytes: Int,
|
||||
walletState: TopAppBarSubTitleState,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
|
@ -81,6 +73,5 @@ private fun WrapNotEnoughFreeSpace(
|
|||
snackbarHostState = snackbarHostState,
|
||||
storageSpaceRequiredGigabytes = requiredStorageSpaceGigabytes,
|
||||
spaceAvailableMegabytes = spaceAvailableMegabytes,
|
||||
topAppBarSubTitleState = walletState,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,189 +1,141 @@
|
|||
package co.electriccoin.zcash.ui.screen.warning.view
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
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.platform.testTag
|
||||
import androidx.compose.ui.graphics.Color
|
||||
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.tooling.preview.Preview
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
|
||||
import co.electriccoin.zcash.ui.common.test.CommonTag
|
||||
import co.electriccoin.zcash.ui.design.MINIMAL_WEIGHT
|
||||
import co.electriccoin.zcash.ui.design.component.BlankBgScaffold
|
||||
import co.electriccoin.zcash.ui.design.component.Body
|
||||
import co.electriccoin.zcash.ui.design.component.Header
|
||||
import co.electriccoin.zcash.ui.design.component.SmallTopAppBar
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiButton
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiSmallTopAppBar
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiTopAppBarHamburgerNavigation
|
||||
import co.electriccoin.zcash.ui.design.component.zashiVerticalGradient
|
||||
import co.electriccoin.zcash.ui.design.newcomponent.PreviewScreens
|
||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||
import co.electriccoin.zcash.ui.design.theme.colors.ZashiColors
|
||||
import co.electriccoin.zcash.ui.design.theme.typography.ZashiTypography
|
||||
import co.electriccoin.zcash.ui.design.util.scaffoldPadding
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun NotEnoughSpacePreview() {
|
||||
ZcashTheme(forceDarkMode = false) {
|
||||
NotEnoughSpaceView(
|
||||
onSettings = {},
|
||||
onSystemSettings = {},
|
||||
snackbarHostState = SnackbarHostState(),
|
||||
spaceAvailableMegabytes = 300,
|
||||
storageSpaceRequiredGigabytes = 1,
|
||||
topAppBarSubTitleState = TopAppBarSubTitleState.None,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun NotEnoughSpaceDarkPreview() {
|
||||
ZcashTheme(forceDarkMode = true) {
|
||||
NotEnoughSpaceView(
|
||||
onSettings = {},
|
||||
onSystemSettings = {},
|
||||
snackbarHostState = SnackbarHostState(),
|
||||
spaceAvailableMegabytes = 300,
|
||||
storageSpaceRequiredGigabytes = 1,
|
||||
topAppBarSubTitleState = TopAppBarSubTitleState.None,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Suppress("LongParameterList")
|
||||
fun NotEnoughSpaceView(
|
||||
onSettings: () -> Unit,
|
||||
onSystemSettings: () -> Unit,
|
||||
spaceAvailableMegabytes: Int,
|
||||
storageSpaceRequiredGigabytes: Int,
|
||||
topAppBarSubTitleState: TopAppBarSubTitleState,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
) {
|
||||
BlankBgScaffold(
|
||||
topBar = {
|
||||
NotEnoughSpaceTopAppBar(
|
||||
onSettings = onSettings,
|
||||
subTitleState = topAppBarSubTitleState,
|
||||
Box(
|
||||
modifier =
|
||||
Modifier.background(
|
||||
zashiVerticalGradient(ZashiColors.Utility.ErrorRed.utilityError100)
|
||||
)
|
||||
},
|
||||
snackbarHost = { SnackbarHost(snackbarHostState) },
|
||||
) { paddingValues ->
|
||||
NotEnoughSpaceMainContent(
|
||||
onSystemSettings = onSystemSettings,
|
||||
spaceRequiredToContinueMegabytes = spaceAvailableMegabytes,
|
||||
storageSpaceRequiredGigabytes = storageSpaceRequiredGigabytes,
|
||||
modifier =
|
||||
Modifier
|
||||
.scaffoldPadding(paddingValues)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun NotEnoughSpaceTopAppBar(
|
||||
onSettings: () -> Unit,
|
||||
subTitleState: TopAppBarSubTitleState
|
||||
) {
|
||||
SmallTopAppBar(
|
||||
subTitle =
|
||||
when (subTitleState) {
|
||||
TopAppBarSubTitleState.Disconnected -> stringResource(id = R.string.disconnected_label)
|
||||
TopAppBarSubTitleState.Restoring -> stringResource(id = R.string.restoring_wallet_label)
|
||||
TopAppBarSubTitleState.None -> null
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
ZashiSmallTopAppBar(
|
||||
colors = ZcashTheme.colors.topAppBarColors.copyColors(containerColor = Color.Transparent),
|
||||
title = null,
|
||||
subtitle = null,
|
||||
hamburgerMenuActions = {
|
||||
ZashiTopAppBarHamburgerNavigation(onSettings)
|
||||
}
|
||||
)
|
||||
},
|
||||
titleText = stringResource(id = R.string.not_enough_space_title).uppercase(),
|
||||
hamburgerMenuActions = {
|
||||
IconButton(
|
||||
onClick = onSettings,
|
||||
modifier = Modifier.testTag(CommonTag.SETTINGS_TOP_BAR_BUTTON)
|
||||
) {
|
||||
snackbarHost = {
|
||||
SnackbarHost(snackbarHostState)
|
||||
},
|
||||
containerColor = Color.Transparent
|
||||
) {
|
||||
Column(modifier = Modifier.scaffoldPadding(it)) {
|
||||
@Suppress("MagicNumber")
|
||||
Spacer(Modifier.weight(.75f))
|
||||
Image(
|
||||
painter = painterResource(id = co.electriccoin.zcash.ui.design.R.drawable.ic_hamburger_menu),
|
||||
contentDescription = stringResource(id = R.string.settings_menu_content_description)
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||
painter = painterResource(R.drawable.ic_not_enough_space),
|
||||
contentDescription = ""
|
||||
)
|
||||
Spacer(Modifier.height(24.dp))
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
textAlign = TextAlign.Center,
|
||||
text = stringResource(id = R.string.not_enough_space_title),
|
||||
style = ZashiTypography.header6,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = ZashiColors.Text.textPrimary
|
||||
)
|
||||
Spacer(Modifier.height(12.dp))
|
||||
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text =
|
||||
buildAnnotatedString {
|
||||
append(
|
||||
stringResource(
|
||||
R.string.not_enough_space_description_1,
|
||||
storageSpaceRequiredGigabytes
|
||||
) + " "
|
||||
)
|
||||
withStyle(
|
||||
SpanStyle(
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
) {
|
||||
append(
|
||||
stringResource(R.string.not_enough_space_description_2, spaceAvailableMegabytes)
|
||||
)
|
||||
}
|
||||
append(
|
||||
stringResource(
|
||||
R.string.not_enough_space_description_3,
|
||||
storageSpaceRequiredGigabytes *
|
||||
GB_TO_MEGABYTES - spaceAvailableMegabytes
|
||||
)
|
||||
)
|
||||
},
|
||||
style = ZashiTypography.textSm,
|
||||
textAlign = TextAlign.Center,
|
||||
color = ZashiColors.Text.textPrimary,
|
||||
)
|
||||
Spacer(Modifier.weight(1f))
|
||||
ZashiButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(R.string.not_enough_space_system_settings_btn),
|
||||
onClick = onSystemSettings,
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun NotEnoughSpaceMainContent(
|
||||
onSystemSettings: () -> Unit,
|
||||
spaceRequiredToContinueMegabytes: Int,
|
||||
storageSpaceRequiredGigabytes: Int,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Column(
|
||||
modifier =
|
||||
modifier.then(
|
||||
Modifier
|
||||
.fillMaxHeight()
|
||||
.verticalScroll(
|
||||
rememberScrollState()
|
||||
)
|
||||
),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingBig))
|
||||
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_zashi_logo_sign_warn),
|
||||
colorFilter = ColorFilter.tint(color = ZcashTheme.colors.secondaryColor),
|
||||
contentDescription = null,
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(ZcashTheme.dimens.spacingBig))
|
||||
|
||||
Header(
|
||||
text =
|
||||
stringResource(
|
||||
id = R.string.not_enough_space_description_1,
|
||||
stringResource(id = R.string.app_name),
|
||||
storageSpaceRequiredGigabytes,
|
||||
spaceRequiredToContinueMegabytes
|
||||
),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(ZcashTheme.dimens.spacingLarge))
|
||||
|
||||
Body(
|
||||
text =
|
||||
stringResource(
|
||||
id = R.string.not_enough_space_description_2,
|
||||
stringResource(id = R.string.app_name)
|
||||
),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
Spacer(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxHeight()
|
||||
.weight(MINIMAL_WEIGHT)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault))
|
||||
|
||||
ZashiButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = onSystemSettings,
|
||||
text = stringResource(R.string.not_enough_space_system_settings_btn),
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(ZcashTheme.dimens.spacingHuge))
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewScreens
|
||||
@Composable
|
||||
private fun NotEnoughSpacePreview() =
|
||||
ZcashTheme {
|
||||
NotEnoughSpaceView(
|
||||
onSettings = {},
|
||||
onSystemSettings = {},
|
||||
snackbarHostState = SnackbarHostState(),
|
||||
spaceAvailableMegabytes = 300,
|
||||
storageSpaceRequiredGigabytes = 1,
|
||||
)
|
||||
}
|
||||
|
||||
private const val GB_TO_MEGABYTES = 1024
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="65dp"
|
||||
android:height="64dp"
|
||||
android:viewportWidth="65"
|
||||
android:viewportHeight="64">
|
||||
<path
|
||||
android:pathData="M0.5,32C0.5,14.327 14.827,0 32.5,0C50.173,0 64.5,14.327 64.5,32C64.5,49.673 50.173,64 32.5,64C14.827,64 0.5,49.673 0.5,32Z"
|
||||
android:fillColor="#3E1C96"/>
|
||||
<path
|
||||
android:pathData="M21.833,37.656C20.225,36.58 19.167,34.747 19.167,32.667C19.167,29.542 21.555,26.975 24.606,26.692C25.23,22.896 28.527,20 32.5,20C36.473,20 39.769,22.896 40.394,26.692C43.444,26.975 45.833,29.542 45.833,32.667C45.833,34.747 44.775,36.58 43.167,37.656M27.167,38.667L32.5,44M32.5,44L37.833,38.667M32.5,44V32"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2.66667"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#BDB4FE"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
|
@ -0,0 +1,17 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="65dp"
|
||||
android:height="64dp"
|
||||
android:viewportWidth="65"
|
||||
android:viewportHeight="64">
|
||||
<path
|
||||
android:pathData="M0.5,32C0.5,14.327 14.827,0 32.5,0C50.173,0 64.5,14.327 64.5,32C64.5,49.673 50.173,64 32.5,64C14.827,64 0.5,49.673 0.5,32Z"
|
||||
android:fillColor="#772917"/>
|
||||
<path
|
||||
android:pathData="M32.5,28V33.334M32.5,38.667H32.513M30.653,21.189L19.687,40.131C19.079,41.182 18.774,41.707 18.819,42.139C18.858,42.515 19.056,42.856 19.361,43.079C19.712,43.334 20.319,43.334 21.533,43.334H43.466C44.68,43.334 45.287,43.334 45.638,43.079C45.944,42.856 46.14,42.515 46.18,42.139C46.225,41.707 45.921,41.182 45.312,40.131L34.346,21.189C33.74,20.142 33.437,19.619 33.041,19.443C32.696,19.29 32.303,19.29 31.958,19.443C31.562,19.619 31.259,20.142 30.653,21.189Z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2.66667"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#F7B27A"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="65dp"
|
||||
android:height="64dp"
|
||||
android:viewportWidth="65"
|
||||
android:viewportHeight="64">
|
||||
<path
|
||||
android:pathData="M0.5,32C0.5,14.327 14.827,0 32.5,0C50.173,0 64.5,14.327 64.5,32C64.5,49.673 50.173,64 32.5,64C14.827,64 0.5,49.673 0.5,32Z"
|
||||
android:fillColor="#EBE9FE"/>
|
||||
<path
|
||||
android:pathData="M21.833,37.656C20.225,36.58 19.167,34.747 19.167,32.667C19.167,29.542 21.555,26.975 24.606,26.692C25.23,22.896 28.527,20 32.5,20C36.473,20 39.769,22.896 40.394,26.692C43.444,26.975 45.833,29.542 45.833,32.667C45.833,34.747 44.775,36.58 43.167,37.656M27.167,38.667L32.5,44M32.5,44L37.833,38.667M32.5,44V32"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2.66667"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#5925DC"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
|
@ -0,0 +1,16 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="65dp"
|
||||
android:height="64dp"
|
||||
android:viewportWidth="65"
|
||||
android:viewportHeight="64">
|
||||
<path
|
||||
android:pathData="M0.5,32C0.5,14.327 14.827,0 32.5,0C50.173,0 64.5,14.327 64.5,32C64.5,49.673 50.173,64 32.5,64C14.827,64 0.5,49.673 0.5,32Z"
|
||||
android:fillColor="#FDEAD7"/>
|
||||
<path
|
||||
android:pathData="M32.5,28V33.334M32.5,38.667H32.513M30.653,21.189L19.687,40.131C19.079,41.182 18.774,41.707 18.819,42.139C18.858,42.515 19.056,42.856 19.361,43.079C19.712,43.334 20.319,43.334 21.533,43.334H43.466C44.68,43.334 45.287,43.334 45.638,43.079C45.944,42.856 46.14,42.515 46.18,42.139C46.225,41.707 45.921,41.182 45.312,40.131L34.346,21.189C33.74,20.142 33.437,19.619 33.041,19.443C32.696,19.29 32.303,19.29 31.958,19.443C31.562,19.619 31.259,20.142 30.653,21.189Z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2.66667"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#B93815"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
|
@ -1,17 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="update_header">Actualización disponible</string>
|
||||
<string name="update_critical_header">Actualización requerida</string>
|
||||
<string name="update_title_available"><xliff:g id="app_name" example="Zashi">%1$s</xliff:g> aquí.</string>
|
||||
<string name="update_title_available">Zashi aquí.</string>
|
||||
<string name="update_title_required">No eres tú, soy yo.</string>
|
||||
<string name="update_description_required">
|
||||
Hay una actualización requerida para <xliff:g id="app_name" example="Zashi">%1$s</xliff:g> que realiza importantes mejoras en el rendimiento y/o la seguridad.
|
||||
Hay una actualización requerida para Zashi que realiza importantes mejoras en el rendimiento y/o la seguridad.
|
||||
</string>
|
||||
<string name="update_description_available">
|
||||
Hay una nueva versión de <xliff:g id="app_name" example="Zashi">%1$s</xliff:g> que realiza actualizaciones menores para mejorar el rendimiento y/o la seguridad.\n\nPor favor, toma un momento para actualizar a la última versión.
|
||||
Hay una nueva versión de Zashi que realiza actualizaciones menores para mejorar el rendimiento y/o la seguridad.\n\nPor favor, toma un momento para actualizar a la última versión.
|
||||
</string>
|
||||
<string name="update_link_text">Obtén más información sobre esta actualización aquí.</string>
|
||||
<string name="update_download_button">Actualizar</string>
|
||||
<string name="update_later_enabled_button">Recordarme más tarde</string>
|
||||
<string name="update_later_disabled_button">(requerido)</string>
|
||||
</resources>
|
||||
|
|
|
@ -1,19 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="update_header">Update available</string>
|
||||
<string name="update_critical_header">Update required</string>
|
||||
<string name="update_title_available"><xliff:g id="app_name" example="Zashi">%1$s</xliff:g> here.</string>
|
||||
<string name="update_title_required">It\'s not you, it\'s me.</string>
|
||||
<string name="update_title_available">Zashi here.</string>
|
||||
<string name="update_title_required">It\'s not you, it\'s us.</string>
|
||||
<string name="update_description_required">
|
||||
There is a required update for <xliff:g id="app_name" example="Zashi">%1$s</xliff:g> that makes major
|
||||
improvements to performance and/or security.
|
||||
There is a required update\nfor Zashi that makes major improvements to\nperformance and/or security.
|
||||
</string>
|
||||
<string name="update_description_available">
|
||||
There is a new version of <xliff:g id="app_name" example="Zashi">%1$s</xliff:g> that makes minor updates to
|
||||
There is a new version of Zashi that makes minor updates to
|
||||
improve performance and/or security.\n\nPlease take a moment to update to the latest version.
|
||||
</string>
|
||||
<string name="update_link_text">Learn more about this update here.</string>
|
||||
<string name="update_download_button">Update</string>
|
||||
<string name="update_later_enabled_button">Remind me later</string>
|
||||
<string name="update_later_disabled_button">(required)</string>
|
||||
</resources>
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="65dp"
|
||||
android:height="64dp"
|
||||
android:viewportWidth="65"
|
||||
android:viewportHeight="64">
|
||||
<path
|
||||
android:pathData="M0.5,32C0.5,14.327 14.827,0 32.5,0C50.173,0 64.5,14.327 64.5,32C64.5,49.673 50.173,64 32.5,64C14.827,64 0.5,49.673 0.5,32Z"
|
||||
android:fillColor="#7A271A"/>
|
||||
<path
|
||||
android:pathData="M32.5,26.667V32M32.5,37.334H32.513M45.833,32C45.833,39.364 39.864,45.334 32.5,45.334C25.136,45.334 19.167,39.364 19.167,32C19.167,24.636 25.136,18.667 32.5,18.667C39.864,18.667 45.833,24.636 45.833,32Z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2.66667"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FDA29B"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
|
@ -0,0 +1,16 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="65dp"
|
||||
android:height="64dp"
|
||||
android:viewportWidth="65"
|
||||
android:viewportHeight="64">
|
||||
<path
|
||||
android:pathData="M0.5,32C0.5,14.327 14.827,0 32.5,0C50.173,0 64.5,14.327 64.5,32C64.5,49.673 50.173,64 32.5,64C14.827,64 0.5,49.673 0.5,32Z"
|
||||
android:fillColor="#FEE4E2"/>
|
||||
<path
|
||||
android:pathData="M32.5,26.667V32M32.5,37.334H32.513M45.833,32C45.833,39.364 39.864,45.334 32.5,45.334C25.136,45.334 19.167,39.364 19.167,32C19.167,24.636 25.136,18.667 32.5,18.667C39.864,18.667 45.833,24.636 45.833,32Z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2.66667"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#B42318"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
|
@ -1,15 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="not_enough_space_title">No hay suficiente espacio libre</string>
|
||||
<string name="not_enough_space_description_1">
|
||||
<xliff:g id="app_name" example="Zashi">%1$s</xliff:g> requiere al menos
|
||||
<xliff:g example="1" id="required_gigabytes">%2$d</xliff:g> GB de espacio para funcionar, pero solo hay
|
||||
<xliff:g example="300" id="available_megabytes">%3$d</xliff:g> MB disponibles.
|
||||
</string>
|
||||
<string name="not_enough_space_description_2">
|
||||
Ve a la configuración de tu dispositivo y libera más espacio si deseas usar la aplicación
|
||||
<xliff:g id="app_name" example="Zashi">%1$s</xliff:g>.
|
||||
</string>
|
||||
<string name="not_enough_space_description_1">Zashi requires <xliff:g example="1" id="required_gigabytes">%1$d</xliff:g> GB of space to synchronize the Zcash blockchain but there is only</string>
|
||||
<string name="not_enough_space_description_2"><xliff:g example="300" id="available_megabytes">%1$d</xliff:g> MB available</string>
|
||||
<string name="not_enough_space_description_3">. Syncing will stay paused until more space is available.\n\n~<xliff:g example="300" id="required_megabytes">%1$d</xliff:g> MB of additional space required to continue</string>
|
||||
<string name="not_enough_space_system_settings_btn">Configuración del sistema</string>
|
||||
<string name="not_enough_space_settings_open_failed">No se pudo iniciar la aplicación de Configuración.</string>
|
||||
</resources>
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="not_enough_space_title">Not enough free space</string>
|
||||
<string name="not_enough_space_description_1">
|
||||
<xliff:g id="app_name" example="Zashi">%1$s</xliff:g> requires at least
|
||||
<xliff:g example="1" id="required_gigabytes">%2$d</xliff:g> GB of space to operate but there is only
|
||||
<xliff:g example="300" id="available_megabytes">%3$d</xliff:g> MB available.
|
||||
</string>
|
||||
<string name="not_enough_space_description_2">
|
||||
Go to your device settings and make more space available if you wish to use the
|
||||
<xliff:g id="app_name" example="Zashi">%1$s</xliff:g> app.
|
||||
</string>
|
||||
<string name="not_enough_space_description_1">Zashi requires <xliff:g example="1" id="required_gigabytes">%1$d</xliff:g> GB of space to synchronize the Zcash blockchain but there is only</string>
|
||||
<string name="not_enough_space_description_2"><xliff:g example="300" id="available_megabytes">%1$d</xliff:g> MB available</string>
|
||||
<string name="not_enough_space_description_3">. Syncing will stay paused until more space is available.\n\n~<xliff:g example="300" id="required_megabytes">%1$d</xliff:g> MB of additional space required to continue</string>
|
||||
<string name="not_enough_space_system_settings_btn">System settings</string>
|
||||
<string name="not_enough_space_settings_open_failed">Unable to launch Settings app.</string>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue