IMPORTANT: The file content has been truncated. Status: Showing lines 50-150 of 196 total lines. Action: To read more of the file, you can use the 'start_line' and 'end_line' parameters in a subsequent 'read_file' call. For example, to read the next section of the file, use start_line: 151. --- FILE CONTENT (truncated) --- data class IncludeDeviceInfoChanged(val include: Boolean) : ReportBugEvent() data object ToggleDeviceInfo : ReportBugEvent() data object Submit : ReportBugEvent() } sealed class ReportBugEffect { data class ShowSnackbar(val message: String) : ReportBugEffect() data object NavigateBack : ReportBugEffect() data class FallbackToEmail( val recipient: String, val subject: String, val body: String, ) : ReportBugEffect() } class ReportBugViewModel( dependencies: ViewModelDependencies, private val sentryWrapper: SentryWrapper = SentryWrapperImpl(), ) : GatedViewModel(dependencies) { private val _state = MutableStateFlow(ReportBugState()) val state: StateFlow = _state.asStateFlow() private val _effects = Channel(Channel.BUFFERED) val effects = _effects.receiveAsFlow() private var retryCount = 0 init { viewModelScope.launch { val prefs = dependencies.preferences.preferences.first() _state.update { it.copy( appVersion = BuildConfig.VERSION_NAME, androidVersion = Build.VERSION.RELEASE, locale = Locale.getDefault().toString(), deviceModel = Build.MODEL, vaultConfigured = !prefs.vaultUriString.isNullOrEmpty(), ) } } } fun onEvent(event: ReportBugEvent) { when (event) { is ReportBugEvent.TypeChanged -> _state.update { it.copy(type = event.type) } is ReportBugEvent.SummaryChanged -> _state.update { it.copy(summary = event.summary.take(MAX_SUMMARY_LENGTH)) } is ReportBugEvent.DescriptionChanged -> _state.update { it.copy(description = event.description.take(MAX_DESCRIPTION_LENGTH)) } is ReportBugEvent.ContactEmailChanged -> _state.update { it.copy(contactEmail = event.email) } is ReportBugEvent.IncludeDeviceInfoChanged -> _state.update { it.copy(includeDeviceInfo = event.include) } ReportBugEvent.ToggleDeviceInfo -> _state.update { it.copy(showDeviceInfo = !it.showDeviceInfo) } ReportBugEvent.Submit -> { retryCount = 0 submitReport() } } } private fun submitReport() { if (!state.value.canSubmit) return viewModelScope.launch { _state.update { it.copy(isSubmitting = true) } performSubmission() } } private suspend fun performSubmission() { try { val s = _state.value val comments = "${s.summary}\n\n${s.description}" val email = s.contactEmail.ifBlank { null } val tags = mutableMapOf( "type" to s.type.name.lowercase(), "app_version" to s.appVersion, "android_version" to s.androidVersion, "locale" to s.locale, "device_model" to s.deviceModel, "vault_configured" to s.vaultConfigured.toString(), ) val submissionTags = if (s.includeDeviceInfo) tags else emptyMap() if (sentryWrapper.isEnabled()) { sentryWrapper.captureFeedback(comments, email, submissionTags) } else { sentryWrapper.lazyInitAndSendFeedback( app, BuildConfig.SENTRY_DSN, comments, email, submissionTags, ) } _effects.send(ReportBugEffect.ShowSnackbar(app.getString(R.string.report_bug_success)))