diff --git a/app/src/main/java/be/re/writand/screens/WViewModel.kt b/app/src/main/java/be/re/writand/screens/WViewModel.kt new file mode 100644 index 0000000..1f4e4de --- /dev/null +++ b/app/src/main/java/be/re/writand/screens/WViewModel.kt @@ -0,0 +1,41 @@ +package be.re.writand.screens + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import be.re.writand.utils.SnackbarManager +import be.re.writand.utils.SnackbarMessage.Companion.toSnackbarMessage +import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +/** + * Custom viewmodel class for this app. + */ +class WViewModel : ViewModel() { + + /** + * Function to launch a suspended function in viewmodel scope. + * This function catches possible errors and displays them as a snackbar message. + * @param[snackbar] controls if a snackbar must be shown or not. + * @param[finally] a lambda-function to execute after the [block] is executed. + * @param[block] the code that must be executed in a Coroutine-context. + */ + fun launchCatching( + snackbar: Boolean = true, + finally: () -> Unit = {}, + block: suspend CoroutineScope.() -> Unit + ) = + viewModelScope.launch( + CoroutineExceptionHandler { _, throwable -> + finally() + if (snackbar) { + SnackbarManager.showMessage(throwable.toSnackbarMessage()) + } + }, + block = { + block() + finally() + } + ) + +} \ No newline at end of file diff --git a/app/src/main/java/be/re/writand/utils/SnackbarManager.kt b/app/src/main/java/be/re/writand/utils/SnackbarManager.kt new file mode 100644 index 0000000..6869d5e --- /dev/null +++ b/app/src/main/java/be/re/writand/utils/SnackbarManager.kt @@ -0,0 +1,43 @@ +package be.re.writand.utils + +import be.re.writand.utils.SnackbarMessage.Companion.toSnackbarMessage +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +/** + * Represents a message to be shown inside a snackbar. + */ +sealed class SnackbarMessage { + class StringSnackbar(val message: String) : SnackbarMessage() + + companion object { + + fun Throwable.toSnackbarMessage(): String { + return this.message.orEmpty() + } + + fun SnackbarMessage.toMessage(): String { + return when (this) { + is StringSnackbar -> this.message + } + } + } +} + +/** + * The actual manager of the snackbar's messages. + */ +object SnackbarManager { + private val messages: MutableStateFlow = MutableStateFlow(null) + val snackbarMessages: StateFlow + get() = messages.asStateFlow() + + fun showMessage(message: String) { + messages.value = SnackbarMessage.StringSnackbar(message) + } + + fun showMessage(message: Throwable) { + messages.value = SnackbarMessage.StringSnackbar(message.toSnackbarMessage()) + } +}