@@ -36,9 +36,13 @@ import com.ichi2.anki.CollectionManager.TR
3636import com.ichi2.anki.CollectionManager.withCol
3737import com.ichi2.anki.CrashReportData.Companion.throwIfDialogUnusable
3838import com.ichi2.anki.CrashReportData.Companion.toCrashReportData
39+ import com.ichi2.anki.CrashReportData.HelpAction
40+ import com.ichi2.anki.CrashReportData.HelpAction.AnkiBackendLink
41+ import com.ichi2.anki.CrashReportData.HelpAction.OpenDeckOptions
3942import com.ichi2.anki.common.annotations.UseContextParameter
4043import com.ichi2.anki.exception.StorageAccessException
4144import com.ichi2.anki.libanki.Collection
45+ import com.ichi2.anki.pages.DeckOptionsDestination
4246import com.ichi2.anki.snackbar.showSnackbar
4347import com.ichi2.anki.utils.openUrl
4448import com.ichi2.utils.create
@@ -260,17 +264,24 @@ fun Context.showError(
260264 title(R .string.vague_error)
261265 message(text = message)
262266 positiveButton(R .string.dialog_ok)
263- if (crashReportData?.helpLink != null ) {
267+ if (crashReportData?.helpAction != null ) {
264268 neutralButton(R .string.help)
265269 }
266- if (crashReportData?.reportException == true ) {
270+ if (crashReportData?.reportableException == true ) {
267271 Timber .w(" sending crash report on close" )
268272 setOnDismissListener { crashReportData.sendCrashReport() }
269273 }
270274 }.apply {
271275 // setup the help link. Link is non-null if neutralButton exists.
272276 setOnShowListener {
273- neutralButton?.setOnClickListener { openUrl(crashReportData!! .helpLink!! ) }
277+ neutralButton?.setOnClickListener {
278+ lifecycle.coroutineScope.launch {
279+ val shouldDismiss = crashReportData!! .helpAction!! .execute(context = context)
280+ if (shouldDismiss) {
281+ dismiss()
282+ }
283+ }
284+ }
274285 }
275286 setupEnterKeyHandler()
276287 show()
@@ -282,6 +293,26 @@ fun Context.showError(
282293 }
283294}
284295
296+ /* *
297+ * @return Whether the dialog should be dismissed
298+ */
299+ suspend fun HelpAction.execute (context : Context ): Boolean {
300+ when (this ) {
301+ is AnkiBackendLink -> {
302+ context.openUrl(this .link)
303+ return false
304+ }
305+ OpenDeckOptions -> {
306+ // if we're in the error dialog, we have no context of the deck which caused the exception
307+ // assume it's the current deck
308+ val openCurrentDeckOptions = DeckOptionsDestination .fromCurrentDeck()
309+ context.startActivity(openCurrentDeckOptions.toIntent(context))
310+ // dismiss the dialog - the user should have resolved the issue
311+ return true
312+ }
313+ }
314+ }
315+
285316/* * In most cases, you'll want [AnkiActivity.withProgress]
286317 * instead. This lower-level routine can be used to integrate your own
287318 * progress UI.
@@ -575,29 +606,66 @@ private fun Activity.showError(
575606data class CrashReportData (
576607 val exception : Throwable ,
577608 val origin : String ,
578- val reportException : Boolean ,
609+ val reportableException : Boolean ,
579610) {
580611 /* *
581612 * Optional link to a help page regarding the error
582613 *
583614 * For example: https://docs.ankiweb.net/templates/errors.html#no-cloze-filter-on-cloze-notetype
615+ * Or opening the deck options
584616 */
585- val helpLink: Uri ?
586- get() =
587- try {
588- (exception as ? BackendException )
589- ?.getDesktopHelpPageLink(CollectionManager .getBackend())
590- ?.toUri()
591- } catch (e: Exception ) {
592- Timber .w(e)
593- null
594- }
617+ val helpAction: HelpAction ?
618+ get() = HelpAction .from(exception)
619+
620+ fun shouldReportException (): Boolean {
621+ if (! reportableException) return false
622+ if (exception.isInvalidFsrsParametersException()) return false
623+ return true
624+ }
595625
596626 fun sendCrashReport () {
597- if (! reportException) return
627+ if (! shouldReportException()) {
628+ Timber .i(" skipped crash report due to further validation" )
629+ return
630+ }
598631 CrashReportService .sendExceptionReport(exception, origin)
599632 }
600633
634+ /* *
635+ * Optional action to provide more information about an error
636+ *
637+ * Examples:
638+ * - Open https://docs.ankiweb.net/templates/errors.html#no-cloze-filter-on-cloze-notetype
639+ * - Open the deck options
640+ */
641+ sealed class HelpAction {
642+ data class AnkiBackendLink (
643+ val link : Uri ,
644+ ) : HelpAction()
645+
646+ /* * Open the deck options for the current deck */
647+ data object OpenDeckOptions : HelpAction ()
648+
649+ companion object {
650+ fun from (e : Throwable ): HelpAction ? {
651+ val link =
652+ try {
653+ (e as ? BackendException )
654+ ?.getDesktopHelpPageLink(CollectionManager .getBackend())
655+ ?.toUri()
656+ } catch (e: Exception ) {
657+ Timber .w(e)
658+ null
659+ }
660+
661+ if (link != null ) return AnkiBackendLink (link)
662+ if (e.isInvalidFsrsParametersException()) return OpenDeckOptions
663+
664+ return null
665+ }
666+ }
667+ }
668+
601669 companion object {
602670 @UseContextParameter(" context" )
603671 fun Throwable.toCrashReportData (
@@ -607,15 +675,15 @@ data class CrashReportData(
607675 exception = this ,
608676 // Appears as 'ManageNotetypes'
609677 origin = context::class .java.simpleName,
610- reportException = reportException,
678+ reportableException = reportException,
611679 )
612680
613681 /* *
614682 * If [throwOnShowError] is set, throws the exception from the crash report data
615683 *
616684 * So unit tests can fail if an unexpected exception is thrown
617685 *
618- * Note: this occurs regardless of the status of [reportException ]
686+ * Note: this occurs regardless of the status of [reportableException ]
619687 *
620688 * @param message The message of the thrown [IllegalStateException]
621689 * @throws IllegalStateException with [exception] as an innerException if the receiver
@@ -627,5 +695,13 @@ data class CrashReportData(
627695 if (this == null ) throw IllegalStateException (message)
628696 throw IllegalStateException (message, exception)
629697 }
698+
699+ private fun Throwable.isInvalidFsrsParametersException (): Boolean =
700+ try {
701+ // `TR` may fail in an error-reporting context
702+ message == TR .deckConfigInvalidParameters()
703+ } catch (_: Throwable ) {
704+ false
705+ }
630706 }
631707}
0 commit comments