From 3a8c4c11ce8e9f017f156fb009862f4b41defbd3 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Sun, 9 Oct 2022 15:09:56 +0100 Subject: [PATCH 1/6] adding dedicated error for missing room keys cipher --- .../app/dapk/st/settings/SettingsScreen.kt | 1 + .../dapk/st/matrix/crypto/CryptoService.kt | 1 + .../matrix/crypto/internal/RoomKeyImporter.kt | 24 ++++++++++++------- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/features/settings/src/main/kotlin/app/dapk/st/settings/SettingsScreen.kt b/features/settings/src/main/kotlin/app/dapk/st/settings/SettingsScreen.kt index 570c632..fba9c23 100644 --- a/features/settings/src/main/kotlin/app/dapk/st/settings/SettingsScreen.kt +++ b/features/settings/src/main/kotlin/app/dapk/st/settings/SettingsScreen.kt @@ -156,6 +156,7 @@ internal fun SettingsScreen(viewModel: SettingsViewModel, onSignOut: () -> Unit, ImportResult.Error.Type.UnexpectedDecryptionOutput -> "Unable to decrypt file, double check your passphrase" is ImportResult.Error.Type.Unknown -> "${type.cause::class.java.simpleName}: ${type.cause.message}" ImportResult.Error.Type.UnableToOpenFile -> "Unable to open file" + ImportResult.Error.Type.InvalidFile -> "Unable to process file" } Text(text = "Import failed\n$message", textAlign = TextAlign.Center) diff --git a/matrix/services/crypto/src/main/kotlin/app/dapk/st/matrix/crypto/CryptoService.kt b/matrix/services/crypto/src/main/kotlin/app/dapk/st/matrix/crypto/CryptoService.kt index ba5936b..d4ac8d8 100644 --- a/matrix/services/crypto/src/main/kotlin/app/dapk/st/matrix/crypto/CryptoService.kt +++ b/matrix/services/crypto/src/main/kotlin/app/dapk/st/matrix/crypto/CryptoService.kt @@ -184,6 +184,7 @@ sealed interface ImportResult { object NoKeysFound : Type object UnexpectedDecryptionOutput : Type object UnableToOpenFile : Type + object InvalidFile : Type } } diff --git a/matrix/services/crypto/src/main/kotlin/app/dapk/st/matrix/crypto/internal/RoomKeyImporter.kt b/matrix/services/crypto/src/main/kotlin/app/dapk/st/matrix/crypto/internal/RoomKeyImporter.kt index 24311a9..7adcc3c 100644 --- a/matrix/services/crypto/src/main/kotlin/app/dapk/st/matrix/crypto/internal/RoomKeyImporter.kt +++ b/matrix/services/crypto/src/main/kotlin/app/dapk/st/matrix/crypto/internal/RoomKeyImporter.kt @@ -86,21 +86,27 @@ class RoomKeyImporter( val line = it.joinToString(separator = "").replace("\n", "") val toByteArray = base64.decode(line) if (index == 0) { - decryptCipher.initialize(toByteArray, password) - toByteArray - .copyOfRange(37, toByteArray.size) - .decrypt(decryptCipher) - .also { - if (!it.startsWith("[{")) { - throw ImportException(ImportResult.Error.Type.UnexpectedDecryptionOutput) - } + toByteArray.ensureHasCipherPayloadOrThrow() + val initializer = toByteArray.copyOfRange(0, 37) + decryptCipher.initialize(initializer, password) + val content = toByteArray.copyOfRange(37, toByteArray.size) + content.decrypt(decryptCipher).also { + if (!it.startsWith("[{")) { + throw ImportException(ImportResult.Error.Type.UnexpectedDecryptionOutput) } + } } else { toByteArray.decrypt(decryptCipher) } } } + private fun ByteArray.ensureHasCipherPayloadOrThrow() { + if (this.size < 37) { + throw ImportException(ImportResult.Error.Type.InvalidFile) + } + } + private fun Cipher.initialize(payload: ByteArray, passphrase: String) { val salt = payload.copyOfRange(1, 1 + 16) val iv = payload.copyOfRange(17, 17 + 16) @@ -176,6 +182,7 @@ private class JsonAccumulator { jsonSegment = withLatest null } + else -> { val string = withLatest.substring(objectRange) importJson.decodeFromString(ElementMegolmExportObject.serializer(), string).also { @@ -200,6 +207,7 @@ private class JsonAccumulator { } opens++ } + c == '}' -> { opens-- if (opens == 0) { From 2e91ae5b670831de76e229f3319e2d6c2adefb1f Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Sun, 9 Oct 2022 15:29:38 +0100 Subject: [PATCH 2/6] add more options to generic error component --- .../app/dapk/st/design/components/Error.kt | 39 +++++++++++++++---- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/design-library/src/main/kotlin/app/dapk/st/design/components/Error.kt b/design-library/src/main/kotlin/app/dapk/st/design/components/Error.kt index 722ff2c..20db56c 100644 --- a/design-library/src/main/kotlin/app/dapk/st/design/components/Error.kt +++ b/design-library/src/main/kotlin/app/dapk/st/design/components/Error.kt @@ -1,21 +1,44 @@ package app.dapk.st.design.components -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp @Composable -fun GenericError(retryAction: () -> Unit) { - Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { +fun GenericError(message: String = "Something went wrong...", label: String = "Retry", moreDetails: String? = null, action: () -> Unit) { + val openDetailsDialog = remember { mutableStateOf(false) } + if (openDetailsDialog.value) { + AlertDialog( + onDismissRequest = { openDetailsDialog.value = false }, + confirmButton = { + Button(onClick = { openDetailsDialog.value = false }) { + Text("OK") + } + }, + title = { Text("Details") }, + text = { + Text(moreDetails!!) + } + ) + } + Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) { Column(horizontalAlignment = Alignment.CenterHorizontally) { - Text("Something went wrong...") - Button(onClick = { retryAction() }) { - Text("Retry") + Text(message) + if (moreDetails != null) { + Text("Tap for more details".uppercase(), fontSize = 12.sp, modifier = Modifier.clickable { openDetailsDialog.value = true }.padding(12.dp)) + } + Spacer(modifier = Modifier.height(12.dp)) + Button(onClick = { action() }) { + Text(label.uppercase()) } } } From 9ec9797d17f56c82a7dfcb073997d63bcd30e0cb Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Sun, 9 Oct 2022 15:30:00 +0100 Subject: [PATCH 3/6] providing more details to the import errors via dialog --- .../st/core/extensions/GlobalExtensions.kt | 7 +++++ .../app/dapk/st/settings/SettingsScreen.kt | 30 +++++++++---------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/core/src/main/kotlin/app/dapk/st/core/extensions/GlobalExtensions.kt b/core/src/main/kotlin/app/dapk/st/core/extensions/GlobalExtensions.kt index 1c947a7..fee8de7 100644 --- a/core/src/main/kotlin/app/dapk/st/core/extensions/GlobalExtensions.kt +++ b/core/src/main/kotlin/app/dapk/st/core/extensions/GlobalExtensions.kt @@ -3,6 +3,13 @@ package app.dapk.st.core.extensions inline fun T?.ifNull(block: () -> T): T = this ?: block() inline fun ifOrNull(condition: Boolean, block: () -> T): T? = if (condition) block() else null +inline fun Any.takeAs(): T? { + return when (this) { + is T -> this + else -> null + } +} + @Suppress("UNCHECKED_CAST") inline fun Iterable.firstOrNull(predicate: (T) -> Boolean, predicate2: (T) -> Boolean): Pair? { var firstValue: T1? = null diff --git a/features/settings/src/main/kotlin/app/dapk/st/settings/SettingsScreen.kt b/features/settings/src/main/kotlin/app/dapk/st/settings/SettingsScreen.kt index fba9c23..402654b 100644 --- a/features/settings/src/main/kotlin/app/dapk/st/settings/SettingsScreen.kt +++ b/features/settings/src/main/kotlin/app/dapk/st/settings/SettingsScreen.kt @@ -40,6 +40,7 @@ import app.dapk.st.core.Lce import app.dapk.st.core.StartObserving import app.dapk.st.core.components.CenteredLoading import app.dapk.st.core.components.Header +import app.dapk.st.core.extensions.takeAs import app.dapk.st.core.getActivity import app.dapk.st.design.components.* import app.dapk.st.matrix.crypto.ImportResult @@ -149,22 +150,21 @@ internal fun SettingsScreen(viewModel: SettingsViewModel, onSignOut: () -> Unit, } is ImportResult.Error -> { - Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { - Column(horizontalAlignment = Alignment.CenterHorizontally) { - val message = when (val type = result.cause) { - ImportResult.Error.Type.NoKeysFound -> "No keys found in the file" - ImportResult.Error.Type.UnexpectedDecryptionOutput -> "Unable to decrypt file, double check your passphrase" - is ImportResult.Error.Type.Unknown -> "${type.cause::class.java.simpleName}: ${type.cause.message}" - ImportResult.Error.Type.UnableToOpenFile -> "Unable to open file" - ImportResult.Error.Type.InvalidFile -> "Unable to process file" - } - - Text(text = "Import failed\n$message", textAlign = TextAlign.Center) - Spacer(modifier = Modifier.height(12.dp)) - Button(onClick = { navigator.navigate.upToHome() }) { - Text(text = "Close".uppercase()) - } + val message = when (result.cause) { + ImportResult.Error.Type.NoKeysFound -> "No keys found in the file" + ImportResult.Error.Type.UnexpectedDecryptionOutput -> "Unable to decrypt file, double check your passphrase" + is ImportResult.Error.Type.Unknown -> "Unknown error" + ImportResult.Error.Type.UnableToOpenFile -> "Unable to open file" + ImportResult.Error.Type.InvalidFile -> "Unable to process file" + } + GenericError( + message = message, + label = "Close", + moreDetails = result.cause.takeAs()?.let { + "${it.cause::class.java.simpleName}: ${it.cause.message}" } + ) { + navigator.navigate.upToHome() } } From 7e0d4d60139f7391bb1799fe57c81eda75d901af Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Sun, 9 Oct 2022 15:40:07 +0100 Subject: [PATCH 4/6] add error handling to the image gallery --- .../kotlin/app/dapk/st/design/components/Error.kt | 4 +++- .../st/messenger/gallery/ImageGalleryScreen.kt | 14 ++++++-------- .../st/messenger/gallery/ImageGalleryViewModel.kt | 4 ++-- .../kotlin/app/dapk/st/settings/SettingsScreen.kt | 5 +---- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/design-library/src/main/kotlin/app/dapk/st/design/components/Error.kt b/design-library/src/main/kotlin/app/dapk/st/design/components/Error.kt index 20db56c..ef06b5f 100644 --- a/design-library/src/main/kotlin/app/dapk/st/design/components/Error.kt +++ b/design-library/src/main/kotlin/app/dapk/st/design/components/Error.kt @@ -14,7 +14,9 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @Composable -fun GenericError(message: String = "Something went wrong...", label: String = "Retry", moreDetails: String? = null, action: () -> Unit) { +fun GenericError(message: String = "Something went wrong...", label: String = "Retry", cause: Throwable? = null, action: () -> Unit) { + val moreDetails = cause?.let { "${it::class.java.simpleName}: ${it.message}" } + val openDetailsDialog = remember { mutableStateOf(false) } if (openDetailsDialog.value) { AlertDialog( diff --git a/features/messenger/src/main/kotlin/app/dapk/st/messenger/gallery/ImageGalleryScreen.kt b/features/messenger/src/main/kotlin/app/dapk/st/messenger/gallery/ImageGalleryScreen.kt index acb46c1..8770857 100644 --- a/features/messenger/src/main/kotlin/app/dapk/st/messenger/gallery/ImageGalleryScreen.kt +++ b/features/messenger/src/main/kotlin/app/dapk/st/messenger/gallery/ImageGalleryScreen.kt @@ -42,12 +42,10 @@ fun ImageGalleryScreen(viewModel: ImageGalleryViewModel, onTopLevelBack: () -> U Spider(currentPage = viewModel.state.page, onNavigate = onNavigate) { item(ImageGalleryPage.Routes.folders) { - ImageGalleryFolders(it) { folder -> - viewModel.selectFolder(folder) - } + ImageGalleryFolders(it, onClick = { viewModel.selectFolder(it) }, onRetry = { viewModel.start() }) } item(ImageGalleryPage.Routes.files) { - ImageGalleryMedia(it, onImageSelected) + ImageGalleryMedia(it, onImageSelected, onRetry = { viewModel.selectFolder(it.folder) }) } } @@ -55,7 +53,7 @@ fun ImageGalleryScreen(viewModel: ImageGalleryViewModel, onTopLevelBack: () -> U @Composable -fun ImageGalleryFolders(state: ImageGalleryPage.Folders, onClick: (Folder) -> Unit) { +fun ImageGalleryFolders(state: ImageGalleryPage.Folders, onClick: (Folder) -> Unit, onRetry: () -> Unit) { val screenWidth = LocalConfiguration.current.screenWidthDp val gradient = Brush.verticalGradient( @@ -106,12 +104,12 @@ fun ImageGalleryFolders(state: ImageGalleryPage.Folders, onClick: (Folder) -> Un } } - is Lce.Error -> GenericError { } + is Lce.Error -> GenericError(cause = content.cause, action = onRetry) } } @Composable -fun ImageGalleryMedia(state: ImageGalleryPage.Files, onFileSelected: (Media) -> Unit) { +fun ImageGalleryMedia(state: ImageGalleryPage.Files, onFileSelected: (Media) -> Unit, onRetry: () -> Unit) { val screenWidth = LocalConfiguration.current.screenWidthDp Column { @@ -149,7 +147,7 @@ fun ImageGalleryMedia(state: ImageGalleryPage.Files, onFileSelected: (Media) -> } } - is Lce.Error -> GenericError { } + is Lce.Error -> GenericError(cause = content.cause, action = onRetry) } } diff --git a/features/messenger/src/main/kotlin/app/dapk/st/messenger/gallery/ImageGalleryViewModel.kt b/features/messenger/src/main/kotlin/app/dapk/st/messenger/gallery/ImageGalleryViewModel.kt index 59cbbb4..0620315 100644 --- a/features/messenger/src/main/kotlin/app/dapk/st/messenger/gallery/ImageGalleryViewModel.kt +++ b/features/messenger/src/main/kotlin/app/dapk/st/messenger/gallery/ImageGalleryViewModel.kt @@ -48,7 +48,7 @@ class ImageGalleryViewModel( route = ImageGalleryPage.Routes.files, label = page.label, parent = ImageGalleryPage.Routes.folders, - state = ImageGalleryPage.Files(Lce.Loading()) + state = ImageGalleryPage.Files(Lce.Loading(), folder) ) ) } @@ -78,7 +78,7 @@ data class ImageGalleryState( sealed interface ImageGalleryPage { data class Folders(val content: Lce>) : ImageGalleryPage - data class Files(val content: Lce>) : ImageGalleryPage + data class Files(val content: Lce>, val folder: Folder) : ImageGalleryPage object Routes { val folders = Route("Folders") diff --git a/features/settings/src/main/kotlin/app/dapk/st/settings/SettingsScreen.kt b/features/settings/src/main/kotlin/app/dapk/st/settings/SettingsScreen.kt index 402654b..4ffb0a9 100644 --- a/features/settings/src/main/kotlin/app/dapk/st/settings/SettingsScreen.kt +++ b/features/settings/src/main/kotlin/app/dapk/st/settings/SettingsScreen.kt @@ -31,7 +31,6 @@ import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.VisualTransformation -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -160,9 +159,7 @@ internal fun SettingsScreen(viewModel: SettingsViewModel, onSignOut: () -> Unit, GenericError( message = message, label = "Close", - moreDetails = result.cause.takeAs()?.let { - "${it.cause::class.java.simpleName}: ${it.cause.message}" - } + cause = result.cause.takeAs()?.cause ) { navigator.navigate.upToHome() } From ec5c58a95c8ad06f4cd751d931d410df9b20caf0 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Sun, 9 Oct 2022 15:53:26 +0100 Subject: [PATCH 5/6] adding error views to profile, messenger and settings screens --- .../app/dapk/st/design/components/Spider.kt | 25 +++++++++++++------ .../app/dapk/st/messenger/MessengerScreen.kt | 20 +++------------ .../messenger/gallery/ImageGalleryActivity.kt | 6 ++++- .../messenger/gallery/ImageGalleryScreen.kt | 1 - .../app/dapk/st/profile/ProfileScreen.kt | 4 +-- .../app/dapk/st/settings/SettingsScreen.kt | 12 ++++----- .../st/settings/eventlogger/EventLogScreen.kt | 16 +++++++----- 7 files changed, 43 insertions(+), 41 deletions(-) diff --git a/design-library/src/main/kotlin/app/dapk/st/design/components/Spider.kt b/design-library/src/main/kotlin/app/dapk/st/design/components/Spider.kt index 826694b..55b8a12 100644 --- a/design-library/src/main/kotlin/app/dapk/st/design/components/Spider.kt +++ b/design-library/src/main/kotlin/app/dapk/st/design/components/Spider.kt @@ -10,22 +10,27 @@ fun Spider(currentPage: SpiderPage, onNavigate: (SpiderPage? val pageCache = remember { mutableMapOf, SpiderPage>() } pageCache[currentPage.route] = currentPage + val navigateAndPopStack = { + pageCache.remove(currentPage.route) + onNavigate(pageCache[currentPage.parent]) + } + val itemScope = object : SpiderItemScope { + override fun goBack() { + navigateAndPopStack() + } + } + val computedWeb = remember(true) { mutableMapOf, @Composable (T) -> Unit>().also { computedWeb -> val scope = object : SpiderScope { - override fun item(route: Route, content: @Composable (T) -> Unit) { - computedWeb[route] = { content(it as T) } + override fun item(route: Route, content: @Composable SpiderItemScope.(T) -> Unit) { + computedWeb[route] = { content(itemScope, it as T) } } } graph.invoke(scope) } } - val navigateAndPopStack = { - pageCache.remove(currentPage.route) - onNavigate(pageCache[currentPage.parent]) - } - Column { if (currentPage.hasToolbar) { Toolbar( @@ -40,7 +45,11 @@ fun Spider(currentPage: SpiderPage, onNavigate: (SpiderPage? interface SpiderScope { - fun item(route: Route, content: @Composable (T) -> Unit) + fun item(route: Route, content: @Composable SpiderItemScope.(T) -> Unit) +} + +interface SpiderItemScope { + fun goBack() } data class SpiderPage( diff --git a/features/messenger/src/main/kotlin/app/dapk/st/messenger/MessengerScreen.kt b/features/messenger/src/main/kotlin/app/dapk/st/messenger/MessengerScreen.kt index d130dd3..5d1447e 100644 --- a/features/messenger/src/main/kotlin/app/dapk/st/messenger/MessengerScreen.kt +++ b/features/messenger/src/main/kotlin/app/dapk/st/messenger/MessengerScreen.kt @@ -43,10 +43,7 @@ import app.dapk.st.core.LifecycleEffect import app.dapk.st.core.StartObserving import app.dapk.st.core.components.CenteredLoading import app.dapk.st.core.extensions.takeIfContent -import app.dapk.st.design.components.MessengerUrlIcon -import app.dapk.st.design.components.MissingAvatarIcon -import app.dapk.st.design.components.SmallTalkTheme -import app.dapk.st.design.components.Toolbar +import app.dapk.st.design.components.* import app.dapk.st.matrix.common.RoomId import app.dapk.st.matrix.common.UserId import app.dapk.st.matrix.sync.MessageMeta @@ -95,7 +92,7 @@ internal fun MessengerScreen( }) when (state.composerState) { is ComposerState.Text -> { - Room(state.roomState, replyActions) + Room(state.roomState, replyActions, onRetry = { viewModel.post(MessengerAction.OnMessengerVisible(roomId, attachments)) }) TextComposer( state.composerState, onTextChange = { viewModel.post(MessengerAction.ComposerTextUpdate(it)) }, @@ -132,7 +129,7 @@ private fun MessengerViewModel.ObserveEvents(galleryLauncher: ActivityResultLaun } @Composable -private fun ColumnScope.Room(roomStateLce: Lce, replyActions: ReplyActions) { +private fun ColumnScope.Room(roomStateLce: Lce, replyActions: ReplyActions, onRetry: () -> Unit) { when (val state = roomStateLce) { is Lce.Loading -> CenteredLoading() is Lce.Content -> { @@ -165,16 +162,7 @@ private fun ColumnScope.Room(roomStateLce: Lce, replyActions: Re } } - is Lce.Error -> { - Box(contentAlignment = Alignment.Center) { - Column(horizontalAlignment = Alignment.CenterHorizontally) { - Text("Something went wrong...") - Button(onClick = {}) { - Text("Retry") - } - } - } - } + is Lce.Error -> GenericError(cause = state.cause, action = onRetry) } } diff --git a/features/messenger/src/main/kotlin/app/dapk/st/messenger/gallery/ImageGalleryActivity.kt b/features/messenger/src/main/kotlin/app/dapk/st/messenger/gallery/ImageGalleryActivity.kt index 06faf13..7ccd939 100644 --- a/features/messenger/src/main/kotlin/app/dapk/st/messenger/gallery/ImageGalleryActivity.kt +++ b/features/messenger/src/main/kotlin/app/dapk/st/messenger/gallery/ImageGalleryActivity.kt @@ -15,6 +15,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.lifecycleScope import app.dapk.st.core.* import app.dapk.st.core.extensions.unsafeLazy +import app.dapk.st.design.components.GenericError import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize @@ -59,7 +60,10 @@ fun Activity.PermissionGuard(state: State>, onGranted: @Co PermissionResult.ShowRational -> finish() } - is Lce.Error -> finish() + is Lce.Error -> GenericError(message = "Store permission required", label = "Close") { + finish() + } + is Lce.Loading -> { // loading should be quick, let's avoid displaying anything } diff --git a/features/messenger/src/main/kotlin/app/dapk/st/messenger/gallery/ImageGalleryScreen.kt b/features/messenger/src/main/kotlin/app/dapk/st/messenger/gallery/ImageGalleryScreen.kt index 8770857..daae82d 100644 --- a/features/messenger/src/main/kotlin/app/dapk/st/messenger/gallery/ImageGalleryScreen.kt +++ b/features/messenger/src/main/kotlin/app/dapk/st/messenger/gallery/ImageGalleryScreen.kt @@ -51,7 +51,6 @@ fun ImageGalleryScreen(viewModel: ImageGalleryViewModel, onTopLevelBack: () -> U } - @Composable fun ImageGalleryFolders(state: ImageGalleryPage.Folders, onClick: (Folder) -> Unit, onRetry: () -> Unit) { val screenWidth = LocalConfiguration.current.screenWidthDp diff --git a/features/profile/src/main/kotlin/app/dapk/st/profile/ProfileScreen.kt b/features/profile/src/main/kotlin/app/dapk/st/profile/ProfileScreen.kt index 6d57470..889ecf4 100644 --- a/features/profile/src/main/kotlin/app/dapk/st/profile/ProfileScreen.kt +++ b/features/profile/src/main/kotlin/app/dapk/st/profile/ProfileScreen.kt @@ -119,7 +119,7 @@ private fun ProfilePage(context: Context, viewModel: ProfileViewModel, profile: } @Composable -private fun Invitations(viewModel: ProfileViewModel, invitations: Page.Invitations) { +private fun SpiderItemScope.Invitations(viewModel: ProfileViewModel, invitations: Page.Invitations) { when (val state = invitations.content) { is Lce.Loading -> CenteredLoading() is Lce.Content -> { @@ -147,7 +147,7 @@ private fun Invitations(viewModel: ProfileViewModel, invitations: Page.Invitatio } } - is Lce.Error -> TODO() + is Lce.Error -> GenericError(label = "Go back", cause = state.cause) { goBack() } } } diff --git a/features/settings/src/main/kotlin/app/dapk/st/settings/SettingsScreen.kt b/features/settings/src/main/kotlin/app/dapk/st/settings/SettingsScreen.kt index 4ffb0a9..edf9bc3 100644 --- a/features/settings/src/main/kotlin/app/dapk/st/settings/SettingsScreen.kt +++ b/features/settings/src/main/kotlin/app/dapk/st/settings/SettingsScreen.kt @@ -63,7 +63,7 @@ internal fun SettingsScreen(viewModel: SettingsViewModel, onSignOut: () -> Unit, } Spider(currentPage = viewModel.state.page, onNavigate = onNavigate) { item(Page.Routes.root) { - RootSettings(it) { viewModel.onClick(it) } + RootSettings(it, onClick = { viewModel.onClick(it) }, onRetry = { viewModel.start() }) } item(Page.Routes.encryption) { Encryption(viewModel, it) @@ -180,7 +180,7 @@ internal fun SettingsScreen(viewModel: SettingsViewModel, onSignOut: () -> Unit, } @Composable -private fun RootSettings(page: Page.Root, onClick: (SettingItem) -> Unit) { +private fun RootSettings(page: Page.Root, onClick: (SettingItem) -> Unit, onRetry: () -> Unit) { when (val content = page.content) { is Lce.Content -> { LazyColumn( @@ -226,12 +226,10 @@ private fun RootSettings(page: Page.Root, onClick: (SettingItem) -> Unit) { } } - is Lce.Error -> { - // TODO - } + is Lce.Error -> GenericError(cause = content.cause, action = onRetry) is Lce.Loading -> { - // TODO + // Should be quick enough to avoid needing a loading state } } } @@ -264,7 +262,7 @@ private fun PushProviders(viewModel: SettingsViewModel, state: Page.PushProvider } } - is Lce.Error -> TODO() + is Lce.Error -> GenericError(cause = lce.cause) { viewModel.fetchPushProviders() } } } diff --git a/features/settings/src/main/kotlin/app/dapk/st/settings/eventlogger/EventLogScreen.kt b/features/settings/src/main/kotlin/app/dapk/st/settings/eventlogger/EventLogScreen.kt index 8a7d76e..50cbfee 100644 --- a/features/settings/src/main/kotlin/app/dapk/st/settings/eventlogger/EventLogScreen.kt +++ b/features/settings/src/main/kotlin/app/dapk/st/settings/eventlogger/EventLogScreen.kt @@ -14,6 +14,8 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import app.dapk.st.core.AppLogTag import app.dapk.st.core.Lce +import app.dapk.st.core.components.CenteredLoading +import app.dapk.st.design.components.GenericError import app.dapk.st.matrix.common.MatrixLogTag private val filterItems = listOf(null) + (MatrixLogTag.values().map { it.key } + AppLogTag.values().map { it.key }).distinct() @@ -33,11 +35,13 @@ fun EventLogScreen(viewModel: EventLoggerViewModel) { viewModel.selectLog(it, filter = null) } } + else -> { Events( selectedPageContent = state.selectedState, onExit = { viewModel.exitLog() }, - onSelectTag = { viewModel.selectLog(state.selectedState.selectedPage, it) } + onSelectTag = { viewModel.selectLog(state.selectedState.selectedPage, it) }, + onRetry = { viewModel.start() }, ) } } @@ -46,6 +50,7 @@ fun EventLogScreen(viewModel: EventLoggerViewModel) { is Lce.Error -> { // TODO } + is Lce.Loading -> { // TODO } @@ -69,7 +74,7 @@ private fun LogKeysList(keys: List, onSelected: (String) -> Unit) { } @Composable -private fun Events(selectedPageContent: SelectedState, onExit: () -> Unit, onSelectTag: (String?) -> Unit) { +private fun Events(selectedPageContent: SelectedState, onExit: () -> Unit, onSelectTag: (String?) -> Unit, onRetry: () -> Unit) { BackHandler(onBack = onExit) when (val content = selectedPageContent.content) { is Lce.Content -> { @@ -112,9 +117,8 @@ private fun Events(selectedPageContent: SelectedState, onExit: () -> Unit, onSel } } } - is Lce.Error -> TODO() - is Lce.Loading -> { - // TODO - } + + is Lce.Error -> GenericError(cause = content.cause, action = onRetry) + is Lce.Loading -> CenteredLoading() } } \ No newline at end of file From 94731d006e2c8c3c0831b71c3149066c9f4ce19b Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Sun, 9 Oct 2022 15:55:10 +0100 Subject: [PATCH 6/6] reusing error component in login screen --- features/login/build.gradle | 1 + .../kotlin/app/dapk/st/login/LoginScreen.kt | 41 ++----------------- 2 files changed, 5 insertions(+), 37 deletions(-) diff --git a/features/login/build.gradle b/features/login/build.gradle index 3a3af95..21dcd44 100644 --- a/features/login/build.gradle +++ b/features/login/build.gradle @@ -7,5 +7,6 @@ dependencies { implementation project(":matrix:services:auth") implementation project(":matrix:services:profile") implementation project(":matrix:services:crypto") + implementation project(":design-library") implementation project(":core") } \ No newline at end of file diff --git a/features/login/src/main/kotlin/app/dapk/st/login/LoginScreen.kt b/features/login/src/main/kotlin/app/dapk/st/login/LoginScreen.kt index 2271c9a..fd56d73 100644 --- a/features/login/src/main/kotlin/app/dapk/st/login/LoginScreen.kt +++ b/features/login/src/main/kotlin/app/dapk/st/login/LoginScreen.kt @@ -1,7 +1,6 @@ package app.dapk.st.login import android.widget.Toast -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions @@ -32,6 +31,8 @@ import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import app.dapk.st.core.StartObserving +import app.dapk.st.core.components.CenteredLoading +import app.dapk.st.design.components.GenericError import app.dapk.st.login.LoginEvent.LoginComplete import app.dapk.st.login.LoginScreenState.* @@ -49,42 +50,8 @@ fun LoginScreen(loginViewModel: LoginViewModel, onLoggedIn: () -> Unit) { val keyboardController = LocalSoftwareKeyboardController.current when (val state = loginViewModel.state) { - is Error -> { - val openDetailsDialog = remember { mutableStateOf(false) } - - if (openDetailsDialog.value) { - AlertDialog( - onDismissRequest = { openDetailsDialog.value = false }, - confirmButton = { - Button(onClick = { openDetailsDialog.value = false }) { - Text("OK") - } - }, - title = { Text("Details") }, - text = { - Text(state.cause.message ?: "Unknown") - } - ) - } - Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) { - Column(horizontalAlignment = Alignment.CenterHorizontally) { - Text("Something went wrong") - Text("Tap for more details".uppercase(), fontSize = 12.sp, modifier = Modifier.clickable { openDetailsDialog.value = true }.padding(12.dp)) - Spacer(modifier = Modifier.height(12.dp)) - Button(onClick = { - loginViewModel.start() - }) { - Text("Retry".uppercase()) - } - } - } - } - - Loading -> { - Box(contentAlignment = Alignment.Center) { - CircularProgressIndicator() - } - } + is Error -> GenericError(cause = state.cause, action = { loginViewModel.start() }) + Loading -> CenteredLoading() is Content -> Row {