diff --git a/src/constants/app.ts b/src/constants/app.ts index 89276dbc..d60da0e9 100644 --- a/src/constants/app.ts +++ b/src/constants/app.ts @@ -49,6 +49,8 @@ export const DEFAULT_PUSH_SERVER = export const IMAGE_TYPE_PROFILE = "profile"; +export const SUPPORT_EMAIL = "info@TimeSafari.app"; + export const PASSKEYS_ENABLED = !!import.meta.env.VITE_PASSKEYS_ENABLED || false; diff --git a/src/router/index.ts b/src/router/index.ts index 6fcca363..0f00ceb4 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -412,6 +412,18 @@ router.beforeEach(async (to, _from, next) => { timestamp: new Date().toISOString(), }); + // Store error details so StartView can display them + const errorMessage = error instanceof Error ? error.message : String(error); + const errorStack = error instanceof Error ? error.stack || "" : ""; + try { + sessionStorage.setItem( + "startupError", + JSON.stringify({ message: errorMessage, stack: errorStack }), + ); + } catch { + // sessionStorage may be unavailable + } + // Redirect to start page if identity creation fails // This allows users to manually create an identity or troubleshoot logger.info( diff --git a/src/views/DeepLinkErrorView.vue b/src/views/DeepLinkErrorView.vue index 57bbd00c..e0385cd8 100644 --- a/src/views/DeepLinkErrorView.vue +++ b/src/views/DeepLinkErrorView.vue @@ -59,6 +59,7 @@ import { } from "../interfaces/deepLinks"; import { logConsoleAndDb } from "../db/databaseUtil"; import { logger } from "../utils/logger"; +import { SUPPORT_EMAIL } from "../constants/app"; const route = useRoute(); const router = useRouter(); @@ -105,7 +106,7 @@ const goHome = () => router.replace({ name: "home" }); const reportIssue = () => { // Open a support form or email window.open( - "mailto:support@timesafari.app?subject=Invalid Deep Link&body=" + + `mailto:${SUPPORT_EMAIL}?subject=Invalid Deep Link&body=` + encodeURIComponent( `I encountered an error with a deep link: timesafari://${originalPath.value}\nError: ${errorMessage.value}`, ), diff --git a/src/views/HelpView.vue b/src/views/HelpView.vue index d48b6272..ac2aa9d6 100644 --- a/src/views/HelpView.vue +++ b/src/views/HelpView.vue @@ -552,8 +552,8 @@

Contact us at - info@TimeSafari.app{{ SUPPORT_EMAIL }}

@@ -591,7 +591,7 @@ import { copyToClipboard } from "../services/ClipboardService"; import * as Package from "../../package.json"; import QuickNav from "../components/QuickNav.vue"; -import { APP_SERVER, NotificationIface } from "../constants/app"; +import { APP_SERVER, NotificationIface, SUPPORT_EMAIL } from "../constants/app"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; import { QRNavigationService } from "@/services/QRNavigationService"; import { UNNAMED_ENTITY_NAME } from "@/constants/entities"; @@ -643,7 +643,7 @@ export default class HelpView extends Vue { showVerifiable = false; APP_SERVER = APP_SERVER; - // Capacitor reference removed - using QRNavigationService instead + SUPPORT_EMAIL = SUPPORT_EMAIL; /** * Initialize notification helpers diff --git a/src/views/StartView.vue b/src/views/StartView.vue index e6871cae..613fdee0 100644 --- a/src/views/StartView.vue +++ b/src/views/StartView.vue @@ -26,6 +26,31 @@ + +
+

Startup Error

+

+ The app encountered a critical error during startup. This is often + caused by a database problem. Please send the details below to + {{ + SUPPORT_EMAIL + }} + so we can help resolve it. +

+
+ + Error details + +
{{ startupError }}
+
+
+
@@ -139,7 +164,7 @@ import { Component, Vue } from "vue-facing-decorator"; import { Router } from "vue-router"; -import { AppString, PASSKEYS_ENABLED } from "../constants/app"; +import { AppString, PASSKEYS_ENABLED, SUPPORT_EMAIL } from "../constants/app"; import { PlatformServiceMixin } from "../utils/PlatformServiceMixin"; import { logger } from "../utils/logger"; @@ -157,10 +182,12 @@ export default class StartView extends Vue { // Feature flags and application constants PASSKEYS_ENABLED = PASSKEYS_ENABLED; + SUPPORT_EMAIL = SUPPORT_EMAIL; // Component state for identity generation givenName = ""; numAccounts = 0; + startupError = ""; /** * Computed property for primary action button styling @@ -201,11 +228,26 @@ export default class StartView extends Vue { */ async mounted() { try { - // Load user settings using platform service + const raw = sessionStorage.getItem("startupError"); + if (raw) { + sessionStorage.removeItem("startupError"); + try { + const parsed = JSON.parse(raw); + const parts = [parsed.message, parsed.stack].filter(Boolean); + this.startupError = parts.length > 0 ? parts.join("\n\n") : raw; + logger.error("[StartView] Displaying startup error to user", parsed); + } catch { + this.startupError = raw; + } + } + } catch { + // sessionStorage or JSON parse may fail; non-critical + } + + try { const settings = await this.$accountSettings(); this.givenName = settings.firstName || ""; - // Load account count for display logic this.numAccounts = await retrieveAccountCount(); logger.debug("[StartView] Component mounted", { @@ -215,7 +257,6 @@ export default class StartView extends Vue { }); } catch (error) { logger.error("[StartView] Failed to load initialization data", error); - // Continue with default behavior if settings load fails this.givenName = ""; this.numAccounts = 0; }