From 2b157d0364a0e88f1c7fdce0dc92ddc156a74889 Mon Sep 17 00:00:00 2001 From: ialokim <ialokim@mailbox.org> Date: Sat, 15 Jan 2022 12:58:26 +0100 Subject: [PATCH] add expiration banner and screen --- .../desktop/expiration/ExpirationBanner.kt | 73 +++++++++++++++++++ .../desktop/expiration/ExpirationUtils.kt | 12 +++ .../briar/desktop/login/ErrorScreen.kt | 6 +- .../briar/desktop/login/ErrorSubViewModel.kt | 2 + .../briar/desktop/login/StartupViewModel.kt | 6 +- .../briarproject/briar/desktop/ui/BriarUi.kt | 13 +++- .../briar/desktop/ui/Constants.kt | 1 + .../resources/strings/BriarDesktop.properties | 3 + 8 files changed, 110 insertions(+), 6 deletions(-) create mode 100644 src/main/kotlin/org/briarproject/briar/desktop/expiration/ExpirationBanner.kt create mode 100644 src/main/kotlin/org/briarproject/briar/desktop/expiration/ExpirationUtils.kt diff --git a/src/main/kotlin/org/briarproject/briar/desktop/expiration/ExpirationBanner.kt b/src/main/kotlin/org/briarproject/briar/desktop/expiration/ExpirationBanner.kt new file mode 100644 index 0000000000..9205a0e520 --- /dev/null +++ b/src/main/kotlin/org/briarproject/briar/desktop/expiration/ExpirationBanner.kt @@ -0,0 +1,73 @@ +package org.briarproject.briar.desktop.expiration + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Icon +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Surface +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Warning +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import org.briarproject.briar.desktop.expiration.ExpirationUtils.getDaysLeft +import org.briarproject.briar.desktop.expiration.ExpirationUtils.isExpired +import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n +import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18nP +import org.briarproject.briar.desktop.utils.PreviewUtils.preview +import kotlin.time.Duration.Companion.hours + +fun main() = preview { + Column { + ExpirationBanner {} + } +} + +@Composable +fun ExpirationBanner(onExpired: () -> Unit) { + + var daysLeft by remember { mutableStateOf(0) } + LaunchedEffect(Unit) { + launch { + while (true) { + daysLeft = getDaysLeft() + if (isExpired()) { + onExpired() + break + } + delay(1.hours.inWholeMilliseconds) + } + } + } + + ExpirationBanner(daysLeft) +} + +@Composable +fun ExpirationBanner( + daysLeft: Int, +) = Surface( + color = MaterialTheme.colors.error, + modifier = Modifier.fillMaxWidth() +) { + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.padding(8.dp) + ) { + Icon(Icons.Filled.Warning, i18n("warning")) + Text(i18nP("expiration.banner", daysLeft)) + } +} diff --git a/src/main/kotlin/org/briarproject/briar/desktop/expiration/ExpirationUtils.kt b/src/main/kotlin/org/briarproject/briar/desktop/expiration/ExpirationUtils.kt new file mode 100644 index 0000000000..7a51a741e5 --- /dev/null +++ b/src/main/kotlin/org/briarproject/briar/desktop/expiration/ExpirationUtils.kt @@ -0,0 +1,12 @@ +package org.briarproject.briar.desktop.expiration + +import org.briarproject.briar.desktop.BuildData +import java.time.Instant +import java.time.temporal.ChronoUnit + +object ExpirationUtils { + + fun getDaysLeft() = 90 - ChronoUnit.DAYS.between(Instant.ofEpochMilli(BuildData.GIT_TIME), Instant.now()).toInt() + + fun isExpired() = getDaysLeft() <= 0 +} diff --git a/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorScreen.kt b/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorScreen.kt index dc7e70b570..871f581bf6 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorScreen.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorScreen.kt @@ -56,9 +56,12 @@ import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n import org.briarproject.briar.desktop.utils.PreviewUtils.preview fun main() = preview { - var error: ErrorSubViewModel.Error by remember { mutableStateOf(RegistrationSubViewModel.RegistrationError) } + var error: ErrorSubViewModel.Error by remember { mutableStateOf(ErrorSubViewModel.ExpirationError) } Row(horizontalArrangement = spacedBy(8.dp)) { + Button(onClick = { error = ErrorSubViewModel.ExpirationError }) { + Text("Expiration") + } Button(onClick = { error = RegistrationSubViewModel.RegistrationError }) { Text("Registration") } @@ -100,6 +103,7 @@ fun ErrorScreen( Text(i18n("sorry"), style = MaterialTheme.typography.h5) val text = when (error) { + is ErrorSubViewModel.ExpirationError -> i18n("startup.failed.expired") is RegistrationSubViewModel.RegistrationError -> i18n("startup.failed.registration") is StartupViewModel.StartingError -> { when (error.error) { diff --git a/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorSubViewModel.kt b/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorSubViewModel.kt index 5f0c1dd228..e4129ebdea 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorSubViewModel.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorSubViewModel.kt @@ -24,4 +24,6 @@ class ErrorSubViewModel( val onBackButton: () -> Unit, ) : StartupViewModel.SubViewModel { sealed interface Error + + object ExpirationError : Error } diff --git a/src/main/kotlin/org/briarproject/briar/desktop/login/StartupViewModel.kt b/src/main/kotlin/org/briarproject/briar/desktop/login/StartupViewModel.kt index ecb09900eb..8cd391466f 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/login/StartupViewModel.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/login/StartupViewModel.kt @@ -30,6 +30,7 @@ import org.briarproject.bramble.api.lifecycle.LifecycleManager import org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.ALREADY_RUNNING import org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent +import org.briarproject.briar.desktop.expiration.ExpirationUtils.isExpired import org.briarproject.briar.desktop.threading.BriarExecutors import org.briarproject.briar.desktop.viewmodel.EventListenerDbViewModel import org.briarproject.briar.desktop.viewmodel.asState @@ -60,8 +61,9 @@ constructor( private val _currentSubViewModel = mutableStateOf(decideSubViewModel()) val currentSubViewModel = _currentSubViewModel.asState() - private fun decideSubViewModel() = - if (accountManager.accountExists()) makeLogin() + private fun decideSubViewModel(): SubViewModel = + if (isExpired()) makeError(ErrorSubViewModel.ExpirationError) + else if (accountManager.accountExists()) makeLogin() else makeRegistration() private fun makeLogin() = LoginSubViewModel( diff --git a/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt b/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt index ef1141c379..ffafaaf8c2 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt @@ -18,12 +18,15 @@ package org.briarproject.briar.desktop.ui +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.window.FrameWindowScope import androidx.compose.ui.window.Window @@ -35,6 +38,7 @@ import org.briarproject.bramble.api.lifecycle.LifecycleManager import org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent import org.briarproject.briar.desktop.DesktopFeatureFlags +import org.briarproject.briar.desktop.expiration.ExpirationBanner import org.briarproject.briar.desktop.login.StartupScreen import org.briarproject.briar.desktop.settings.SettingsViewModel import org.briarproject.briar.desktop.theme.BriarTheme @@ -114,9 +118,12 @@ constructor( ) { val settingsViewModel: SettingsViewModel = viewModel() BriarTheme(isDarkTheme = settingsViewModel.isDarkMode.value) { - when (screenState) { - STARTUP -> StartupScreen() - MAIN -> MainScreen(settingsViewModel) + Column(Modifier.fillMaxSize()) { + ExpirationBanner(onExpired = { screenState = STARTUP }) + when (screenState) { + STARTUP -> StartupScreen() + MAIN -> MainScreen(settingsViewModel) + } } } } diff --git a/src/main/kotlin/org/briarproject/briar/desktop/ui/Constants.kt b/src/main/kotlin/org/briarproject/briar/desktop/ui/Constants.kt index d860f80b8f..67c1b749c3 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/ui/Constants.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/ui/Constants.kt @@ -21,6 +21,7 @@ package org.briarproject.briar.desktop.ui import androidx.compose.ui.unit.dp object Constants { + val BANNER_HEIGHT = 24.dp val HEADER_SIZE = 56.dp val COLUMN_WIDTH = 275.dp val STARTUP_FIELDS_WIDTH = 400.dp diff --git a/src/main/resources/strings/BriarDesktop.properties b/src/main/resources/strings/BriarDesktop.properties index 7b65487cb9..d5ae8d74ef 100644 --- a/src/main/resources/strings/BriarDesktop.properties +++ b/src/main/resources/strings/BriarDesktop.properties @@ -121,6 +121,7 @@ next=Next open=Open sorry=Sorry error=Error +warning=Warning unsupported_feature=Unfortunately, this feature is not yet supported by Briar Desktop. # Startup screen @@ -142,6 +143,7 @@ startup.error.decryption.text=Briar cannot check your password. Please try reboo startup.password_forgotten.button=I have forgotten my password startup.password_forgotten.title=Lost Password startup.password_forgotten.text=Your Briar account is stored encrypted on your device, not in the cloud, so we can't reset your password. Would you like to delete your account and start again?\n\nCaution: Your identities, contacts and messages will be permanently lost. +startup.failed.expired=This software has expired. Thank you for testing!\n\nTo continue using Briar, please download the latest release. You will be able to continue using your account. startup.failed.registration=Briar was unable to create your account.\n\nPlease upgrade to the latest version and try again. startup.failed.clock_error=Briar was unable to start because your device's clock is wrong.\n\nPlease set your device's clock to the right time and try again. startup.failed.db_error=Briar was unable to open the database containing your account, your contacts and your messages.\n\nPlease check if Briar is already running on this device. Otherwise, upgrade to the latest version of the app and try again, or set up a new account by choosing 'I have forgotten my password' at the password prompt. @@ -152,6 +154,7 @@ startup.database.creating=Creating Account... startup.database.opening=Decrypting Database... startup.database.migrating=Upgrading Database... startup.database.compacting=Compacting Database... +expiration.banner={0, plural, one {This is a test version of Briar that will expire in {0} day. Please update to a newer version in time.} other {This is a test version of Briar that will expire in {0} days. Please update to a newer version in time.}} # Settings settings.title=Settings -- GitLab