- TypeScript: DeepLinkRouter with URL parsing and handler registration - Swift: BLDeepLinkRouter with iOS URL handling and Logger integration - Kotlin: DeepLinkRouter with Android Uri parsing and handler mapping - Common screen constants: broadcasts, surveys, settings, profile, etc.
162 lines
3.7 KiB
TypeScript
162 lines
3.7 KiB
TypeScript
/**
|
|
* Deep Link Router — TypeScript
|
|
* Handles routing from push notification deep links to app screens
|
|
*/
|
|
|
|
export interface DeepLinkRoute {
|
|
screen: string;
|
|
params?: Record<string, string>;
|
|
}
|
|
|
|
export type DeepLinkHandler = (route: DeepLinkRoute) => void;
|
|
|
|
/**
|
|
* Deep Link Router class
|
|
*/
|
|
export class DeepLinkRouter {
|
|
private handlers = new Map<string, DeepLinkHandler>();
|
|
private fallbackHandler?: DeepLinkHandler;
|
|
|
|
/**
|
|
* Register a handler for a specific screen
|
|
*/
|
|
register(screen: string, handler: DeepLinkHandler): void {
|
|
this.handlers.set(screen, handler);
|
|
}
|
|
|
|
/**
|
|
* Set a fallback handler for unregistered screens
|
|
*/
|
|
setFallback(handler: DeepLinkHandler): void {
|
|
this.fallbackHandler = handler;
|
|
}
|
|
|
|
/**
|
|
* Parse a deep link URL and extract route
|
|
*/
|
|
parseDeepLink(url: string): DeepLinkRoute | null {
|
|
try {
|
|
const urlObj = new URL(url);
|
|
|
|
// Handle app-specific URLs: myapp://screen/params
|
|
if (urlObj.protocol !== 'http:' && urlObj.protocol !== 'https:') {
|
|
const pathParts = urlObj.pathname.split('/').filter(Boolean);
|
|
const screen = pathParts[0] || 'home';
|
|
const params: Record<string, string> = {};
|
|
|
|
// Parse query params
|
|
urlObj.searchParams.forEach((value, key) => {
|
|
params[key] = value;
|
|
});
|
|
|
|
return { screen, params };
|
|
}
|
|
|
|
// Handle web URLs with deep link params
|
|
const deepLinkParam = urlObj.searchParams.get('dl');
|
|
if (deepLinkParam) {
|
|
return this.parseDeepLink(deepLinkParam);
|
|
}
|
|
|
|
// Handle path-based routing: /screen/params
|
|
const pathParts = urlObj.pathname.split('/').filter(Boolean);
|
|
if (pathParts.length > 0) {
|
|
const screen = pathParts[0];
|
|
const params: Record<string, string> = {};
|
|
|
|
urlObj.searchParams.forEach((value, key) => {
|
|
params[key] = value;
|
|
});
|
|
|
|
return { screen, params };
|
|
}
|
|
|
|
return null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle a deep link route
|
|
*/
|
|
handle(route: DeepLinkRoute): boolean {
|
|
const handler = this.handlers.get(route.screen);
|
|
|
|
if (handler) {
|
|
handler(route);
|
|
return true;
|
|
}
|
|
|
|
if (this.fallbackHandler) {
|
|
this.fallbackHandler(route);
|
|
return true;
|
|
}
|
|
|
|
console.warn(`[DeepLink] No handler for screen: ${route.screen}`);
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Process a deep link URL end-to-end
|
|
*/
|
|
process(url: string): boolean {
|
|
const route = this.parseDeepLink(url);
|
|
if (!route) {
|
|
console.warn(`[DeepLink] Failed to parse: ${url}`);
|
|
return false;
|
|
}
|
|
return this.handle(route);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a broadcast deep link URL
|
|
*/
|
|
export function createBroadcastDeepLink(
|
|
baseUrl: string,
|
|
screen: string,
|
|
params?: Record<string, string>,
|
|
broadcastId?: string
|
|
): string {
|
|
const url = new URL(baseUrl);
|
|
url.pathname = `/${screen}`;
|
|
|
|
if (params) {
|
|
Object.entries(params).forEach(([key, value]) => {
|
|
url.searchParams.set(key, value);
|
|
});
|
|
}
|
|
|
|
if (broadcastId) {
|
|
url.searchParams.set('broadcastId', broadcastId);
|
|
}
|
|
|
|
return url.toString();
|
|
}
|
|
|
|
/**
|
|
* Common deep link screens for broadcast/survey flows
|
|
*/
|
|
export const DeepLinkScreens = {
|
|
// Broadcasts
|
|
BROADCAST_DETAIL: 'broadcast',
|
|
ANNOUNCEMENTS: 'announcements',
|
|
|
|
// Surveys
|
|
SURVEY: 'survey',
|
|
SURVEY_LIST: 'surveys',
|
|
|
|
// Product-specific (examples)
|
|
SETTINGS: 'settings',
|
|
PROFILE: 'profile',
|
|
UPGRADE: 'upgrade',
|
|
SUPPORT: 'support',
|
|
|
|
// Fallback
|
|
HOME: 'home',
|
|
} as const;
|
|
|
|
// Singleton instance for app-wide use
|
|
export const deepLinkRouter = new DeepLinkRouter();
|