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. 119
      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 = "";

119
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
* 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]) => { Object.entries(deepLinkSchemas).map(([route, schema]) => {
const param = Object.keys(schema.shape)[0]; const param = Object.keys(schema.shape)[0];
return [route, param]; return [route, param];
}), }),
); );
}
// Extract error information from query params
// Computed properties for error information const errorCode = computed(
get errorCode(): string { () => (route.query.errorCode as string) || "UNKNOWN_ERROR",
return (this.route.query.errorCode as string) || "UNKNOWN_ERROR"; );
} const errorMessage = computed(
() =>
get errorMessage(): string { (route.query.errorMessage as string) ||
return ( "The deep link you followed is invalid or not supported.",
(this.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;
}
// Format the path and include any parameters
get originalPath(): string { const formattedPath = computed(() => {
return this.route.query.originalPath as string; if (!originalPath.value) return "";
} const path = originalPath.value.replace(/^\/+/, "");
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 // Log for debugging
logger.log( logger.log(
"[DeepLinkError] Original Path:", "[DeepLinkError] Original Path:",
this.originalPath, originalPath.value,
"Route Params:", "Route Params:",
this.route.params, route.params,
"Route Query:", "Route Query:",
this.route.query, route.query,
); );
return path; return path;
} });
// Navigation methods // Navigation methods
goHome(): void { const goHome = () => router.replace({ name: "home" });
this.router.replace({ name: "home" }); const reportIssue = () => {
}
reportIssue(): void {
// 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@timesafari.app?subject=Invalid Deep Link&body=" +
encodeURIComponent( encodeURIComponent(
`I encountered an error with a deep link: timesafari://${this.originalPath}\nError: ${this.errorMessage}`, `I encountered an error with a deep link: timesafari://${originalPath.value}\nError: ${errorMessage.value}`,
), ),
); );
} };
// Lifecycle hook // Log the error for analytics
mounted(): void { onMounted(() => {
// Log the error for analytics logConsoleAndDb(
this.$logAndConsole( `[DeepLinkError] Error page displayed for path: ${originalPath.value}, code: ${errorCode.value}, params: ${JSON.stringify(route.params)}, query: ${JSON.stringify(route.query)}`,
`[DeepLinkError] Error page displayed for path: ${this.originalPath}, code: ${this.errorCode}, params: ${JSON.stringify(this.route.params)}, query: ${JSON.stringify(this.route.query)}`,
true, 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