You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
185 lines
5.7 KiB
185 lines
5.7 KiB
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<void> {
|
|
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<void> {
|
|
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<void> {
|
|
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<string> {
|
|
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<void> {
|
|
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<string> {
|
|
return ClipboardService.getInstance().readFromClipboard();
|
|
}
|
|
|