diff --git a/app/src/main/java/io/element/android/x/node/RootFlowNode.kt b/app/src/main/java/io/element/android/x/node/RootFlowNode.kt index 376f88cdb2..aea7eb1a68 100644 --- a/app/src/main/java/io/element/android/x/node/RootFlowNode.kt +++ b/app/src/main/java/io/element/android/x/node/RootFlowNode.kt @@ -8,7 +8,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -27,14 +26,10 @@ import com.bumble.appyx.core.node.ParentNode import com.bumble.appyx.core.node.node import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.newRoot -import com.bumble.appyx.navmodel.backstack.operation.replace import io.element.android.x.BuildConfig import io.element.android.x.component.ShowkaseButton import io.element.android.x.core.di.DaggerComponentOwner import io.element.android.x.di.SessionComponentsOwner -import io.element.android.x.features.rageshake.bugreport.BugReportScreen -import io.element.android.x.features.rageshake.crash.ui.CrashDetectionScreen -import io.element.android.x.features.rageshake.detection.RageshakeDetectionScreen import io.element.android.x.getBrowserIntent import io.element.android.x.matrix.Matrix import io.element.android.x.matrix.core.SessionId diff --git a/features/rageshake/build.gradle.kts b/features/rageshake/build.gradle.kts index 653e0e55ef..2cd99b6dea 100644 --- a/features/rageshake/build.gradle.kts +++ b/features/rageshake/build.gradle.kts @@ -20,6 +20,7 @@ plugins { id("io.element.android-compose-library") alias(libs.plugins.ksp) alias(libs.plugins.anvil) + id("kotlin-parcelize") } android { diff --git a/features/rageshake/src/main/java/io/element/android/x/features/rageshake/bugreport/BugReportEvents.kt b/features/rageshake/src/main/java/io/element/android/x/features/rageshake/bugreport/BugReportEvents.kt new file mode 100644 index 0000000000..05d150d12c --- /dev/null +++ b/features/rageshake/src/main/java/io/element/android/x/features/rageshake/bugreport/BugReportEvents.kt @@ -0,0 +1,11 @@ +package io.element.android.x.features.rageshake.bugreport + +sealed interface BugReportEvents { + object SendBugReport : BugReportEvents + object ResetAll: BugReportEvents + data class SetDescription(val description: String): BugReportEvents + data class SetSendLog(val sendLog: Boolean): BugReportEvents + data class SetSendCrashLog(val sendCrashlog: Boolean): BugReportEvents + data class SetCanContact(val canContact: Boolean): BugReportEvents + data class SetSendScreenshot(val sendScreenshot: Boolean) : BugReportEvents +} diff --git a/features/rageshake/src/main/java/io/element/android/x/features/rageshake/bugreport/BugReportPresenter.kt b/features/rageshake/src/main/java/io/element/android/x/features/rageshake/bugreport/BugReportPresenter.kt new file mode 100644 index 0000000000..a4cbf027d8 --- /dev/null +++ b/features/rageshake/src/main/java/io/element/android/x/features/rageshake/bugreport/BugReportPresenter.kt @@ -0,0 +1,127 @@ +package io.element.android.x.features.rageshake.bugreport + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import io.element.android.x.architecture.Async +import io.element.android.x.architecture.Presenter +import io.element.android.x.features.rageshake.crash.CrashDataStore +import io.element.android.x.features.rageshake.logs.VectorFileLogger +import io.element.android.x.features.rageshake.reporter.BugReporter +import io.element.android.x.features.rageshake.reporter.ReportType +import io.element.android.x.features.rageshake.screenshot.ScreenshotHolder +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.launch +import javax.inject.Inject + +class BugReportPresenter @Inject constructor( + private val bugReporter: BugReporter, + private val crashDataStore: CrashDataStore, + private val screenshotHolder: ScreenshotHolder, + private val appCoroutineScope: CoroutineScope, +) : Presenter { + + private class BugReporterUploadListener( + private val sendingProgress: MutableState, + private val sendingAction: MutableState> + ) : BugReporter.IMXBugReportListener { + override fun onUploadCancelled() { + sendingProgress.value = 0f + sendingAction.value = Async.Uninitialized + } + + override fun onUploadFailed(reason: String?) { + sendingProgress.value = 0f + sendingAction.value = Async.Failure(Exception(reason)) + } + + override fun onProgress(progress: Int) { + sendingProgress.value = progress.toFloat() / 100 + sendingAction.value = Async.Loading() + } + + override fun onUploadSucceed(reportUrl: String?) { + sendingProgress.value = 0f + sendingAction.value = Async.Success(Unit) + } + } + + @Composable + override fun present(events: Flow): BugReportState { + val crashInfo: String by crashDataStore + .crashInfo() + .collectAsState(initial = "") + + val sendingProgress = remember { + mutableStateOf(0f) + } + val sendingAction: MutableState> = remember { + mutableStateOf(Async.Uninitialized) + } + val formState: MutableState = rememberSaveable { + mutableStateOf(BugReportFormState.Default) + } + val uploadListener = BugReporterUploadListener(sendingProgress, sendingAction) + val state = BugReportState( + hasCrashLogs = crashInfo.isNotEmpty(), + sendingProgress = sendingProgress.value, + sending = sendingAction.value + ) + LaunchedEffect(Unit) { + events.collect { event -> + when (event) { + BugReportEvents.SendBugReport -> appCoroutineScope.sendBugReport(state, uploadListener) + BugReportEvents.ResetAll -> appCoroutineScope.resetAll() + is BugReportEvents.SetDescription -> updateFormState(formState) { + copy(description = event.description) + } + is BugReportEvents.SetCanContact -> updateFormState(formState) { + copy(canContact = event.canContact) + } + is BugReportEvents.SetSendCrashLog -> updateFormState(formState) { + copy(sendCrashLogs = event.sendCrashlog) + } + is BugReportEvents.SetSendLog -> updateFormState(formState) { + copy(sendLogs = event.sendLog) + } + is BugReportEvents.SetSendScreenshot -> updateFormState(formState) { + copy(sendScreenshot = event.sendScreenshot) + } + } + } + } + return state + } + + private fun updateFormState(formState: MutableState, operation: BugReportFormState.() -> BugReportFormState) { + formState.value = operation(formState.value) + } + + private fun CoroutineScope.sendBugReport(state: BugReportState, listener: BugReporter.IMXBugReportListener) = launch { + bugReporter.sendBugReport( + coroutineScope = this, + reportType = ReportType.BUG_REPORT, + withDevicesLogs = state.formState.sendLogs, + withCrashLogs = state.hasCrashLogs && state.formState.sendCrashLogs, + withKeyRequestHistory = false, + withScreenshot = state.formState.sendScreenshot, + theBugDescription = state.formState.description, + serverVersion = "", + canContact = state.formState.canContact, + customFields = emptyMap(), + listener = listener + ) + } + + private fun CoroutineScope.resetAll() = launch { + screenshotHolder.reset() + crashDataStore.reset() + VectorFileLogger.getFromTimber().reset() + } +} diff --git a/features/rageshake/src/main/java/io/element/android/x/features/rageshake/bugreport/BugReportViewState.kt b/features/rageshake/src/main/java/io/element/android/x/features/rageshake/bugreport/BugReportState.kt similarity index 60% rename from features/rageshake/src/main/java/io/element/android/x/features/rageshake/bugreport/BugReportViewState.kt rename to features/rageshake/src/main/java/io/element/android/x/features/rageshake/bugreport/BugReportState.kt index 813a25dec4..e32f0c3abc 100644 --- a/features/rageshake/src/main/java/io/element/android/x/features/rageshake/bugreport/BugReportViewState.kt +++ b/features/rageshake/src/main/java/io/element/android/x/features/rageshake/bugreport/BugReportState.kt @@ -16,30 +16,37 @@ package io.element.android.x.features.rageshake.bugreport -import com.airbnb.mvrx.Async -import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MavericksState -import com.airbnb.mvrx.Uninitialized +import android.os.Parcelable +import io.element.android.x.architecture.Async +import kotlinx.parcelize.Parcelize -data class BugReportViewState( +data class BugReportState( val formState: BugReportFormState = BugReportFormState.Default, - val sendLogs: Boolean = true, val hasCrashLogs: Boolean = false, - val sendCrashLogs: Boolean = true, - val canContact: Boolean = false, - val sendScreenshot: Boolean = false, val screenshotUri: String? = null, val sendingProgress: Float = 0F, - val sending: Async = Uninitialized, -) : MavericksState { + val sending: Async = Async.Uninitialized, +) { val submitEnabled = - formState.description.length > 10 && sending !is Loading + formState.description.length > 10 && sending !is Async.Loading } +@Parcelize data class BugReportFormState( val description: String, -) { + val sendLogs: Boolean, + val sendCrashLogs: Boolean, + val canContact: Boolean, + val sendScreenshot: Boolean + +): Parcelable { companion object { - val Default = BugReportFormState("") + val Default = BugReportFormState( + description = "", + sendLogs = true, + sendCrashLogs = true, + canContact = false, + sendScreenshot = false + ) } } diff --git a/features/rageshake/src/main/java/io/element/android/x/features/rageshake/bugreport/BugReportScreen.kt b/features/rageshake/src/main/java/io/element/android/x/features/rageshake/bugreport/BugReportView.kt similarity index 81% rename from features/rageshake/src/main/java/io/element/android/x/features/rageshake/bugreport/BugReportScreen.kt rename to features/rageshake/src/main/java/io/element/android/x/features/rageshake/bugreport/BugReportView.kt index cbd9ae8be4..0b5ab39365 100644 --- a/features/rageshake/src/main/java/io/element/android/x/features/rageshake/bugreport/BugReportScreen.kt +++ b/features/rageshake/src/main/java/io/element/android/x/features/rageshake/bugreport/BugReportView.kt @@ -37,6 +37,7 @@ import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -50,48 +51,17 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import coil.compose.AsyncImage import coil.request.ImageRequest -import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.Success -import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.compose.collectAsState -import com.airbnb.mvrx.compose.mavericksViewModel +import io.element.android.x.architecture.Async import io.element.android.x.core.compose.LogCompositions +import io.element.android.x.core.compose.textFieldState import io.element.android.x.designsystem.ElementXTheme import io.element.android.x.designsystem.components.LabelledCheckbox import io.element.android.x.designsystem.components.dialogs.ErrorDialog import io.element.android.x.element.resources.R as ElementR @Composable -fun BugReportScreen( - viewModel: BugReportViewModel = mavericksViewModel(), - onDone: () -> Unit = { }, -) { - val state: BugReportViewState by viewModel.collectAsState() - val formState: BugReportFormState by viewModel.formState - LogCompositions(tag = "Rageshake", msg = "Root") - if (state.sending is Success) { - onDone() - } - BugReportContent( - state = state, - formState = formState, - onDescriptionChanged = viewModel::onSetDescription, - onSetSendLog = viewModel::onSetSendLog, - onSetSendCrashLog = viewModel::onSetSendCrashLog, - onSetCanContact = viewModel::onSetCanContact, - onSetSendScreenshot = viewModel::onSetSendScreenshot, - onSubmit = viewModel::onSubmit, - onFailureDialogClosed = viewModel::onFailureDialogClosed, - onDone = onDone, - ) -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun BugReportContent( - state: BugReportViewState, - formState: BugReportFormState, +fun BugReportView( + state: BugReportState, modifier: Modifier = Modifier, onDescriptionChanged: (String) -> Unit = {}, onSetSendLog: (Boolean) -> Unit = {}, @@ -102,6 +72,10 @@ fun BugReportContent( onFailureDialogClosed: () -> Unit = { }, onDone: () -> Unit = { }, ) { + LogCompositions(tag = "Rageshake", msg = "Root") + if (state.sending is Async.Success) { + onDone() + } Surface( modifier = modifier, color = MaterialTheme.colorScheme.background, @@ -120,8 +94,8 @@ fun BugReportContent( ) .padding(horizontal = 16.dp), ) { - val isError = state.sending is Fail - val isFormEnabled = state.sending !is Loading + val isError = state.sending is Async.Failure + val isFormEnabled = state.sending !is Async.Loading // Title Text( text = stringResource(id = ElementR.string.send_bug_report), @@ -140,11 +114,12 @@ fun BugReportContent( .padding(horizontal = 16.dp, vertical = 16.dp), fontSize = 16.sp, ) + var descriptionFieldState by textFieldState(stateValue = state.formState.description) Column( // modifier = Modifier.weight(1f), ) { OutlinedTextField( - value = formState.description, + value = descriptionFieldState, modifier = Modifier .fillMaxWidth() .padding(top = 16.dp), @@ -155,7 +130,10 @@ fun BugReportContent( supportingText = { Text(text = stringResource(id = ElementR.string.send_bug_report_description_in_english)) }, - onValueChange = onDescriptionChanged, + onValueChange = { + descriptionFieldState = it + onDescriptionChanged(it) + }, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Text, imeAction = ImeAction.Next @@ -164,33 +142,33 @@ fun BugReportContent( ) } LabelledCheckbox( - checked = state.sendLogs, + checked = state.formState.sendLogs, onCheckedChange = onSetSendLog, enabled = isFormEnabled, text = stringResource(id = ElementR.string.send_bug_report_include_logs) ) if (state.hasCrashLogs) { LabelledCheckbox( - checked = state.sendCrashLogs, + checked = state.formState.sendCrashLogs, onCheckedChange = onSetSendCrashLog, enabled = isFormEnabled, text = stringResource(id = ElementR.string.send_bug_report_include_crash_logs) ) } LabelledCheckbox( - checked = state.canContact, + checked = state.formState.canContact, onCheckedChange = onSetCanContact, enabled = isFormEnabled, text = stringResource(id = ElementR.string.you_may_contact_me) ) if (state.screenshotUri != null) { LabelledCheckbox( - checked = state.sendScreenshot, + checked = state.formState.sendScreenshot, onCheckedChange = onSetSendScreenshot, enabled = isFormEnabled, text = stringResource(id = ElementR.string.send_bug_report_include_screenshot) ) - if (state.sendScreenshot) { + if (state.formState.sendScreenshot) { Box( modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center @@ -219,18 +197,18 @@ fun BugReportContent( } } when (state.sending) { - Uninitialized -> Unit - is Loading -> { + Async.Uninitialized -> Unit + is Async.Loading -> { CircularProgressIndicator( progress = state.sendingProgress, modifier = Modifier.align(Alignment.Center) ) } - is Fail -> ErrorDialog( + is Async.Failure -> ErrorDialog( content = state.sending.error.toString(), onDismiss = onFailureDialogClosed, ) - is Success -> onDone() + is Async.Success -> onDone() } } } @@ -240,9 +218,8 @@ fun BugReportContent( @Preview fun BugReportContentPreview() { ElementXTheme(darkTheme = false) { - BugReportContent( - state = BugReportViewState(), - formState = BugReportFormState.Default + BugReportView( + state = BugReportState(), ) } } diff --git a/features/rageshake/src/main/java/io/element/android/x/features/rageshake/bugreport/BugReportViewModel.kt b/features/rageshake/src/main/java/io/element/android/x/features/rageshake/bugreport/BugReportViewModel.kt deleted file mode 100644 index 588dcffaa9..0000000000 --- a/features/rageshake/src/main/java/io/element/android/x/features/rageshake/bugreport/BugReportViewModel.kt +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (c) 2022 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.element.android.x.features.rageshake.bugreport - -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.snapshotFlow -import androidx.core.net.toUri -import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MavericksViewModel -import com.airbnb.mvrx.MavericksViewModelFactory -import com.airbnb.mvrx.Success -import com.airbnb.mvrx.Uninitialized -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject -import io.element.android.x.anvilannotations.ContributesViewModel -import io.element.android.x.architecture.viewmodel.daggerMavericksViewModelFactory -import io.element.android.x.di.AppScope -import io.element.android.x.features.rageshake.crash.CrashDataStore -import io.element.android.x.features.rageshake.logs.VectorFileLogger -import io.element.android.x.features.rageshake.reporter.BugReporter -import io.element.android.x.features.rageshake.reporter.ReportType -import io.element.android.x.features.rageshake.screenshot.ScreenshotHolder -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch - -@ContributesViewModel(AppScope::class) -class BugReportViewModel @AssistedInject constructor( - @Assisted initialState: BugReportViewState, - private val bugReporter: BugReporter, - private val crashDataStore: CrashDataStore, - private val screenshotHolder: ScreenshotHolder, - private val appCoroutineScope: CoroutineScope -) : - MavericksViewModel(initialState) { - - companion object : - MavericksViewModelFactory by daggerMavericksViewModelFactory() - - var formState = mutableStateOf(BugReportFormState.Default) - private set - - init { - snapshotFlow { formState.value } - .onEach { - setState { copy(formState = it) } - }.launchIn(viewModelScope) - observerCrashDataStore() - setState { - copy( - screenshotUri = screenshotHolder.getFile()?.toUri()?.toString() - ) - } - } - - private fun observerCrashDataStore() { - viewModelScope.launch { - crashDataStore.crashInfo().collect { - setState { - copy( - hasCrashLogs = it.isNotEmpty() - ) - } - } - } - } - - private val listener: BugReporter.IMXBugReportListener = object : BugReporter.IMXBugReportListener { - override fun onUploadCancelled() { - setState { - copy( - sendingProgress = 0F, - sending = Uninitialized - ) - } - } - - override fun onUploadFailed(reason: String?) { - setState { - copy( - sendingProgress = 0F, - sending = Fail(Exception(reason)) - ) - } - } - - override fun onProgress(progress: Int) { - setState { - copy( - sendingProgress = progress.toFloat() / 100, - sending = Loading() - ) - } - } - - override fun onUploadSucceed(reportUrl: String?) { - setState { - copy( - sendingProgress = 1F, - sending = Success(Unit) - ) - } - } - } - - override fun onCleared() { - // Use appCoroutineScope because we don't want this coroutine to be cancelled - appCoroutineScope.launch(Dispatchers.IO) { - screenshotHolder.reset() - crashDataStore.reset() - VectorFileLogger.getFromTimber().reset() - } - super.onCleared() - } - - fun onSubmit() { - setState { - copy( - sendingProgress = 0F, - sending = Loading() - ) - } - withState { state -> - bugReporter.sendBugReport( - coroutineScope = viewModelScope, - reportType = ReportType.BUG_REPORT, - withDevicesLogs = state.sendLogs, - withCrashLogs = state.hasCrashLogs && state.sendCrashLogs, - withKeyRequestHistory = false, - withScreenshot = state.sendScreenshot, - theBugDescription = state.formState.description, - serverVersion = "", - canContact = state.canContact, - customFields = emptyMap(), - listener = listener - ) - } - } - - fun onFailureDialogClosed() { - setState { - copy( - sendingProgress = 0F, - sending = Uninitialized - ) - } - } - - fun onSetDescription(str: String) { - formState.value = formState.value.copy(description = str) - setState { copy(sending = Uninitialized) } - } - - fun onSetSendLog(value: Boolean) = setState { copy(sendLogs = value) } - fun onSetSendCrashLog(value: Boolean) = setState { copy(sendCrashLogs = value) } - fun onSetCanContact(value: Boolean) = setState { copy(canContact = value) } - fun onSetSendScreenshot(value: Boolean) = setState { copy(sendScreenshot = value) } -} diff --git a/features/rageshake/src/main/java/io/element/android/x/features/rageshake/crash/ui/CrashDetectionEvents.kt b/features/rageshake/src/main/java/io/element/android/x/features/rageshake/crash/ui/CrashDetectionEvents.kt new file mode 100644 index 0000000000..e57a6de9bc --- /dev/null +++ b/features/rageshake/src/main/java/io/element/android/x/features/rageshake/crash/ui/CrashDetectionEvents.kt @@ -0,0 +1,6 @@ +package io.element.android.x.features.rageshake.crash.ui + +sealed interface CrashDetectionEvents { + object ResetAll : CrashDetectionEvents + object ResetAppHasCrashed : CrashDetectionEvents +} diff --git a/features/rageshake/src/main/java/io/element/android/x/features/rageshake/crash/ui/CrashDetectionPresenter.kt b/features/rageshake/src/main/java/io/element/android/x/features/rageshake/crash/ui/CrashDetectionPresenter.kt new file mode 100644 index 0000000000..ae22cec08c --- /dev/null +++ b/features/rageshake/src/main/java/io/element/android/x/features/rageshake/crash/ui/CrashDetectionPresenter.kt @@ -0,0 +1,38 @@ +package io.element.android.x.features.rageshake.crash.ui + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import io.element.android.x.architecture.Presenter +import io.element.android.x.features.rageshake.crash.CrashDataStore +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.launch +import javax.inject.Inject + +class CrashDetectionPresenter @Inject constructor(private val crashDataStore: CrashDataStore) : Presenter { + + @Composable + override fun present(events: Flow): CrashDetectionState { + val crashDetected = crashDataStore.appHasCrashed().collectAsState(initial = false) + LaunchedEffect(Unit) { + events.collect { event -> + when (event) { + CrashDetectionEvents.ResetAll -> resetAll() + CrashDetectionEvents.ResetAppHasCrashed -> resetAppHasCrashed() + } + } + } + return CrashDetectionState( + crashDetected = crashDetected.value + ) + } + + private fun CoroutineScope.resetAppHasCrashed() = launch { + crashDataStore.resetAppHasCrashed() + } + + fun CoroutineScope.resetAll() = launch { + crashDataStore.reset() + } +} diff --git a/features/rageshake/src/main/java/io/element/android/x/features/rageshake/crash/ui/CrashDetectionScreen.kt b/features/rageshake/src/main/java/io/element/android/x/features/rageshake/crash/ui/CrashDetectionScreen.kt index 7c5cc36acb..e7da8ba4ab 100644 --- a/features/rageshake/src/main/java/io/element/android/x/features/rageshake/crash/ui/CrashDetectionScreen.kt +++ b/features/rageshake/src/main/java/io/element/android/x/features/rageshake/crash/ui/CrashDetectionScreen.kt @@ -17,40 +17,33 @@ package io.element.android.x.features.rageshake.crash.ui import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import com.airbnb.mvrx.compose.collectAsState -import com.airbnb.mvrx.compose.mavericksViewModel import io.element.android.x.core.compose.LogCompositions import io.element.android.x.designsystem.ElementXTheme import io.element.android.x.designsystem.components.dialogs.ConfirmationDialog import io.element.android.x.element.resources.R as ElementR @Composable -fun CrashDetectionScreen( - viewModel: CrashDetectionViewModel = mavericksViewModel(), +fun CrashDetectionView( + state: CrashDetectionState, onOpenBugReport: () -> Unit = { }, + onPopupDismissed: () -> Unit = {} ) { - val state: CrashDetectionViewState by viewModel.collectAsState() LogCompositions(tag = "Crash", msg = "CrashDetectionScreen") - if (state.crashDetected) { CrashDetectionContent( state, - onYesClicked = { - viewModel.onYes() - onOpenBugReport() - }, - onNoClicked = viewModel::onPopupDismissed, - onDismiss = viewModel::onPopupDismissed, + onYesClicked = onOpenBugReport, + onNoClicked = onPopupDismissed, + onDismiss = onPopupDismissed, ) } } @Composable fun CrashDetectionContent( - state: CrashDetectionViewState, + state: CrashDetectionState, onNoClicked: () -> Unit = { }, onYesClicked: () -> Unit = { }, onDismiss: () -> Unit = { }, @@ -71,7 +64,7 @@ fun CrashDetectionContent( fun CrashDetectionContentPreview() { ElementXTheme { CrashDetectionContent( - state = CrashDetectionViewState() + state = CrashDetectionState() ) } } diff --git a/features/rageshake/src/main/java/io/element/android/x/features/rageshake/crash/ui/CrashDetectionViewState.kt b/features/rageshake/src/main/java/io/element/android/x/features/rageshake/crash/ui/CrashDetectionState.kt similarity index 88% rename from features/rageshake/src/main/java/io/element/android/x/features/rageshake/crash/ui/CrashDetectionViewState.kt rename to features/rageshake/src/main/java/io/element/android/x/features/rageshake/crash/ui/CrashDetectionState.kt index e7a50645f9..1ce7142735 100644 --- a/features/rageshake/src/main/java/io/element/android/x/features/rageshake/crash/ui/CrashDetectionViewState.kt +++ b/features/rageshake/src/main/java/io/element/android/x/features/rageshake/crash/ui/CrashDetectionState.kt @@ -16,8 +16,6 @@ package io.element.android.x.features.rageshake.crash.ui -import com.airbnb.mvrx.MavericksState - -data class CrashDetectionViewState( +data class CrashDetectionState( val crashDetected: Boolean = false, -) : MavericksState +) diff --git a/features/rageshake/src/main/java/io/element/android/x/features/rageshake/crash/ui/CrashDetectionViewModel.kt b/features/rageshake/src/main/java/io/element/android/x/features/rageshake/crash/ui/CrashDetectionViewModel.kt deleted file mode 100644 index cd387bfa1f..0000000000 --- a/features/rageshake/src/main/java/io/element/android/x/features/rageshake/crash/ui/CrashDetectionViewModel.kt +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2022 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.element.android.x.features.rageshake.crash.ui - -import com.airbnb.mvrx.MavericksViewModel -import com.airbnb.mvrx.MavericksViewModelFactory -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject -import io.element.android.x.anvilannotations.ContributesViewModel -import io.element.android.x.architecture.viewmodel.daggerMavericksViewModelFactory -import io.element.android.x.di.AppScope -import io.element.android.x.features.rageshake.crash.CrashDataStore -import kotlinx.coroutines.launch - -@ContributesViewModel(AppScope::class) -class CrashDetectionViewModel @AssistedInject constructor( - @Assisted initialState: CrashDetectionViewState, - private val crashDataStore: CrashDataStore, -) : MavericksViewModel(initialState) { - - companion object : - MavericksViewModelFactory by daggerMavericksViewModelFactory() - - init { - observeDataStore() - } - - private fun observeDataStore() { - viewModelScope.launch { - crashDataStore.appHasCrashed().collect { appHasCrashed -> - setState { - copy( - crashDetected = appHasCrashed - ) - } - } - } - } - - fun onYes() { - viewModelScope.launch { - crashDataStore.resetAppHasCrashed() - } - } - - fun onPopupDismissed() { - viewModelScope.launch { - crashDataStore.reset() - } - } -}