import { Capacitor } from "@capacitor/core"; import { Clipboard } from "@capacitor/clipboard"; import { useClipboard } from "@vueuse/core"; import { logger } from "@/utils/logger"; /** * Platform-agnostic clipboard service that handles both web and native platforms * Provides reliable clipboard functionality across all platforms including iOS */ export class ClipboardService { private static instance: ClipboardService | null = null; /** * Get singleton instance of ClipboardService */ public static getInstance(): ClipboardService { if (!ClipboardService.instance) { ClipboardService.instance = new ClipboardService(); } return ClipboardService.instance; } /** * Copy text to clipboard with platform-specific handling * * @param text - The text to copy to clipboard * @returns Promise that resolves when copy is complete * @throws Error if copy operation fails */ public async copyToClipboard(text: string): Promise { const platform = Capacitor.getPlatform(); const isNative = Capacitor.isNativePlatform(); logger.debug("[ClipboardService] Copying to clipboard:", { text: text.substring(0, 50) + (text.length > 50 ? "..." : ""), platform, isNative, timestamp: new Date().toISOString(), }); try { if (isNative && (platform === "ios" || platform === "android")) { // Use native Capacitor clipboard for mobile platforms await this.copyNative(text); } else { // Use web clipboard API for web/desktop platforms await this.copyWeb(text); } logger.debug("[ClipboardService] Copy successful", { platform, timestamp: new Date().toISOString(), }); } catch (error) { logger.error("[ClipboardService] Copy failed:", { error: error instanceof Error ? error.message : String(error), platform, timestamp: new Date().toISOString(), }); throw error; } } /** * Copy text using native Capacitor clipboard API * * @param text - The text to copy * @returns Promise that resolves when copy is complete */ private async copyNative(text: string): Promise { try { await Clipboard.write({ string: text, }); } catch (error) { logger.error("[ClipboardService] Native copy failed:", { error: error instanceof Error ? error.message : String(error), timestamp: new Date().toISOString(), }); throw new Error( `Native clipboard copy failed: ${error instanceof Error ? error.message : String(error)}`, ); } } /** * Copy text using web clipboard API with fallback * * @param text - The text to copy * @returns Promise that resolves when copy is complete */ private async copyWeb(text: string): Promise { try { // Try VueUse clipboard first (handles some edge cases) const { copy } = useClipboard(); await copy(text); } catch (error) { logger.warn( "[ClipboardService] VueUse clipboard failed, trying native API:", { error: error instanceof Error ? error.message : String(error), timestamp: new Date().toISOString(), }, ); // Fallback to native navigator.clipboard if (navigator.clipboard && navigator.clipboard.writeText) { await navigator.clipboard.writeText(text); } else { throw new Error("Clipboard API not supported in this browser"); } } } /** * Read text from clipboard (platform-specific) * * @returns Promise that resolves to the clipboard text * @throws Error if read operation fails */ public async readFromClipboard(): Promise { const platform = Capacitor.getPlatform(); const isNative = Capacitor.isNativePlatform(); try { if (isNative && (platform === "ios" || platform === "android")) { // Use native Capacitor clipboard for mobile platforms const result = await Clipboard.read(); return result.value || ""; } else { // Use web clipboard API for web/desktop platforms if (navigator.clipboard && navigator.clipboard.readText) { return await navigator.clipboard.readText(); } else { throw new Error("Clipboard read API not supported in this browser"); } } } catch (error) { logger.error("[ClipboardService] Read from clipboard failed:", { error: error instanceof Error ? error.message : String(error), platform, timestamp: new Date().toISOString(), }); throw error; } } /** * Check if clipboard is supported on current platform * * @returns boolean indicating if clipboard is supported */ public isSupported(): boolean { const platform = Capacitor.getPlatform(); const isNative = Capacitor.isNativePlatform(); if (isNative && (platform === "ios" || platform === "android")) { return true; // Capacitor clipboard should work on native platforms } // Check web clipboard support return !!(navigator.clipboard && navigator.clipboard.writeText); } } /** * Convenience function to copy text to clipboard * Uses the singleton ClipboardService instance * * @param text - The text to copy to clipboard * @returns Promise that resolves when copy is complete */ export async function copyToClipboard(text: string): Promise { return ClipboardService.getInstance().copyToClipboard(text); } /** * Convenience function to read text from clipboard * Uses the singleton ClipboardService instance * * @returns Promise that resolves to the clipboard text */ export async function readFromClipboard(): Promise { return ClipboardService.getInstance().readFromClipboard(); }