forked from trent_larson/crowd-funder-for-time-pwa
chore: reverting files to conform to master
This commit is contained in:
@@ -403,7 +403,7 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 35;
|
||||
CURRENT_PROJECT_VERSION = 37;
|
||||
DEVELOPMENT_TEAM = GM3FS5JQPH;
|
||||
ENABLE_APP_SANDBOX = NO;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
@@ -413,7 +413,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.2;
|
||||
MARKETING_VERSION = 1.0.4;
|
||||
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.timesafari;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -430,7 +430,7 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 35;
|
||||
CURRENT_PROJECT_VERSION = 37;
|
||||
DEVELOPMENT_TEAM = GM3FS5JQPH;
|
||||
ENABLE_APP_SANDBOX = NO;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
@@ -440,7 +440,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.2;
|
||||
MARKETING_VERSION = 1.0.4;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.timesafari;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
|
||||
|
||||
@@ -42,114 +42,75 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from "vue-facing-decorator";
|
||||
import { RouteLocationNormalizedLoaded, Router } from "vue-router";
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import {
|
||||
VALID_DEEP_LINK_ROUTES,
|
||||
deepLinkSchemas,
|
||||
} from "../interfaces/deepLinks";
|
||||
import { logConsoleAndDb } from "../db/databaseUtil";
|
||||
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;
|
||||
}
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
// an object with the route as the key and the first param name as the value
|
||||
const deepLinkSchemaKeys = Object.fromEntries(
|
||||
Object.entries(deepLinkSchemas).map(([route, schema]) => {
|
||||
const param = Object.keys(schema.shape)[0];
|
||||
return [route, param];
|
||||
}),
|
||||
);
|
||||
|
||||
get router() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return (this as any).$router as Router;
|
||||
}
|
||||
// Extract error information from query params
|
||||
const errorCode = computed(
|
||||
() => (route.query.errorCode as string) || "UNKNOWN_ERROR",
|
||||
);
|
||||
const errorMessage = computed(
|
||||
() =>
|
||||
(route.query.errorMessage as string) ||
|
||||
"The deep link you followed is invalid or not supported.",
|
||||
);
|
||||
const originalPath = computed(() => route.query.originalPath as string);
|
||||
const validRoutes = VALID_DEEP_LINK_ROUTES;
|
||||
|
||||
// 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];
|
||||
}),
|
||||
);
|
||||
}
|
||||
// Format the path and include any parameters
|
||||
const formattedPath = computed(() => {
|
||||
if (!originalPath.value) return "";
|
||||
const path = originalPath.value.replace(/^\/+/, "");
|
||||
|
||||
// Computed properties for error information
|
||||
get errorCode(): string {
|
||||
return (this.route.query.errorCode as string) || "UNKNOWN_ERROR";
|
||||
}
|
||||
// Log for debugging
|
||||
logger.log(
|
||||
"[DeepLinkError] Original Path:",
|
||||
originalPath.value,
|
||||
"Route Params:",
|
||||
route.params,
|
||||
"Route Query:",
|
||||
route.query,
|
||||
);
|
||||
|
||||
get errorMessage(): string {
|
||||
return (
|
||||
(this.route.query.errorMessage as string) ||
|
||||
"The deep link you followed is invalid or not supported."
|
||||
);
|
||||
}
|
||||
return path;
|
||||
});
|
||||
|
||||
get originalPath(): string {
|
||||
return this.route.query.originalPath as string;
|
||||
}
|
||||
// Navigation methods
|
||||
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=" +
|
||||
encodeURIComponent(
|
||||
`I encountered an error with a deep link: timesafari://${originalPath.value}\nError: ${errorMessage.value}`,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
// Log the error for analytics
|
||||
onMounted(() => {
|
||||
logConsoleAndDb(
|
||||
`[DeepLinkError] Error page displayed for path: ${originalPath.value}, code: ${errorCode.value}, params: ${JSON.stringify(route.params)}, query: ${JSON.stringify(route.query)}`,
|
||||
true,
|
||||
);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -102,35 +102,9 @@ import { RouteLocationNormalizedLoaded, Router } from "vue-router";
|
||||
import { APP_SERVER } from "@/constants/app";
|
||||
import { logger } from "@/utils/logger";
|
||||
import { errorStringForLog } from "@/libs/endorserServer";
|
||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
|
||||
|
||||
/**
|
||||
* DeepLinkRedirectView Component
|
||||
*
|
||||
* Handles deep link redirection from web to the Time Safari mobile app.
|
||||
* Provides platform-specific redirection logic and fallback options.
|
||||
*
|
||||
* Features:
|
||||
* - Deep link redirection to mobile app
|
||||
* - Platform detection (iOS/Android/Desktop)
|
||||
* - Web fallback for desktop users
|
||||
* - Manual link options for failed redirects
|
||||
* - Development debugging information
|
||||
*
|
||||
* Deep Link Format:
|
||||
* - Mobile: timesafari://[path]?[query]
|
||||
* - Web: https://timesafari.app/[path]?[query]
|
||||
*
|
||||
* Route Parameters:
|
||||
* - path: Catch-all parameter for the destination path
|
||||
* - query: Query parameters to pass to the app
|
||||
*
|
||||
* @author Matthew Raymer
|
||||
* @since 2025-07-21
|
||||
*/
|
||||
@Component({
|
||||
mixins: [PlatformServiceMixin],
|
||||
})
|
||||
@Component({})
|
||||
export default class DeepLinkRedirectView extends Vue {
|
||||
$router!: Router;
|
||||
$route!: RouteLocationNormalizedLoaded;
|
||||
@@ -140,11 +114,8 @@ export default class DeepLinkRedirectView extends Vue {
|
||||
webUrl: string | null = null; // web link, eg "https://timesafari.app/..."
|
||||
isDevelopment: boolean = false;
|
||||
userAgent: string = "";
|
||||
private platformService = PlatformServiceFactory.getInstance();
|
||||
|
||||
/**
|
||||
* Component lifecycle hook - initializes deep link redirection
|
||||
* Parses route parameters and sets up redirect URLs
|
||||
*/
|
||||
mounted() {
|
||||
// Get the path from the route parameter (catch-all parameter)
|
||||
const pathParam = this.$route.params.path;
|
||||
@@ -183,10 +154,6 @@ export default class DeepLinkRedirectView extends Vue {
|
||||
this.openDeepLink();
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to open the deep link URL
|
||||
* Uses multiple fallback methods for maximum compatibility
|
||||
*/
|
||||
private openDeepLink() {
|
||||
if (!this.deepLinkUrl || !this.webUrl) {
|
||||
this.pageError =
|
||||
@@ -225,11 +192,6 @@ export default class DeepLinkRedirectView extends Vue {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles deep link button click
|
||||
* Prevents default behavior and manually triggers deep link
|
||||
* @param event - Click event
|
||||
*/
|
||||
private handleDeepLinkClick(event: Event) {
|
||||
if (!this.deepLinkUrl) return;
|
||||
|
||||
@@ -239,16 +201,11 @@ export default class DeepLinkRedirectView extends Vue {
|
||||
this.openDeepLink();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles web fallback button click
|
||||
* Uses platform-specific behavior for opening web links
|
||||
* @param event - Click event
|
||||
*/
|
||||
private handleWebFallbackClick(event: Event) {
|
||||
if (!this.webUrl) return;
|
||||
|
||||
// Get platform capabilities using mixin
|
||||
const capabilities = this.platformCapabilities;
|
||||
// Get platform capabilities
|
||||
const capabilities = this.platformService.getCapabilities();
|
||||
|
||||
// For mobile, try to open in a new tab/window
|
||||
if (capabilities.isMobile) {
|
||||
@@ -258,30 +215,13 @@ export default class DeepLinkRedirectView extends Vue {
|
||||
// For desktop, let the default behavior happen (opens in same tab)
|
||||
}
|
||||
|
||||
// ===== COMPUTED PROPERTIES =====
|
||||
|
||||
/**
|
||||
* Platform capabilities accessor
|
||||
* Provides cached access to platform capabilities
|
||||
*/
|
||||
get platformCapabilities() {
|
||||
return this.platformService.getCapabilities();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the current platform is mobile
|
||||
* Uses PlatformServiceMixin for platform detection
|
||||
*/
|
||||
// Computed properties for template
|
||||
get isMobile(): boolean {
|
||||
return this.platformCapabilities.isMobile;
|
||||
return this.platformService.getCapabilities().isMobile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the current platform is iOS
|
||||
* Uses PlatformServiceMixin for platform detection
|
||||
*/
|
||||
get isIOS(): boolean {
|
||||
return this.platformCapabilities.isIOS;
|
||||
return this.platformService.getCapabilities().isIOS;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user