Browse Source

chore: reverting files to conform to master

pull/142/head
Matthew Raymer 4 days ago
parent
commit
4b88b63669
  1. 8
      ios/App/App.xcodeproj/project.pbxproj
  2. 165
      src/views/DeepLinkErrorView.vue
  3. 76
      src/views/DeepLinkRedirectView.vue

8
ios/App/App.xcodeproj/project.pbxproj

@ -403,7 +403,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 35; CURRENT_PROJECT_VERSION = 37;
DEVELOPMENT_TEAM = GM3FS5JQPH; DEVELOPMENT_TEAM = GM3FS5JQPH;
ENABLE_APP_SANDBOX = NO; ENABLE_APP_SANDBOX = NO;
ENABLE_USER_SCRIPT_SANDBOXING = NO; ENABLE_USER_SCRIPT_SANDBOXING = NO;
@ -413,7 +413,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.2; MARKETING_VERSION = 1.0.4;
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
PRODUCT_BUNDLE_IDENTIFIER = app.timesafari; PRODUCT_BUNDLE_IDENTIFIER = app.timesafari;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@ -430,7 +430,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 35; CURRENT_PROJECT_VERSION = 37;
DEVELOPMENT_TEAM = GM3FS5JQPH; DEVELOPMENT_TEAM = GM3FS5JQPH;
ENABLE_APP_SANDBOX = NO; ENABLE_APP_SANDBOX = NO;
ENABLE_USER_SCRIPT_SANDBOXING = NO; ENABLE_USER_SCRIPT_SANDBOXING = NO;
@ -440,7 +440,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.2; MARKETING_VERSION = 1.0.4;
PRODUCT_BUNDLE_IDENTIFIER = app.timesafari; PRODUCT_BUNDLE_IDENTIFIER = app.timesafari;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";

165
src/views/DeepLinkErrorView.vue

@ -42,114 +42,75 @@
</div> </div>
</template> </template>
<script lang="ts"> <script setup lang="ts">
import { Component, Vue } from "vue-facing-decorator"; import { computed, onMounted } from "vue";
import { RouteLocationNormalizedLoaded, Router } from "vue-router"; import { useRoute, useRouter } from "vue-router";
import { import {
VALID_DEEP_LINK_ROUTES, VALID_DEEP_LINK_ROUTES,
deepLinkSchemas, deepLinkSchemas,
} from "../interfaces/deepLinks"; } from "../interfaces/deepLinks";
import { logConsoleAndDb } from "../db/databaseUtil";
import { logger } from "../utils/logger"; import { logger } from "../utils/logger";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
/** const route = useRoute();
* DeepLinkErrorView - Displays error information for invalid deep links const router = useRouter();
* // an object with the route as the key and the first param name as the value
* This view shows detailed error information when a user follows an invalid const deepLinkSchemaKeys = Object.fromEntries(
* or unsupported deep link. It provides debugging information and allows Object.entries(deepLinkSchemas).map(([route, schema]) => {
* users to report issues or navigate back to the home page. const param = Object.keys(schema.shape)[0];
* return [route, param];
* @author Matthew Raymer }),
*/ );
@Component({
name: "DeepLinkErrorView", // Extract error information from query params
mixins: [PlatformServiceMixin], const errorCode = computed(
}) () => (route.query.errorCode as string) || "UNKNOWN_ERROR",
export default class DeepLinkErrorView extends Vue { );
// Route and router access const errorMessage = computed(
get route() { () =>
// eslint-disable-next-line @typescript-eslint/no-explicit-any (route.query.errorMessage as string) ||
return (this as any).$route as RouteLocationNormalizedLoaded; "The deep link you followed is invalid or not supported.",
} );
const originalPath = computed(() => route.query.originalPath as string);
get router() { const validRoutes = VALID_DEEP_LINK_ROUTES;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (this as any).$router as Router; // Format the path and include any parameters
} const formattedPath = computed(() => {
if (!originalPath.value) return "";
// Deep link schema keys mapping const path = originalPath.value.replace(/^\/+/, "");
// This is an object with the route as the key and the first param name as the value
get deepLinkSchemaKeys() { // Log for debugging
return Object.fromEntries( logger.log(
Object.entries(deepLinkSchemas).map(([route, schema]) => { "[DeepLinkError] Original Path:",
const param = Object.keys(schema.shape)[0]; originalPath.value,
return [route, param]; "Route Params:",
}), route.params,
); "Route Query:",
} route.query,
);
// Computed properties for error information
get errorCode(): string { return path;
return (this.route.query.errorCode as string) || "UNKNOWN_ERROR"; });
}
// Navigation methods
get errorMessage(): string { const goHome = () => router.replace({ name: "home" });
return ( const reportIssue = () => {
(this.route.query.errorMessage as string) || // Open a support form or email
"The deep link you followed is invalid or not supported." 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 originalPath(): string { ),
return this.route.query.originalPath as string; );
} };
get validRoutes() { // Log the error for analytics
return VALID_DEEP_LINK_ROUTES; onMounted(() => {
} logConsoleAndDb(
`[DeepLinkError] Error page displayed for path: ${originalPath.value}, code: ${errorCode.value}, params: ${JSON.stringify(route.params)}, query: ${JSON.stringify(route.query)}`,
// Format the path and include any parameters true,
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> </script>
<style scoped> <style scoped>

76
src/views/DeepLinkRedirectView.vue

@ -102,35 +102,9 @@ import { RouteLocationNormalizedLoaded, Router } from "vue-router";
import { APP_SERVER } from "@/constants/app"; import { APP_SERVER } from "@/constants/app";
import { logger } from "@/utils/logger"; import { logger } from "@/utils/logger";
import { errorStringForLog } from "@/libs/endorserServer"; import { errorStringForLog } from "@/libs/endorserServer";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
/** @Component({})
* 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],
})
export default class DeepLinkRedirectView extends Vue { export default class DeepLinkRedirectView extends Vue {
$router!: Router; $router!: Router;
$route!: RouteLocationNormalizedLoaded; $route!: RouteLocationNormalizedLoaded;
@ -140,11 +114,8 @@ export default class DeepLinkRedirectView extends Vue {
webUrl: string | null = null; // web link, eg "https://timesafari.app/..." webUrl: string | null = null; // web link, eg "https://timesafari.app/..."
isDevelopment: boolean = false; isDevelopment: boolean = false;
userAgent: string = ""; userAgent: string = "";
private platformService = PlatformServiceFactory.getInstance();
/**
* Component lifecycle hook - initializes deep link redirection
* Parses route parameters and sets up redirect URLs
*/
mounted() { mounted() {
// Get the path from the route parameter (catch-all parameter) // Get the path from the route parameter (catch-all parameter)
const pathParam = this.$route.params.path; const pathParam = this.$route.params.path;
@ -183,10 +154,6 @@ export default class DeepLinkRedirectView extends Vue {
this.openDeepLink(); this.openDeepLink();
} }
/**
* Attempts to open the deep link URL
* Uses multiple fallback methods for maximum compatibility
*/
private openDeepLink() { private openDeepLink() {
if (!this.deepLinkUrl || !this.webUrl) { if (!this.deepLinkUrl || !this.webUrl) {
this.pageError = 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) { private handleDeepLinkClick(event: Event) {
if (!this.deepLinkUrl) return; if (!this.deepLinkUrl) return;
@ -239,16 +201,11 @@ export default class DeepLinkRedirectView extends Vue {
this.openDeepLink(); this.openDeepLink();
} }
/**
* Handles web fallback button click
* Uses platform-specific behavior for opening web links
* @param event - Click event
*/
private handleWebFallbackClick(event: Event) { private handleWebFallbackClick(event: Event) {
if (!this.webUrl) return; if (!this.webUrl) return;
// Get platform capabilities using mixin // Get platform capabilities
const capabilities = this.platformCapabilities; const capabilities = this.platformService.getCapabilities();
// For mobile, try to open in a new tab/window // For mobile, try to open in a new tab/window
if (capabilities.isMobile) { if (capabilities.isMobile) {
@ -258,30 +215,13 @@ export default class DeepLinkRedirectView extends Vue {
// For desktop, let the default behavior happen (opens in same tab) // For desktop, let the default behavior happen (opens in same tab)
} }
// ===== COMPUTED PROPERTIES ===== // Computed properties for template
/**
* 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
*/
get isMobile(): boolean { 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 { get isIOS(): boolean {
return this.platformCapabilities.isIOS; return this.platformService.getCapabilities().isIOS;
} }
} }
</script> </script>

Loading…
Cancel
Save