forked from jsnbuchanan/crowd-funder-for-time-pwa
- Fix logging levels: change verbose debugging from info to debug level - TestView: component mounting, boot-time config, URL flow testing - main.capacitor.ts: deeplink processing steps and router state - HomeView: API call details, component state updates, template rendering - Remove redundant environment variable override in vite.config.common.mts - Environment loading via dotenv works correctly - Manual override was defensive programming but unnecessary - Simplifies configuration and reduces maintenance burden - Add comprehensive Playwright timeout behavior documentation - README.md: detailed timeout types, failure behavior, debugging guide - TESTING.md: timeout failure troubleshooting and common scenarios - Clarifies that timeout failures indicate real issues, not flaky tests - Fix TypeScript configuration for .mts imports - tsconfig.node.json: add allowImportingTsExtensions for Vite config files - Resolves import path linting errors for .mts extensions All changes maintain existing functionality while improving code quality and reducing log noise in production environments.
191 lines
6.1 KiB
TypeScript
191 lines
6.1 KiB
TypeScript
/**
|
|
* @file Capacitor Main Entry Point
|
|
* @author Matthew Raymer
|
|
*
|
|
* This file initializes the deep linking system for the TimeSafari app.
|
|
* It sets up the connection between Capacitor's URL handling and our deep link processor.
|
|
*
|
|
* Deep Linking Flow:
|
|
* 1. Capacitor receives URL open event
|
|
* 2. Event is passed to DeepLinkHandler
|
|
* 3. URL is validated and processed
|
|
* 4. Router navigates to appropriate view
|
|
*
|
|
* Integration Points:
|
|
* - Capacitor App plugin for URL handling
|
|
* - Vue Router for navigation
|
|
* - Error handling system
|
|
* - Logging system
|
|
*
|
|
* Type Safety:
|
|
* - Uses DeepLinkHandler for type-safe parameter processing
|
|
* - Ensures type safety between Capacitor events and app routing
|
|
* - Maintains type checking through the entire deep link flow
|
|
*
|
|
* @example
|
|
* // URL open event from OS
|
|
* timesafari://claim/123?view=details
|
|
* // Processed and routed to appropriate view with type-safe parameters
|
|
*/
|
|
|
|
import { initializeApp } from "./main.common";
|
|
import { App as CapacitorApp } from "@capacitor/app";
|
|
import router from "./router";
|
|
import { handleApiError } from "./services/api";
|
|
import { AxiosError } from "axios";
|
|
import { DeepLinkHandler } from "./services/deepLinks";
|
|
import { logger, safeStringify } from "./utils/logger";
|
|
import "./utils/safeAreaInset";
|
|
|
|
logger.log("[Capacitor] 🚀 Starting initialization");
|
|
logger.log("[Capacitor] Platform:", process.env.VITE_PLATFORM);
|
|
|
|
const app = initializeApp();
|
|
|
|
// Initialize API error handling for unhandled promise rejections
|
|
window.addEventListener("unhandledrejection", (event) => {
|
|
if (event.reason?.response) {
|
|
handleApiError(event.reason, event.reason.config?.url || "unknown");
|
|
}
|
|
});
|
|
|
|
const deepLinkHandler = new DeepLinkHandler(router);
|
|
|
|
/**
|
|
* Handles deep link routing for the application
|
|
* Processes URLs in the format timesafari://<route>/<param>
|
|
* Maps incoming deep links to corresponding router paths with parameters
|
|
*
|
|
* @param {Object} data - Deep link data object
|
|
* @param {string} data.url - The full deep link URL to process
|
|
* @returns {Promise<void>}
|
|
*
|
|
* @example
|
|
* // Handles URLs like:
|
|
* // timesafari://claim/01JMAAFZRNSRTQ0EBSD70A8E1H
|
|
* // timesafari://project/abc123
|
|
*
|
|
* @throws {Error} If URL format is invalid
|
|
*/
|
|
const handleDeepLink = async (data: { url: string }) => {
|
|
const { url } = data;
|
|
logger.debug(`[Main] 🌐 Deeplink received from Capacitor: ${url}`);
|
|
|
|
try {
|
|
// Wait for router to be ready
|
|
logger.debug(`[Main] ⏳ Waiting for router to be ready...`);
|
|
await router.isReady();
|
|
logger.debug(`[Main] ✅ Router is ready, processing deeplink`);
|
|
|
|
// Process the deeplink
|
|
logger.debug(`[Main] 🚀 Starting deeplink processing`);
|
|
await deepLinkHandler.handleDeepLink(url);
|
|
logger.debug(`[Main] ✅ Deeplink processed successfully`);
|
|
} catch (error) {
|
|
logger.error(`[Main] ❌ Deeplink processing failed:`, {
|
|
url,
|
|
error: error instanceof Error ? error.message : String(error),
|
|
stack: error instanceof Error ? error.stack : undefined,
|
|
timestamp: new Date().toISOString(),
|
|
});
|
|
|
|
// Log additional context for debugging
|
|
logger.error(`[Main] 🔍 Debug context:`, {
|
|
routerReady: router.isReady(),
|
|
currentRoute: router.currentRoute.value,
|
|
appMounted: app._instance?.isMounted,
|
|
timestamp: new Date().toISOString(),
|
|
});
|
|
|
|
// Fallback to original error handling
|
|
let message: string =
|
|
error instanceof Error ? error.message : safeStringify(error);
|
|
if (url) {
|
|
message += `\nURL: ${url}`;
|
|
}
|
|
handleApiError({ message } as AxiosError, "deep-link");
|
|
}
|
|
};
|
|
|
|
// Function to register the deeplink listener
|
|
const registerDeepLinkListener = async () => {
|
|
try {
|
|
logger.info(
|
|
`[Main] 🔗 Attempting to register deeplink handler with Capacitor`,
|
|
);
|
|
|
|
// Check if Capacitor App plugin is available
|
|
logger.info(`[Main] 🔍 Checking Capacitor App plugin availability...`);
|
|
if (!CapacitorApp) {
|
|
throw new Error("Capacitor App plugin not available");
|
|
}
|
|
logger.info(`[Main] ✅ Capacitor App plugin is available`);
|
|
|
|
// Check available methods on CapacitorApp
|
|
logger.info(
|
|
`[Main] 🔍 Capacitor App plugin methods:`,
|
|
Object.getOwnPropertyNames(CapacitorApp),
|
|
);
|
|
logger.info(
|
|
`[Main] 🔍 Capacitor App plugin addListener method:`,
|
|
typeof CapacitorApp.addListener,
|
|
);
|
|
|
|
// Wait for router to be ready first
|
|
await router.isReady();
|
|
logger.info(
|
|
`[Main] ✅ Router is ready, proceeding with listener registration`,
|
|
);
|
|
|
|
// Try to register the listener
|
|
logger.info(`[Main] 🧪 Attempting to register appUrlOpen listener...`);
|
|
const listenerHandle = await CapacitorApp.addListener(
|
|
"appUrlOpen",
|
|
handleDeepLink,
|
|
);
|
|
logger.info(
|
|
`[Main] ✅ appUrlOpen listener registered successfully with handle:`,
|
|
listenerHandle,
|
|
);
|
|
|
|
// Test the listener registration by checking if it's actually registered
|
|
logger.info(`[Main] 🧪 Verifying listener registration...`);
|
|
|
|
return listenerHandle;
|
|
} catch (error) {
|
|
logger.error(`[Main] ❌ Failed to register deeplink listener:`, {
|
|
error: error instanceof Error ? error.message : String(error),
|
|
stack: error instanceof Error ? error.stack : undefined,
|
|
timestamp: new Date().toISOString(),
|
|
});
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
logger.log("[Capacitor] 🚀 Mounting app");
|
|
app.mount("#app");
|
|
logger.info(`[Main] ✅ App mounted successfully`);
|
|
|
|
// Register deeplink listener after app is mounted
|
|
setTimeout(async () => {
|
|
try {
|
|
logger.info(
|
|
`[Main] ⏳ Delaying listener registration to ensure Capacitor is ready...`,
|
|
);
|
|
await registerDeepLinkListener();
|
|
logger.info(`[Main] 🎉 Deep link system fully initialized!`);
|
|
} catch (error) {
|
|
logger.error(`[Main] ❌ Deep link system initialization failed:`, error);
|
|
}
|
|
}, 2000); // 2 second delay to ensure Capacitor is fully ready
|
|
|
|
// Log app initialization status
|
|
setTimeout(() => {
|
|
logger.info(`[Main] 📊 App initialization status:`, {
|
|
routerReady: router.isReady(),
|
|
currentRoute: router.currentRoute.value,
|
|
appMounted: app._instance?.isMounted,
|
|
timestamp: new Date().toISOString(),
|
|
});
|
|
}, 1000);
|