You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
290 lines
6.2 KiB
290 lines
6.2 KiB
<template>
|
|
<div class="deep-link-error">
|
|
<div class="safe-area-spacer"></div>
|
|
<h1>Invalid Deep Link</h1>
|
|
<div class="error-details">
|
|
<div class="error-message">
|
|
<h3>Error Details</h3>
|
|
<p>{{ errorMessage }}</p>
|
|
<div v-if="errorCode" class="error-code">
|
|
Error Code: <span>{{ errorCode }}</span>
|
|
</div>
|
|
</div>
|
|
<div v-if="originalPath" class="original-link">
|
|
<h3>Attempted Link</h3>
|
|
<code>timesafari://{{ formattedPath }}</code>
|
|
<div class="debug-info">
|
|
<h4>Parameters:</h4>
|
|
<pre>{{ JSON.stringify(route.params, null, 2) }}</pre>
|
|
<h4>Query:</h4>
|
|
<pre>{{ JSON.stringify(route.query, null, 2) }}</pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="actions">
|
|
<button class="primary-button" @click="goHome">Go to Home</button>
|
|
<button class="secondary-button" @click="reportIssue">
|
|
Report Issue
|
|
</button>
|
|
</div>
|
|
<div class="supported-links">
|
|
<h2>Supported Deep Links</h2>
|
|
<ul>
|
|
<li v-for="(routeItem, index) in validRoutes" :key="index">
|
|
<code
|
|
>timesafari://{{ routeItem }}/:{{
|
|
deepLinkSchemaKeys[routeItem]
|
|
}}</code
|
|
>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { Component, Vue } from "vue-facing-decorator";
|
|
import { RouteLocationNormalizedLoaded, Router } from "vue-router";
|
|
import {
|
|
VALID_DEEP_LINK_ROUTES,
|
|
deepLinkSchemas,
|
|
} from "../interfaces/deepLinks";
|
|
import { logger } from "../utils/logger";
|
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
|
|
|
/**
|
|
* DeepLinkErrorView - Displays error information for invalid deep links
|
|
*
|
|
* This view shows detailed error information when a user follows an invalid
|
|
* or unsupported deep link. It provides debugging information and allows
|
|
* users to report issues or navigate back to the home page.
|
|
*
|
|
* @author Matthew Raymer
|
|
*/
|
|
@Component({
|
|
name: "DeepLinkErrorView",
|
|
mixins: [PlatformServiceMixin],
|
|
})
|
|
export default class DeepLinkErrorView extends Vue {
|
|
// Route and router access
|
|
get route() {
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
return (this as any).$route as RouteLocationNormalizedLoaded;
|
|
}
|
|
|
|
get router() {
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
return (this as any).$router as Router;
|
|
}
|
|
|
|
// Deep link schema keys mapping
|
|
// This is an object with the route as the key and the first param name as the value
|
|
get deepLinkSchemaKeys() {
|
|
return Object.fromEntries(
|
|
Object.entries(deepLinkSchemas).map(([route, schema]) => {
|
|
const param = Object.keys(schema.shape)[0];
|
|
return [route, param];
|
|
}),
|
|
);
|
|
}
|
|
|
|
// Computed properties for error information
|
|
get errorCode(): string {
|
|
return (this.route.query.errorCode as string) || "UNKNOWN_ERROR";
|
|
}
|
|
|
|
get errorMessage(): string {
|
|
return (
|
|
(this.route.query.errorMessage as string) ||
|
|
"The deep link you followed is invalid or not supported."
|
|
);
|
|
}
|
|
|
|
get originalPath(): string {
|
|
return this.route.query.originalPath as string;
|
|
}
|
|
|
|
get validRoutes() {
|
|
return VALID_DEEP_LINK_ROUTES;
|
|
}
|
|
|
|
// Format the path and include any parameters
|
|
get formattedPath(): string {
|
|
if (!this.originalPath) return "";
|
|
const path = this.originalPath.replace(/^\/+/, "");
|
|
|
|
// Log for debugging
|
|
logger.log(
|
|
"[DeepLinkError] Original Path:",
|
|
this.originalPath,
|
|
"Route Params:",
|
|
this.route.params,
|
|
"Route Query:",
|
|
this.route.query,
|
|
);
|
|
|
|
return path;
|
|
}
|
|
|
|
// Navigation methods
|
|
goHome(): void {
|
|
this.router.replace({ name: "home" });
|
|
}
|
|
|
|
reportIssue(): void {
|
|
// Open a support form or email
|
|
window.open(
|
|
"mailto:support@timesafari.app?subject=Invalid Deep Link&body=" +
|
|
encodeURIComponent(
|
|
`I encountered an error with a deep link: timesafari://${this.originalPath}\nError: ${this.errorMessage}`,
|
|
),
|
|
);
|
|
}
|
|
|
|
// Lifecycle hook
|
|
mounted(): void {
|
|
// Log the error for analytics
|
|
this.$logAndConsole(
|
|
`[DeepLinkError] Error page displayed for path: ${this.originalPath}, code: ${this.errorCode}, params: ${JSON.stringify(this.route.params)}, query: ${JSON.stringify(this.route.query)}`,
|
|
true,
|
|
);
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.deep-link-error {
|
|
padding-top: 60px;
|
|
padding-left: 20px;
|
|
padding-right: 20px;
|
|
max-width: 600px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.safe-area-spacer {
|
|
height: env(safe-area-inset-top);
|
|
}
|
|
|
|
h1 {
|
|
color: #ff4444;
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
h2,
|
|
h3 {
|
|
color: #333;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.error-details {
|
|
background-color: #f8f8f8;
|
|
border-radius: 8px;
|
|
padding: 20px;
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.error-message {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.error-message p {
|
|
color: #666;
|
|
line-height: 1.5;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.error-code {
|
|
font-family: monospace;
|
|
color: #666;
|
|
margin-top: 8px;
|
|
}
|
|
|
|
.error-code span {
|
|
background-color: #eee;
|
|
padding: 2px 6px;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.original-link {
|
|
padding: 12px;
|
|
background-color: #fff;
|
|
border: 1px solid #ddd;
|
|
border-radius: 6px;
|
|
}
|
|
|
|
.original-link code {
|
|
color: #0066cc;
|
|
font-family: monospace;
|
|
word-break: break-all;
|
|
}
|
|
|
|
.actions {
|
|
margin: 24px 0;
|
|
display: flex;
|
|
gap: 12px;
|
|
}
|
|
|
|
.actions button {
|
|
padding: 10px 20px;
|
|
border-radius: 6px;
|
|
border: none;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.primary-button {
|
|
background-color: #007aff;
|
|
color: white;
|
|
}
|
|
|
|
.secondary-button {
|
|
background-color: #f2f2f2;
|
|
color: #333;
|
|
}
|
|
|
|
.supported-links {
|
|
margin-top: 32px;
|
|
}
|
|
|
|
.supported-links ul {
|
|
list-style: none;
|
|
padding: 0;
|
|
}
|
|
|
|
.supported-links li {
|
|
padding: 8px 12px;
|
|
background-color: #f8f8f8;
|
|
border-radius: 4px;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.supported-links code {
|
|
font-family: monospace;
|
|
color: #0066cc;
|
|
}
|
|
|
|
.debug-info {
|
|
margin-top: 16px;
|
|
padding: 12px;
|
|
background-color: #f0f0f0;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.debug-info h4 {
|
|
margin: 8px 0;
|
|
color: #666;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.debug-info pre {
|
|
white-space: pre-wrap;
|
|
word-break: break-all;
|
|
font-family: monospace;
|
|
font-size: 12px;
|
|
color: #333;
|
|
background-color: #fff;
|
|
padding: 8px;
|
|
border-radius: 4px;
|
|
margin: 4px 0;
|
|
}
|
|
</style>
|
|
|