Compare commits

..

11 Commits

Author SHA1 Message Date
Jose Olarte III
504f26190f Merge branch 'master' into ios-contact-copy 2025-08-26 14:35:24 +08:00
Matthew Raymer
1893c2af1b fix: downgrade commitlint strict rules to warnings
- Move commitlint config from package.json to dedicated file
- Change subject-case and subject-full-stop rules from errors to warnings
- Eliminates red error messages on push while maintaining guidance
- Maintains conventional commit standards with non-blocking feedback
- Update BUILDING.md with comprehensive changelog entry
2025-08-26 04:32:32 +00:00
580a485573 Merge pull request 'Refactor: create reusable component version of registration/onboarding notice' (#179) from onboard-alert-component into master
Reviewed-on: #179
2025-08-25 02:53:36 -04:00
Jose Olarte III
5a4bc9efc5 Merge branch 'master' into ios-contact-copy 2025-08-18 15:34:06 +08:00
Jose Olarte III
909d6ff561 Change log level to debug 2025-08-18 15:23:36 +08:00
Jose Olarte III
6d0f4d910f Fix: overloaded arguments + linting 2025-08-13 16:22:15 +08:00
Jose Olarte III
87ebe4ffae Fix: restore newer clipboard service call 2025-08-13 16:05:50 +08:00
Jose Olarte III
47509b1482 Merge branch 'master' into ios-contact-copy 2025-08-13 15:38:16 +08:00
Jose Olarte III
48856d4fce Lint-fix 2025-07-28 16:08:20 +08:00
Jose Olarte III
c3bd22fb83 Fix: iOS copy function
- Update ContactsView, ShareMyContactInfoView, ContactQRScanShowView to use new service
- Replace unreliable web navigator.clipboard with native Capacitor clipboard
- Add comprehensive error handling and logging for clipboard operations
- Sync Capacitor plugins to include clipboard functionality
2025-07-28 16:06:48 +08:00
Jose Olarte III
c18a6b334f Added: native clipboard service
- Install @capacitor/clipboard@^6.0.0 for native clipboard support
- Create platform-agnostic ClipboardService with iOS/Android native API
2025-07-28 15:58:57 +08:00
13 changed files with 278 additions and 42 deletions

View File

@@ -2716,6 +2716,33 @@ configuration files in the repository.
--- ---
### 2025-08-21 - Commitlint Configuration Refinement
#### Commit Message Validation Improvements
- **Modified**: Commitlint configuration moved from `package.json` to dedicated `commitlint.config.js`
- **Enhanced**: Strict validation rules downgraded from errors to warnings
- **Before**: `subject-case` and `subject-full-stop` rules caused red error messages
- **After**: Same rules now show yellow warnings without blocking commits
- **Benefit**: Eliminates confusing red error messages while maintaining commit quality guidance
#### Configuration Structure
- **File**: `commitlint.config.js` - Dedicated commitlint configuration
- **Extends**: `@commitlint/config-conventional` - Standard conventional commit rules
- **Custom Rules**:
- `subject-case: [1, 'never', ['sentence-case', 'start-case', 'pascal-case', 'upper-case']]`
- `subject-full-stop: [1, 'never', '.']`
- **Levels**:
- `0` = Disabled, `1` = Warning, `2` = Error
- Current: Problematic rules set to warning level (1)
#### User Experience Impact
- **Before**: Red error messages on every push with strict commit rules
- **After**: Yellow warning messages that provide guidance without disruption
- **Workflow**: Commits and pushes continue to work while maintaining quality standards
- **Feedback**: Developers still receive helpful commit message guidance
---
**Note**: This documentation is maintained alongside the build system. For the **Note**: This documentation is maintained alongside the build system. For the
most up-to-date information, refer to the actual script files and Vite most up-to-date information, refer to the actual script files and Vite
configuration files in the repository. configuration files in the repository.

View File

@@ -13,6 +13,7 @@ dependencies {
implementation project(':capacitor-mlkit-barcode-scanning') implementation project(':capacitor-mlkit-barcode-scanning')
implementation project(':capacitor-app') implementation project(':capacitor-app')
implementation project(':capacitor-camera') implementation project(':capacitor-camera')
implementation project(':capacitor-clipboard')
implementation project(':capacitor-filesystem') implementation project(':capacitor-filesystem')
implementation project(':capacitor-share') implementation project(':capacitor-share')
implementation project(':capawesome-capacitor-file-picker') implementation project(':capawesome-capacitor-file-picker')

View File

@@ -15,6 +15,10 @@
"pkg": "@capacitor/camera", "pkg": "@capacitor/camera",
"classpath": "com.capacitorjs.plugins.camera.CameraPlugin" "classpath": "com.capacitorjs.plugins.camera.CameraPlugin"
}, },
{
"pkg": "@capacitor/clipboard",
"classpath": "com.capacitorjs.plugins.clipboard.ClipboardPlugin"
},
{ {
"pkg": "@capacitor/filesystem", "pkg": "@capacitor/filesystem",
"classpath": "com.capacitorjs.plugins.filesystem.FilesystemPlugin" "classpath": "com.capacitorjs.plugins.filesystem.FilesystemPlugin"

View File

@@ -14,6 +14,9 @@ project(':capacitor-app').projectDir = new File('../node_modules/@capacitor/app/
include ':capacitor-camera' include ':capacitor-camera'
project(':capacitor-camera').projectDir = new File('../node_modules/@capacitor/camera/android') project(':capacitor-camera').projectDir = new File('../node_modules/@capacitor/camera/android')
include ':capacitor-clipboard'
project(':capacitor-clipboard').projectDir = new File('../node_modules/@capacitor/clipboard/android')
include ':capacitor-filesystem' include ':capacitor-filesystem'
project(':capacitor-filesystem').projectDir = new File('../node_modules/@capacitor/filesystem/android') project(':capacitor-filesystem').projectDir = new File('../node_modules/@capacitor/filesystem/android')

9
commitlint.config.js Normal file
View File

@@ -0,0 +1,9 @@
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
// Downgrade strict case rules to warnings (level 1) instead of errors (level 2)
// This eliminates red error messages while maintaining helpful guidance
'subject-case': [1, 'never', ['sentence-case', 'start-case', 'pascal-case', 'upper-case']],
'subject-full-stop': [1, 'never', '.'],
}
};

View File

@@ -15,6 +15,7 @@ def capacitor_pods
pod 'CapacitorMlkitBarcodeScanning', :path => '../../node_modules/@capacitor-mlkit/barcode-scanning' pod 'CapacitorMlkitBarcodeScanning', :path => '../../node_modules/@capacitor-mlkit/barcode-scanning'
pod 'CapacitorApp', :path => '../../node_modules/@capacitor/app' pod 'CapacitorApp', :path => '../../node_modules/@capacitor/app'
pod 'CapacitorCamera', :path => '../../node_modules/@capacitor/camera' pod 'CapacitorCamera', :path => '../../node_modules/@capacitor/camera'
pod 'CapacitorClipboard', :path => '../../node_modules/@capacitor/clipboard'
pod 'CapacitorFilesystem', :path => '../../node_modules/@capacitor/filesystem' pod 'CapacitorFilesystem', :path => '../../node_modules/@capacitor/filesystem'
pod 'CapacitorShare', :path => '../../node_modules/@capacitor/share' pod 'CapacitorShare', :path => '../../node_modules/@capacitor/share'
pod 'CapawesomeCapacitorFilePicker', :path => '../../node_modules/@capawesome/capacitor-file-picker' pod 'CapawesomeCapacitorFilePicker', :path => '../../node_modules/@capawesome/capacitor-file-picker'

View File

@@ -5,6 +5,8 @@ PODS:
- Capacitor - Capacitor
- CapacitorCamera (6.1.2): - CapacitorCamera (6.1.2):
- Capacitor - Capacitor
- CapacitorClipboard (6.0.2):
- Capacitor
- CapacitorCommunitySqlite (6.0.2): - CapacitorCommunitySqlite (6.0.2):
- Capacitor - Capacitor
- SQLCipher - SQLCipher
@@ -88,6 +90,7 @@ DEPENDENCIES:
- "Capacitor (from `../../node_modules/@capacitor/ios`)" - "Capacitor (from `../../node_modules/@capacitor/ios`)"
- "CapacitorApp (from `../../node_modules/@capacitor/app`)" - "CapacitorApp (from `../../node_modules/@capacitor/app`)"
- "CapacitorCamera (from `../../node_modules/@capacitor/camera`)" - "CapacitorCamera (from `../../node_modules/@capacitor/camera`)"
- "CapacitorClipboard (from `../../node_modules/@capacitor/clipboard`)"
- "CapacitorCommunitySqlite (from `../../node_modules/@capacitor-community/sqlite`)" - "CapacitorCommunitySqlite (from `../../node_modules/@capacitor-community/sqlite`)"
- "CapacitorCordova (from `../../node_modules/@capacitor/ios`)" - "CapacitorCordova (from `../../node_modules/@capacitor/ios`)"
- "CapacitorFilesystem (from `../../node_modules/@capacitor/filesystem`)" - "CapacitorFilesystem (from `../../node_modules/@capacitor/filesystem`)"
@@ -119,6 +122,8 @@ EXTERNAL SOURCES:
:path: "../../node_modules/@capacitor/app" :path: "../../node_modules/@capacitor/app"
CapacitorCamera: CapacitorCamera:
:path: "../../node_modules/@capacitor/camera" :path: "../../node_modules/@capacitor/camera"
CapacitorClipboard:
:path: "../../node_modules/@capacitor/clipboard"
CapacitorCommunitySqlite: CapacitorCommunitySqlite:
:path: "../../node_modules/@capacitor-community/sqlite" :path: "../../node_modules/@capacitor-community/sqlite"
CapacitorCordova: CapacitorCordova:
@@ -136,6 +141,7 @@ SPEC CHECKSUMS:
Capacitor: c95400d761e376be9da6be5a05f226c0e865cebf Capacitor: c95400d761e376be9da6be5a05f226c0e865cebf
CapacitorApp: e1e6b7d05e444d593ca16fd6d76f2b7c48b5aea7 CapacitorApp: e1e6b7d05e444d593ca16fd6d76f2b7c48b5aea7
CapacitorCamera: 9bc7b005d0e6f1d5f525b8137045b60cffffce79 CapacitorCamera: 9bc7b005d0e6f1d5f525b8137045b60cffffce79
CapacitorClipboard: 4443c3cdb7c77b1533dfe3ff0f9f7756aa8579df
CapacitorCommunitySqlite: 0299d20f4b00c2e6aa485a1d8932656753937b9b CapacitorCommunitySqlite: 0299d20f4b00c2e6aa485a1d8932656753937b9b
CapacitorCordova: 8d93e14982f440181be7304aa9559ca631d77fff CapacitorCordova: 8d93e14982f440181be7304aa9559ca631d77fff
CapacitorFilesystem: 59270a63c60836248812671aa3b15df673fbaf74 CapacitorFilesystem: 59270a63c60836248812671aa3b15df673fbaf74
@@ -157,6 +163,6 @@ SPEC CHECKSUMS:
SQLCipher: 31878d8ebd27e5c96db0b7cb695c96e9f8ad77da SQLCipher: 31878d8ebd27e5c96db0b7cb695c96e9f8ad77da
ZIPFoundation: b8c29ea7ae353b309bc810586181fd073cb3312c ZIPFoundation: b8c29ea7ae353b309bc810586181fd073cb3312c
PODFILE CHECKSUM: f987510f7383b04a1b09ea8472bdadcd88b6c924 PODFILE CHECKSUM: 60f54b19c5a7a07343ab5ba9e5db49019fd86aa0
COCOAPODS: 1.16.2 COCOAPODS: 1.16.2

9
package-lock.json generated
View File

@@ -15,6 +15,7 @@
"@capacitor/app": "^6.0.0", "@capacitor/app": "^6.0.0",
"@capacitor/camera": "^6.0.0", "@capacitor/camera": "^6.0.0",
"@capacitor/cli": "^6.2.0", "@capacitor/cli": "^6.2.0",
"@capacitor/clipboard": "^6.0.2",
"@capacitor/core": "^6.2.0", "@capacitor/core": "^6.2.0",
"@capacitor/filesystem": "^6.0.0", "@capacitor/filesystem": "^6.0.0",
"@capacitor/ios": "^6.2.0", "@capacitor/ios": "^6.2.0",
@@ -2301,6 +2302,14 @@
"url": "https://github.com/sponsors/isaacs" "url": "https://github.com/sponsors/isaacs"
} }
}, },
"node_modules/@capacitor/clipboard": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/@capacitor/clipboard/-/clipboard-6.0.2.tgz",
"integrity": "sha512-jQ6UeFra5NP58THNZNb7HtzOZU7cHsjgrbQGVuMTgsK1uTILZpNeh+pfqHbKggba6KaNh5DAsJvEVQGpIR1VBA==",
"peerDependencies": {
"@capacitor/core": "^6.0.0"
}
},
"node_modules/@capacitor/core": { "node_modules/@capacitor/core": {
"version": "6.2.1", "version": "6.2.1",
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-6.2.1.tgz", "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-6.2.1.tgz",

View File

@@ -136,11 +136,7 @@
"*.{js,ts,vue,css,json,yml,yaml}": "eslint --fix || true", "*.{js,ts,vue,css,json,yml,yaml}": "eslint --fix || true",
"*.{md,markdown,mdc}": "markdownlint-cli2 --fix" "*.{md,markdown,mdc}": "markdownlint-cli2 --fix"
}, },
"commitlint": {
"extends": [
"@commitlint/config-conventional"
]
},
"dependencies": { "dependencies": {
"@capacitor-community/electron": "^5.0.1", "@capacitor-community/electron": "^5.0.1",
"@capacitor-community/sqlite": "6.0.2", "@capacitor-community/sqlite": "6.0.2",
@@ -149,6 +145,7 @@
"@capacitor/app": "^6.0.0", "@capacitor/app": "^6.0.0",
"@capacitor/camera": "^6.0.0", "@capacitor/camera": "^6.0.0",
"@capacitor/cli": "^6.2.0", "@capacitor/cli": "^6.2.0",
"@capacitor/clipboard": "^6.0.2",
"@capacitor/core": "^6.2.0", "@capacitor/core": "^6.2.0",
"@capacitor/filesystem": "^6.0.0", "@capacitor/filesystem": "^6.0.0",
"@capacitor/ios": "^6.2.0", "@capacitor/ios": "^6.2.0",

View File

@@ -0,0 +1,185 @@
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();
}

View File

@@ -140,7 +140,7 @@ import { AxiosError } from "axios";
import { Buffer } from "buffer/"; import { Buffer } from "buffer/";
import QRCodeVue3 from "qr-code-generator-vue3"; import QRCodeVue3 from "qr-code-generator-vue3";
import { Component, Vue } from "vue-facing-decorator"; import { Component, Vue } from "vue-facing-decorator";
import { useClipboard } from "@vueuse/core";
import { QrcodeStream } from "vue-qrcode-reader"; import { QrcodeStream } from "vue-qrcode-reader";
import QuickNav from "../components/QuickNav.vue"; import QuickNav from "../components/QuickNav.vue";
@@ -183,8 +183,6 @@ import {
NOTIFY_QR_PROCESSING_ERROR, NOTIFY_QR_PROCESSING_ERROR,
createQRContactAddedMessage, createQRContactAddedMessage,
createQRRegistrationSuccessMessage, createQRRegistrationSuccessMessage,
QR_TIMEOUT_SHORT,
QR_TIMEOUT_MEDIUM,
QR_TIMEOUT_STANDARD, QR_TIMEOUT_STANDARD,
QR_TIMEOUT_LONG, QR_TIMEOUT_LONG,
} from "@/constants/notifications"; } from "@/constants/notifications";
@@ -544,11 +542,7 @@ export default class ContactQRScanShow extends Vue {
did: contact.did, did: contact.did,
name: contact.name, name: contact.name,
}); });
this.notify.toast( this.notify.toast("Submitted", NOTIFY_QR_REGISTRATION_SUBMITTED.message);
"Submitted",
NOTIFY_QR_REGISTRATION_SUBMITTED.message,
QR_TIMEOUT_SHORT,
);
try { try {
const regResult = await register( const regResult = await register(
@@ -624,18 +618,15 @@ export default class ContactQRScanShow extends Vue {
); );
// Copy the URL to clipboard // Copy the URL to clipboard
useClipboard() const { copyToClipboard } = await import("../services/ClipboardService");
.copy(jwtUrl) await copyToClipboard(jwtUrl);
.then(() => { this.notify.toast(
this.notify.toast( NOTIFY_QR_URL_COPIED.title,
"Copied", NOTIFY_QR_URL_COPIED.message,
NOTIFY_QR_URL_COPIED.message, );
QR_TIMEOUT_MEDIUM,
);
});
} catch (error) { } catch (error) {
logger.error("Failed to generate contact URL:", error); this.$logAndConsole(`Error copying URL to clipboard: ${error}`, true);
this.notify.error("Failed to generate contact URL. Please try again."); this.notify.error("Failed to copy URL to clipboard.");
} }
} }
@@ -643,13 +634,16 @@ export default class ContactQRScanShow extends Vue {
this.notify.info(NOTIFY_QR_CODE_HELP.message, QR_TIMEOUT_LONG); this.notify.info(NOTIFY_QR_CODE_HELP.message, QR_TIMEOUT_LONG);
} }
onCopyDidToClipboard() { async onCopyDidToClipboard() {
//this.onScanDetect([{ rawValue: this.qrValue }]); // good for testing //this.onScanDetect([{ rawValue: this.qrValue }]); // good for testing
useClipboard() try {
.copy(this.activeDid) const { copyToClipboard } = await import("../services/ClipboardService");
.then(() => { await copyToClipboard(this.activeDid);
this.notify.info(NOTIFY_QR_DID_COPIED.message, QR_TIMEOUT_LONG); this.notify.info(NOTIFY_QR_DID_COPIED.message, QR_TIMEOUT_LONG);
}); } catch (error) {
this.$logAndConsole(`Error copying DID to clipboard: ${error}`, true);
this.notify.error("Failed to copy DID to clipboard.");
}
} }
openUserNameDialog() { openUserNameDialog() {
@@ -745,7 +739,6 @@ export default class ContactQRScanShow extends Vue {
) { ) {
setTimeout(() => { setTimeout(() => {
this.notify.confirm( this.notify.confirm(
"Register",
"Do you want to register them?", "Do you want to register them?",
{ {
onCancel: async (stopAsking?: boolean) => { onCancel: async (stopAsking?: boolean) => {

View File

@@ -130,10 +130,9 @@ import { JWTPayload } from "did-jwt";
import * as R from "ramda"; import * as R from "ramda";
import { Component, Vue } from "vue-facing-decorator"; import { Component, Vue } from "vue-facing-decorator";
import { RouteLocationNormalizedLoaded, Router } from "vue-router"; import { RouteLocationNormalizedLoaded, Router } from "vue-router";
import { useClipboard } from "@vueuse/core";
// Capacitor import removed - using PlatformService instead
import QuickNav from "../components/QuickNav.vue"; import QuickNav from "../components/QuickNav.vue";
import { copyToClipboard } from "../services/ClipboardService";
import EntityIcon from "../components/EntityIcon.vue"; import EntityIcon from "../components/EntityIcon.vue";
import GiftedDialog from "../components/GiftedDialog.vue"; import GiftedDialog from "../components/GiftedDialog.vue";
import OfferDialog from "../components/OfferDialog.vue"; import OfferDialog from "../components/OfferDialog.vue";
@@ -1192,12 +1191,14 @@ export default class ContactsView extends Vue {
}); });
// Use production URL for sharing to avoid localhost issues in development // Use production URL for sharing to avoid localhost issues in development
const contactsJwtUrl = `${APP_SERVER}/deep-link/contact-import/${contactsJwt}`; const contactsJwtUrl = `${APP_SERVER}/deep-link/contact-import/${contactsJwt}`;
useClipboard() try {
.copy(contactsJwtUrl) await copyToClipboard(contactsJwtUrl);
.then(() => { // Use notification helper
// Use notification helper this.notify.copied(NOTIFY_CONTACT_LINK_COPIED.message);
this.notify.copied(NOTIFY_CONTACT_LINK_COPIED.message); } catch (error) {
}); this.$logAndConsole(`Error copying to clipboard: ${error}`, true);
this.notify.error("Failed to copy to clipboard. Please try again.");
}
} }
private showCopySelectionsInfo() { private showCopySelectionsInfo() {

View File

@@ -144,8 +144,8 @@ export default class ShareMyContactInfoView extends Vue {
* Copy the contact message to clipboard * Copy the contact message to clipboard
*/ */
private async copyToClipboard(message: string): Promise<void> { private async copyToClipboard(message: string): Promise<void> {
const { useClipboard } = await import("@vueuse/core"); const { copyToClipboard } = await import("../services/ClipboardService");
await useClipboard().copy(message); await copyToClipboard(message);
} }
/** /**