git.iko.soy/parseable-android
on 0da331f help sign in
84 lines 2.8 kB history raw
1package com.parseable.android.ui
3import androidx.compose.foundation.layout.Box
4import androidx.compose.foundation.layout.fillMaxSize
5import androidx.compose.foundation.layout.padding
6import androidx.compose.material3.Scaffold
7import androidx.compose.material3.Snackbar
8import androidx.compose.material3.SnackbarDuration
9import androidx.compose.material3.SnackbarHost
10import androidx.compose.material3.SnackbarHostState
11import androidx.compose.runtime.Composable
12import androidx.compose.runtime.LaunchedEffect
13import androidx.compose.runtime.compositionLocalOf
14import androidx.compose.runtime.remember
15import androidx.compose.ui.Modifier
16import kotlinx.coroutines.channels.Channel
17import kotlinx.coroutines.flow.receiveAsFlow
19/**
20 * Central error handler that any screen can use to display error messages
21 * via a global Snackbar. Access it through [LocalErrorHandler].
22 *
23 * Uses a [Channel] so that rapid-fire calls to [showError] queue messages
24 * rather than overwriting each other (which happened with StateFlow).
25 */
26class ErrorHandler {
27 private val _errors = Channel<String>(Channel.BUFFERED)
28 val errors = _errors.receiveAsFlow()
30 fun showError(message: String) {
31 _errors.trySend(message)
32 }
35/**
36 * CompositionLocal providing access to the global [ErrorHandler].
37 * Any composable in the tree can call `LocalErrorHandler.current.showError(...)`.
38 */
39val LocalErrorHandler = compositionLocalOf<ErrorHandler> {
40 error("No ErrorHandler provided. Wrap your content in GlobalErrorBoundary.")
43/**
44 * Wraps [content] in a Scaffold with a SnackbarHost that reacts to errors
45 * posted via the provided [ErrorHandler]. The handler is exposed to the
46 * entire subtree through [LocalErrorHandler].
47 */
48@Composable
49fun GlobalErrorBoundary(
50 errorHandler: ErrorHandler = remember { ErrorHandler() },
51 content: @Composable () -> Unit,
52) {
53 val snackbarHostState = remember { SnackbarHostState() }
55 // Collect errors and show them as Snackbars
56 LaunchedEffect(errorHandler) {
57 errorHandler.errors.collect { message ->
58 snackbarHostState.showSnackbar(
59 message = message,
60 duration = SnackbarDuration.Short,
61 )
62 }
63 }
65 androidx.compose.runtime.CompositionLocalProvider(
66 LocalErrorHandler provides errorHandler,
67 ) {
68 Scaffold(
69 snackbarHost = {
70 SnackbarHost(hostState = snackbarHostState) { data ->
71 Snackbar(snackbarData = data)
72 }
73 },
74 ) { innerPadding ->
75 Box(
76 modifier = Modifier
77 .fillMaxSize()
78 .padding(innerPadding),
79 ) {
80 content()
81 }
82 }
83 }