add better support info when there's an error on startup
This commit is contained in:
@@ -49,6 +49,8 @@ export const DEFAULT_PUSH_SERVER =
|
|||||||
|
|
||||||
export const IMAGE_TYPE_PROFILE = "profile";
|
export const IMAGE_TYPE_PROFILE = "profile";
|
||||||
|
|
||||||
|
export const SUPPORT_EMAIL = "info@TimeSafari.app";
|
||||||
|
|
||||||
export const PASSKEYS_ENABLED =
|
export const PASSKEYS_ENABLED =
|
||||||
!!import.meta.env.VITE_PASSKEYS_ENABLED || false;
|
!!import.meta.env.VITE_PASSKEYS_ENABLED || false;
|
||||||
|
|
||||||
|
|||||||
@@ -412,6 +412,18 @@ router.beforeEach(async (to, _from, next) => {
|
|||||||
timestamp: new Date().toISOString(),
|
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
|
// Redirect to start page if identity creation fails
|
||||||
// This allows users to manually create an identity or troubleshoot
|
// This allows users to manually create an identity or troubleshoot
|
||||||
logger.info(
|
logger.info(
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ import {
|
|||||||
} from "../interfaces/deepLinks";
|
} from "../interfaces/deepLinks";
|
||||||
import { logConsoleAndDb } from "../db/databaseUtil";
|
import { logConsoleAndDb } from "../db/databaseUtil";
|
||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
|
import { SUPPORT_EMAIL } from "../constants/app";
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -105,7 +106,7 @@ const goHome = () => router.replace({ name: "home" });
|
|||||||
const reportIssue = () => {
|
const reportIssue = () => {
|
||||||
// Open a support form or email
|
// Open a support form or email
|
||||||
window.open(
|
window.open(
|
||||||
"mailto:support@timesafari.app?subject=Invalid Deep Link&body=" +
|
`mailto:${SUPPORT_EMAIL}?subject=Invalid Deep Link&body=` +
|
||||||
encodeURIComponent(
|
encodeURIComponent(
|
||||||
`I encountered an error with a deep link: timesafari://${originalPath.value}\nError: ${errorMessage.value}`,
|
`I encountered an error with a deep link: timesafari://${originalPath.value}\nError: ${errorMessage.value}`,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -552,8 +552,8 @@
|
|||||||
</h2>
|
</h2>
|
||||||
<p>
|
<p>
|
||||||
Contact us at
|
Contact us at
|
||||||
<a href="mailto:info@TimeSafari.app" class="text-blue-500"
|
<a :href="`mailto:${SUPPORT_EMAIL}`" class="text-blue-500"
|
||||||
>info@TimeSafari.app</a
|
>{{ SUPPORT_EMAIL }}</a
|
||||||
>
|
>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -591,7 +591,7 @@ import { copyToClipboard } from "../services/ClipboardService";
|
|||||||
|
|
||||||
import * as Package from "../../package.json";
|
import * as Package from "../../package.json";
|
||||||
import QuickNav from "../components/QuickNav.vue";
|
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 { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||||
import { QRNavigationService } from "@/services/QRNavigationService";
|
import { QRNavigationService } from "@/services/QRNavigationService";
|
||||||
import { UNNAMED_ENTITY_NAME } from "@/constants/entities";
|
import { UNNAMED_ENTITY_NAME } from "@/constants/entities";
|
||||||
@@ -643,7 +643,7 @@ export default class HelpView extends Vue {
|
|||||||
showVerifiable = false;
|
showVerifiable = false;
|
||||||
|
|
||||||
APP_SERVER = APP_SERVER;
|
APP_SERVER = APP_SERVER;
|
||||||
// Capacitor reference removed - using QRNavigationService instead
|
SUPPORT_EMAIL = SUPPORT_EMAIL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize notification helpers
|
* Initialize notification helpers
|
||||||
|
|||||||
@@ -26,6 +26,31 @@
|
|||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Startup error banner -->
|
||||||
|
<div
|
||||||
|
v-if="startupError"
|
||||||
|
class="max-w-3xl mx-auto mb-6 p-4 bg-red-50 border border-red-300 rounded-lg"
|
||||||
|
>
|
||||||
|
<h2 class="text-red-800 font-semibold text-lg mb-2">Startup Error</h2>
|
||||||
|
<p class="text-red-700 mb-3">
|
||||||
|
The app encountered a critical error during startup. This is often
|
||||||
|
caused by a database problem. Please send the details below to
|
||||||
|
<a :href="`mailto:${SUPPORT_EMAIL}`" class="underline font-semibold">{{
|
||||||
|
SUPPORT_EMAIL
|
||||||
|
}}</a>
|
||||||
|
so we can help resolve it.
|
||||||
|
</p>
|
||||||
|
<details class="bg-white border border-red-200 rounded p-3">
|
||||||
|
<summary class="cursor-pointer text-red-700 font-medium">
|
||||||
|
Error details
|
||||||
|
</summary>
|
||||||
|
<pre
|
||||||
|
class="mt-2 text-xs text-red-900 whitespace-pre-wrap break-words"
|
||||||
|
>{{ startupError }}</pre
|
||||||
|
>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- id used by puppeteer test script -->
|
<!-- id used by puppeteer test script -->
|
||||||
<div id="start-question">
|
<div id="start-question">
|
||||||
<div class="max-w-3xl mx-auto">
|
<div class="max-w-3xl mx-auto">
|
||||||
@@ -139,7 +164,7 @@
|
|||||||
import { Component, Vue } from "vue-facing-decorator";
|
import { Component, Vue } from "vue-facing-decorator";
|
||||||
import { Router } from "vue-router";
|
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 { PlatformServiceMixin } from "../utils/PlatformServiceMixin";
|
||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
|
|
||||||
@@ -157,10 +182,12 @@ export default class StartView extends Vue {
|
|||||||
|
|
||||||
// Feature flags and application constants
|
// Feature flags and application constants
|
||||||
PASSKEYS_ENABLED = PASSKEYS_ENABLED;
|
PASSKEYS_ENABLED = PASSKEYS_ENABLED;
|
||||||
|
SUPPORT_EMAIL = SUPPORT_EMAIL;
|
||||||
|
|
||||||
// Component state for identity generation
|
// Component state for identity generation
|
||||||
givenName = "";
|
givenName = "";
|
||||||
numAccounts = 0;
|
numAccounts = 0;
|
||||||
|
startupError = "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computed property for primary action button styling
|
* Computed property for primary action button styling
|
||||||
@@ -201,11 +228,26 @@ export default class StartView extends Vue {
|
|||||||
*/
|
*/
|
||||||
async mounted() {
|
async mounted() {
|
||||||
try {
|
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();
|
const settings = await this.$accountSettings();
|
||||||
this.givenName = settings.firstName || "";
|
this.givenName = settings.firstName || "";
|
||||||
|
|
||||||
// Load account count for display logic
|
|
||||||
this.numAccounts = await retrieveAccountCount();
|
this.numAccounts = await retrieveAccountCount();
|
||||||
|
|
||||||
logger.debug("[StartView] Component mounted", {
|
logger.debug("[StartView] Component mounted", {
|
||||||
@@ -215,7 +257,6 @@ export default class StartView extends Vue {
|
|||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("[StartView] Failed to load initialization data", error);
|
logger.error("[StartView] Failed to load initialization data", error);
|
||||||
// Continue with default behavior if settings load fails
|
|
||||||
this.givenName = "";
|
this.givenName = "";
|
||||||
this.numAccounts = 0;
|
this.numAccounts = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user