From 638693a5d75c6b9e91513ad138a47d6f18dfab56 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Sat, 8 Aug 2026 20:59:01 +0200 Subject: [PATCH] fix(app): keep the crash screen readable and its retry reachable (#3043) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The root error fallback was one unbounded scroller, so on a phone the title rendered under the status bar and a long stack trace pushed Retry off the bottom of the screen — the one action that recovers the app was unreachable on exactly the errors that need it most. The screen is now a fixed shell: a safe-area message block, an independently scrolling details region, and a Retry that stays put. `SafeAreaProvider` moved above the boundary in the root tree because the fallback renders when the app tree below it has already failed, and insets have to be available there. The hand-rolled Pressable and bordered message box are replaced by the shared ` + ); return ( - - {t("rootError.kicker")} - {t("rootError.title")} - {t("rootError.body")} - - {t("rootError.details")} - {error} + + {t("rootError.title")} + {t("rootError.body")} - - {t("common.actions.retry")} - + + {t("rootError.details")} + + {error} + + + {isCompact ? null : retry} - + {isCompact ? {retry} : null} + ); } -function retryButtonStyle({ pressed }: PressableStateCallbackType): StyleProp { - return [styles.retryButton, pressed ? styles.retryButtonPressed : null]; -} - const styles = StyleSheet.create((theme) => ({ container: { flex: 1, backgroundColor: theme.colors.surface0, }, - contentContainer: { - flexGrow: 1, + content: { + flex: 1, + alignSelf: "center", + width: "100%", + maxWidth: 420, justifyContent: "center", + gap: theme.spacing[6], paddingHorizontal: theme.spacing[6], paddingVertical: theme.spacing[8], }, - content: { - alignSelf: "center", - width: "100%", - maxWidth: 520, - gap: theme.spacing[4], - }, - kicker: { - color: theme.colors.destructive, - fontSize: theme.fontSize.sm, - fontWeight: theme.fontWeight.semibold, + header: { + alignItems: "center", + gap: theme.spacing[2], }, title: { color: theme.colors.foreground, fontSize: theme.fontSize.xl, - fontWeight: theme.fontWeight.semibold, + fontWeight: theme.fontWeight.medium, + textAlign: "center", }, body: { color: theme.colors.foregroundMuted, - fontSize: theme.fontSize.base, - lineHeight: 22, - }, - messageBox: { - gap: theme.spacing[2], - borderWidth: 1, - borderColor: theme.colors.borderAccent, - borderRadius: theme.borderRadius.lg, - backgroundColor: theme.colors.surface1, - padding: theme.spacing[4], - }, - messageLabel: { - color: theme.colors.foregroundMuted, - fontSize: theme.fontSize.xs, - fontWeight: theme.fontWeight.semibold, - }, - message: { - color: theme.colors.foreground, fontSize: theme.fontSize.sm, lineHeight: 20, + textAlign: "center", }, - retryButton: { - alignSelf: "flex-start", - minHeight: 40, - justifyContent: "center", - borderRadius: theme.borderRadius.md, - backgroundColor: theme.colors.accent, - paddingHorizontal: theme.spacing[4], - paddingVertical: theme.spacing[2], + details: { + flexShrink: 1, + gap: theme.spacing[2], }, - retryButtonPressed: { - opacity: 0.85, + detailsLabel: { + color: theme.colors.foregroundMuted, + fontSize: theme.fontSize.xs, + fontWeight: theme.fontWeight.medium, }, - retryButtonText: { - color: theme.colors.accentForeground, - fontSize: theme.fontSize.sm, - fontWeight: theme.fontWeight.semibold, + detailsSurface: { + flexShrink: 1, + }, + detailsScroll: { + flexShrink: 1, + }, + footer: { + alignSelf: "center", + width: "100%", + maxWidth: 420, + paddingHorizontal: theme.spacing[6], + paddingBottom: theme.spacing[6], }, })); diff --git a/packages/app/src/i18n/resources/ar.ts b/packages/app/src/i18n/resources/ar.ts index c84a2f098..4870eef43 100644 --- a/packages/app/src/i18n/resources/ar.ts +++ b/packages/app/src/i18n/resources/ar.ts @@ -1243,7 +1243,6 @@ export const ar: TranslationResources = { }, }, rootError: { - kicker: "حدث خطأ", title: "واجه Paseo مشكلة.", body: "جرّب مرة أخرى لإعادة تحميل التطبيق. إذا استمر حدوث ذلك، فأرفق التفاصيل أدناه عند الإبلاغ عنه.", details: "التفاصيل", diff --git a/packages/app/src/i18n/resources/en.ts b/packages/app/src/i18n/resources/en.ts index 67a066112..d26f6ed34 100644 --- a/packages/app/src/i18n/resources/en.ts +++ b/packages/app/src/i18n/resources/en.ts @@ -1253,7 +1253,6 @@ export const en = { }, }, rootError: { - kicker: "Something went wrong", title: "Paseo ran into a problem.", body: "Try again to reload the app. If this keeps happening, include the details below when you report it.", details: "Details", diff --git a/packages/app/src/i18n/resources/es.ts b/packages/app/src/i18n/resources/es.ts index 8536cc9eb..f50db606b 100644 --- a/packages/app/src/i18n/resources/es.ts +++ b/packages/app/src/i18n/resources/es.ts @@ -1283,7 +1283,6 @@ export const es: TranslationResources = { }, }, rootError: { - kicker: "Algo salió mal", title: "Paseo tuvo un problema.", body: "Vuelve a intentarlo para recargar la app. Si sigue ocurriendo, incluye los detalles de abajo al reportarlo.", details: "Detalles", diff --git a/packages/app/src/i18n/resources/fr.ts b/packages/app/src/i18n/resources/fr.ts index 2d3305cbc..4b26acbcd 100644 --- a/packages/app/src/i18n/resources/fr.ts +++ b/packages/app/src/i18n/resources/fr.ts @@ -1286,7 +1286,6 @@ export const fr: TranslationResources = { }, }, rootError: { - kicker: "Une erreur s'est produite", title: "Paseo a rencontré un problème.", body: "Réessayez pour recharger l'application. Si cela continue, joignez les détails ci-dessous au signalement.", details: "Détails", diff --git a/packages/app/src/i18n/resources/ja.ts b/packages/app/src/i18n/resources/ja.ts index 20590fa3d..760f8250d 100644 --- a/packages/app/src/i18n/resources/ja.ts +++ b/packages/app/src/i18n/resources/ja.ts @@ -1256,7 +1256,6 @@ export const ja: TranslationResources = { }, }, rootError: { - kicker: "問題が発生しました", title: "Paseo で問題が発生しました。", body: "アプリを再読み込みするにはもう一度お試しください。繰り返し発生する場合は、以下の詳細を添えて報告してください。", details: "詳細", diff --git a/packages/app/src/i18n/resources/ko.ts b/packages/app/src/i18n/resources/ko.ts index c2268ed65..f3ec7246b 100644 --- a/packages/app/src/i18n/resources/ko.ts +++ b/packages/app/src/i18n/resources/ko.ts @@ -1252,7 +1252,6 @@ export const ko: TranslationResources = { }, }, rootError: { - kicker: "문제가 발생했습니다.", title: "Paseo에 문제가 발생했습니다.", body: "앱을 다시 로드해 보세요. 이런 일이 계속 발생하면 신고할 때 아래 세부정보를 포함하세요.", details: "세부", diff --git a/packages/app/src/i18n/resources/pt-BR.ts b/packages/app/src/i18n/resources/pt-BR.ts index 99de443f8..c2d21c467 100644 --- a/packages/app/src/i18n/resources/pt-BR.ts +++ b/packages/app/src/i18n/resources/pt-BR.ts @@ -1269,7 +1269,6 @@ export const ptBR: TranslationResources = { }, }, rootError: { - kicker: "Algo deu errado", title: "O Paseo encontrou um problema.", body: "Tente novamente para recarregar o app. Se isso continuar acontecendo, inclua os detalhes abaixo ao relatar o problema.", details: "Detalhes", diff --git a/packages/app/src/i18n/resources/ru.ts b/packages/app/src/i18n/resources/ru.ts index 3d9cd2601..aa0b51082 100644 --- a/packages/app/src/i18n/resources/ru.ts +++ b/packages/app/src/i18n/resources/ru.ts @@ -1272,7 +1272,6 @@ export const ru: TranslationResources = { }, }, rootError: { - kicker: "Что-то пошло не так", title: "В Paseo возникла проблема.", body: "Попробуйте снова перезагрузить приложение. Если это повторяется, приложите приведенные ниже подробности к отчету.", details: "Подробности", diff --git a/packages/app/src/i18n/resources/zh-CN.ts b/packages/app/src/i18n/resources/zh-CN.ts index 45b13f0cb..d83da6c0a 100644 --- a/packages/app/src/i18n/resources/zh-CN.ts +++ b/packages/app/src/i18n/resources/zh-CN.ts @@ -1227,7 +1227,6 @@ export const zhCN: TranslationResources = { }, }, rootError: { - kicker: "出现问题", title: "Paseo 遇到了问题。", body: "请重试以重新加载应用。如果问题持续发生,请在报告时附上下面的详细信息。", details: "详情",