Continue migrating BugReport/Rageshake/Crash screens
This commit is contained in:
parent
c299ab4031
commit
e56ba5e315
21 changed files with 366 additions and 80 deletions
|
|
@ -14,6 +14,7 @@ import kotlinx.parcelize.Parcelize
|
|||
|
||||
class PreferencesFlowNode(
|
||||
buildContext: BuildContext,
|
||||
private val onOpenBugReport: () -> Unit,
|
||||
private val backstack: BackStack<NavTarget> = BackStack(
|
||||
initialElement = NavTarget.Root,
|
||||
savedStateMap = buildContext.savedStateMap,
|
||||
|
|
@ -23,6 +24,12 @@ class PreferencesFlowNode(
|
|||
buildContext = buildContext
|
||||
) {
|
||||
|
||||
private val preferencesRootNodeCallback = object : PreferencesRootNode.Callback {
|
||||
override fun onOpenBugReport() {
|
||||
onOpenBugReport.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface NavTarget : Parcelable {
|
||||
@Parcelize
|
||||
object Root : NavTarget
|
||||
|
|
@ -30,7 +37,7 @@ class PreferencesFlowNode(
|
|||
|
||||
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
||||
return when (navTarget) {
|
||||
NavTarget.Root -> createNode<PreferencesRootNode>(buildContext)
|
||||
NavTarget.Root -> createNode<PreferencesRootNode>(buildContext, plugins = listOf(preferencesRootNodeCallback))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import androidx.compose.ui.Modifier
|
|||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import com.bumble.appyx.core.plugin.Plugin
|
||||
import com.bumble.appyx.core.plugin.plugins
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import io.element.android.x.anvilannotations.ContributesNode
|
||||
|
|
@ -20,6 +21,10 @@ class PreferencesRootNode @AssistedInject constructor(
|
|||
private val presenter: PreferencesRootPresenter,
|
||||
) : Node(buildContext, plugins = plugins) {
|
||||
|
||||
public interface Callback : Plugin {
|
||||
fun onOpenBugReport()
|
||||
}
|
||||
|
||||
private val presenterConnector = presenterConnector(presenter)
|
||||
|
||||
private fun onLogoutClicked() {
|
||||
|
|
@ -34,6 +39,10 @@ class PreferencesRootNode @AssistedInject constructor(
|
|||
presenterConnector.emitEvent(PreferencesRootEvents.SetRageshakeSensitivity(sensitivity))
|
||||
}
|
||||
|
||||
private fun onOpenBugReport() {
|
||||
plugins<Callback>().forEach { it.onOpenBugReport() }
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
val state by presenterConnector.stateFlow.collectAsState()
|
||||
|
|
@ -42,7 +51,8 @@ class PreferencesRootNode @AssistedInject constructor(
|
|||
onLogoutClicked = this::onLogoutClicked,
|
||||
onBackPressed = this::navigateUp,
|
||||
onRageshakeEnabledChanged = this::onRageshakeEnabledChanged,
|
||||
onRageshakeSensitivityChanged = this::onRageshakeSensitivityChanged
|
||||
onRageshakeSensitivityChanged = this::onRageshakeSensitivityChanged,
|
||||
onOpenRageShake = this::onOpenBugReport
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
package io.element.android.x.features.rageshake.bugreport
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import com.bumble.appyx.core.plugin.Plugin
|
||||
import com.bumble.appyx.core.plugin.plugins
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import io.element.android.x.anvilannotations.ContributesNode
|
||||
import io.element.android.x.architecture.presenterConnector
|
||||
import io.element.android.x.di.AppScope
|
||||
|
||||
@ContributesNode(AppScope::class)
|
||||
class BugReportNode @AssistedInject constructor(
|
||||
@Assisted buildContext: BuildContext,
|
||||
@Assisted plugins: List<Plugin>,
|
||||
presenter: BugReportPresenter,
|
||||
) : Node(buildContext, plugins = plugins) {
|
||||
|
||||
private val presenterConnector = presenterConnector(presenter)
|
||||
|
||||
interface Callback : Plugin {
|
||||
fun onBugReportSent()
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
val state by presenterConnector.stateFlow.collectAsState()
|
||||
BugReportView(
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
onDescriptionChanged = this::onDescriptionChanged,
|
||||
onSetSendLog = this::onSetSendLog,
|
||||
onSetSendCrashLog = this::onSetSendCrashLog,
|
||||
onSetCanContact = this::onSetCanContact,
|
||||
onSetSendScreenshot = this::onSetSendScreenshot,
|
||||
onSubmit = this::onSubmit,
|
||||
onDone = this::onDone
|
||||
)
|
||||
}
|
||||
|
||||
private fun onDone() {
|
||||
presenterConnector.emitEvent(BugReportEvents.ResetAll)
|
||||
plugins<Callback>().forEach { it.onBugReportSent() }
|
||||
}
|
||||
|
||||
private fun onSubmit() {
|
||||
presenterConnector.emitEvent(BugReportEvents.SendBugReport)
|
||||
}
|
||||
|
||||
private fun onSetSendLog(sendLog: Boolean) {
|
||||
presenterConnector.emitEvent(BugReportEvents.SetSendLog(sendLog))
|
||||
}
|
||||
|
||||
private fun onSetSendCrashLog(sendCrashLog: Boolean) {
|
||||
presenterConnector.emitEvent(BugReportEvents.SetSendCrashLog(sendCrashLog))
|
||||
}
|
||||
|
||||
private fun onSetSendScreenshot(sendScreenshot: Boolean) {
|
||||
presenterConnector.emitEvent(BugReportEvents.SetSendScreenshot(sendScreenshot))
|
||||
}
|
||||
|
||||
private fun onSetCanContact(canContact: Boolean) {
|
||||
presenterConnector.emitEvent(BugReportEvents.SetCanContact(canContact))
|
||||
}
|
||||
|
||||
private fun onDescriptionChanged(description: String) {
|
||||
presenterConnector.emitEvent(BugReportEvents.SetDescription(description))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4,10 +4,12 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.core.net.toUri
|
||||
import io.element.android.x.architecture.Async
|
||||
import io.element.android.x.architecture.Presenter
|
||||
import io.element.android.x.features.rageshake.crash.CrashDataStore
|
||||
|
|
@ -31,6 +33,7 @@ class BugReportPresenter @Inject constructor(
|
|||
private val sendingProgress: MutableState<Float>,
|
||||
private val sendingAction: MutableState<Async<Unit>>
|
||||
) : BugReporter.IMXBugReportListener {
|
||||
|
||||
override fun onUploadCancelled() {
|
||||
sendingProgress.value = 0f
|
||||
sendingAction.value = Async.Uninitialized
|
||||
|
|
@ -54,6 +57,11 @@ class BugReportPresenter @Inject constructor(
|
|||
|
||||
@Composable
|
||||
override fun present(events: Flow<BugReportEvents>): BugReportState {
|
||||
val screenshotUri = rememberSaveable {
|
||||
mutableStateOf(
|
||||
screenshotHolder.getFile()?.toUri()?.toString()
|
||||
)
|
||||
}
|
||||
val crashInfo: String by crashDataStore
|
||||
.crashInfo()
|
||||
.collectAsState(initial = "")
|
||||
|
|
@ -64,15 +72,21 @@ class BugReportPresenter @Inject constructor(
|
|||
val sendingAction: MutableState<Async<Unit>> = remember {
|
||||
mutableStateOf(Async.Uninitialized)
|
||||
}
|
||||
val formState: MutableState<BugReportFormState> = rememberSaveable {
|
||||
val formState: MutableState<BugReportFormState> = remember {
|
||||
mutableStateOf(BugReportFormState.Default)
|
||||
}
|
||||
val uploadListener = BugReporterUploadListener(sendingProgress, sendingAction)
|
||||
val state = BugReportState(
|
||||
hasCrashLogs = crashInfo.isNotEmpty(),
|
||||
sendingProgress = sendingProgress.value,
|
||||
sending = sendingAction.value
|
||||
)
|
||||
val state by remember {
|
||||
derivedStateOf {
|
||||
BugReportState(
|
||||
hasCrashLogs = crashInfo.isNotEmpty(),
|
||||
sendingProgress = sendingProgress.value,
|
||||
sending = sendingAction.value,
|
||||
formState = formState.value,
|
||||
screenshotUri = screenshotUri.value
|
||||
)
|
||||
}
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
events.collect { event ->
|
||||
when (event) {
|
||||
|
|
|
|||
|
|
@ -38,8 +38,7 @@ data class BugReportFormState(
|
|||
val sendCrashLogs: Boolean,
|
||||
val canContact: Boolean,
|
||||
val sendScreenshot: Boolean
|
||||
|
||||
): Parcelable {
|
||||
) : Parcelable {
|
||||
companion object {
|
||||
val Default = BugReportFormState(
|
||||
description = "",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package io.element.android.x.features.rageshake.crash.ui
|
||||
|
||||
sealed interface CrashDetectionEvents {
|
||||
object ResetAll : CrashDetectionEvents
|
||||
object ResetAllCrashData : CrashDetectionEvents
|
||||
object ResetAppHasCrashed : CrashDetectionEvents
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class CrashDetectionPresenter @Inject constructor(private val crashDataStore: Cr
|
|||
LaunchedEffect(Unit) {
|
||||
events.collect { event ->
|
||||
when (event) {
|
||||
CrashDetectionEvents.ResetAll -> resetAll()
|
||||
CrashDetectionEvents.ResetAllCrashData -> resetAll()
|
||||
CrashDetectionEvents.ResetAppHasCrashed -> resetAppHasCrashed()
|
||||
}
|
||||
}
|
||||
|
|
@ -32,7 +32,7 @@ class CrashDetectionPresenter @Inject constructor(private val crashDataStore: Cr
|
|||
crashDataStore.resetAppHasCrashed()
|
||||
}
|
||||
|
||||
fun CoroutineScope.resetAll() = launch {
|
||||
private fun CoroutineScope.resetAll() = launch {
|
||||
crashDataStore.reset()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import io.element.android.x.core.screenshot.ImageResult
|
|||
import io.element.android.x.features.rageshake.preferences.RageshakePreferencesEvents
|
||||
import io.element.android.x.features.rageshake.preferences.RageshakePreferencesPresenter
|
||||
import io.element.android.x.features.rageshake.rageshake.RageShake
|
||||
import io.element.android.x.features.rageshake.rageshake.RageshakeDataStore
|
||||
import io.element.android.x.features.rageshake.screenshot.ScreenshotHolder
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
|
@ -22,7 +21,6 @@ import timber.log.Timber
|
|||
import javax.inject.Inject
|
||||
|
||||
class RageshakeDetectionPresenter @Inject constructor(
|
||||
private val rageshakeDataStore: RageshakeDataStore,
|
||||
private val screenshotHolder: ScreenshotHolder,
|
||||
private val rageShake: RageShake,
|
||||
private val preferencesPresenter: RageshakePreferencesPresenter,
|
||||
|
|
@ -42,12 +40,14 @@ class RageshakeDetectionPresenter @Inject constructor(
|
|||
val showDialog = rememberSaveable {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
val state = RageshakeDetectionState(
|
||||
isStarted = isStarted.value,
|
||||
takeScreenshot = takeScreenshot.value,
|
||||
showDialog = showDialog.value,
|
||||
preferenceState = preferencesState
|
||||
)
|
||||
val state = remember(preferencesState, isStarted.value, takeScreenshot.value, showDialog.value) {
|
||||
RageshakeDetectionState(
|
||||
isStarted = isStarted.value,
|
||||
takeScreenshot = takeScreenshot.value,
|
||||
showDialog = showDialog.value,
|
||||
preferenceState = preferencesState
|
||||
)
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
events.collect { event ->
|
||||
when (event) {
|
||||
|
|
@ -62,22 +62,18 @@ class RageshakeDetectionPresenter @Inject constructor(
|
|||
LaunchedEffect(preferencesState.sensitivity) {
|
||||
rageShake.setSensitivity(preferencesState.sensitivity)
|
||||
}
|
||||
val shouldStart = remember {
|
||||
derivedStateOf {
|
||||
preferencesState.isEnabled &&
|
||||
val shouldStart = preferencesState.isEnabled &&
|
||||
preferencesState.isSupported &&
|
||||
isStarted.value &&
|
||||
!takeScreenshot.value &&
|
||||
!showDialog.value
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(shouldStart) {
|
||||
handleRageShake(shouldStart.value, state, takeScreenshot)
|
||||
handleRageShake(shouldStart, state, takeScreenshot)
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
|
||||
private fun handleRageShake(start: Boolean, state: RageshakeDetectionState, takeScreenshot: MutableState<Boolean>) {
|
||||
if (start) {
|
||||
rageShake.start(state.preferenceState.sensitivity)
|
||||
|
|
|
|||
|
|
@ -16,8 +16,10 @@
|
|||
|
||||
package io.element.android.x.features.rageshake.detection
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import io.element.android.x.features.rageshake.preferences.RageshakePreferencesState
|
||||
|
||||
@Stable
|
||||
data class RageshakeDetectionState(
|
||||
val takeScreenshot: Boolean = false,
|
||||
val showDialog: Boolean = false,
|
||||
|
|
|
|||
|
|
@ -34,9 +34,9 @@ import io.element.android.x.element.resources.R as ElementR
|
|||
fun RageshakeDetectionView(
|
||||
state: RageshakeDetectionState,
|
||||
onOpenBugReport: () -> Unit = { },
|
||||
onScreenshotTaken: (ImageResult) -> Unit,
|
||||
onDisableClicked: () -> Unit,
|
||||
onNoClicked: () -> Unit
|
||||
onScreenshotTaken: (ImageResult) -> Unit = {},
|
||||
onDisableClicked: () -> Unit = {},
|
||||
onNoClicked: () -> Unit = {}
|
||||
) {
|
||||
LogCompositions(tag = "Rageshake", msg = "RageshakeDetectionScreen")
|
||||
val context = LocalContext.current
|
||||
|
|
@ -63,8 +63,10 @@ private fun TakeScreenshot(
|
|||
onScreenshotTaken: (ImageResult) -> Unit = {}
|
||||
) {
|
||||
val view = LocalView.current
|
||||
view.screenshot {
|
||||
onScreenshotTaken(it)
|
||||
LaunchedEffect(Unit) {
|
||||
view.screenshot {
|
||||
onScreenshotTaken(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,8 +17,7 @@ import javax.inject.Inject
|
|||
class RageshakePreferencesPresenter @Inject constructor(
|
||||
private val rageshake: RageShake,
|
||||
private val rageshakeDataStore: RageshakeDataStore,
|
||||
|
||||
) : Presenter<RageshakePreferencesState, RageshakePreferencesEvents> {
|
||||
) : Presenter<RageshakePreferencesState, RageshakePreferencesEvents> {
|
||||
|
||||
@Composable
|
||||
override fun present(events: Flow<RageshakePreferencesEvents>): RageshakePreferencesState {
|
||||
|
|
|
|||
|
|
@ -19,10 +19,13 @@ package io.element.android.x.features.rageshake.screenshot
|
|||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import io.element.android.x.core.bitmap.writeBitmap
|
||||
import io.element.android.x.di.AppScope
|
||||
import io.element.android.x.di.ApplicationContext
|
||||
import io.element.android.x.di.SingleIn
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
@SingleIn(AppScope::class)
|
||||
class ScreenshotHolder @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue