Browse Source

feat: migrate all clipboard operations from useClipboard to ClipboardService

- Replace useClipboard with platform-agnostic ClipboardService across 13 files
- Add proper error handling with user notifications for all clipboard operations
- Fix naming conflicts between method names and imported function names
- Ensure consistent async/await patterns throughout the codebase
- Add notification system to HelpView.vue for user feedback on clipboard errors
- Remove unnecessary wrapper methods for cleaner code

Files migrated:
- View components: UserProfileView, QuickActionBvcEndView, ProjectViewView,
  InviteOneView, SeedBackupView, HelpView, AccountViewView, DatabaseMigration,
  ConfirmGiftView, ClaimView, OnboardMeetingSetupView
- Utility functions: libs/util.ts (doCopyTwoSecRedo)
- Components: HiddenDidDialog

Naming conflicts resolved:
- DatabaseMigration: copyToClipboard() → copyExportedDataToClipboard()
- ShareMyContactInfoView: copyToClipboard() → copyContactMessageToClipboard() → removed
- HiddenDidDialog: copyToClipboard() → copyTextToClipboard()
- ClaimView: copyToClipboard() → copyTextToClipboard()
- ConfirmGiftView: copyToClipboard() → copyTextToClipboard()

This migration ensures reliable clipboard functionality across iOS, Android,
and web platforms with proper error handling and user feedback.

Closes: Platform-specific clipboard issues on mobile devices
pull/199/head
Jose Olarte III 5 days ago
parent
commit
4c218c4786
  1. 29
      src/components/HiddenDidDialog.vue
  2. 18
      src/libs/util.ts
  3. 14
      src/views/AccountViewView.vue
  4. 31
      src/views/ClaimView.vue
  5. 43
      src/views/ConfirmGiftView.vue
  6. 3
      src/views/ContactQRScanFullView.vue
  7. 3
      src/views/ContactQRScanShowView.vue
  8. 12
      src/views/DatabaseMigration.vue
  9. 26
      src/views/HelpView.vue
  10. 30
      src/views/InviteOneView.vue
  11. 19
      src/views/OnboardMeetingSetupView.vue
  12. 16
      src/views/ProjectViewView.vue
  13. 32
      src/views/QuickActionBvcEndView.vue
  14. 14
      src/views/SeedBackupView.vue
  15. 11
      src/views/ShareMyContactInfoView.vue
  16. 16
      src/views/UserProfileView.vue

29
src/components/HiddenDidDialog.vue

@ -74,7 +74,7 @@
If you'd like an introduction, If you'd like an introduction,
<a <a
class="text-blue-500" class="text-blue-500"
@click="copyToClipboard('A link to this page', deepLinkUrl)" @click="copyTextToClipboard('A link to this page', deepLinkUrl)"
>click here to copy this page, paste it into a message, and ask if >click here to copy this page, paste it into a message, and ask if
they'll tell you more about the {{ roleName }}.</a they'll tell you more about the {{ roleName }}.</a
> >
@ -110,7 +110,7 @@
* @since 2024-12-19 * @since 2024-12-19
*/ */
import { Component, Vue } from "vue-facing-decorator"; import { Component, Vue } from "vue-facing-decorator";
import { useClipboard } from "@vueuse/core"; import { copyToClipboard } from "../services/ClipboardService";
import * as R from "ramda"; import * as R from "ramda";
import * as serverUtil from "../libs/endorserServer"; import * as serverUtil from "../libs/endorserServer";
import { Contact } from "../db/tables/contacts"; import { Contact } from "../db/tables/contacts";
@ -197,19 +197,24 @@ export default class HiddenDidDialog extends Vue {
); );
} }
copyToClipboard(name: string, text: string) { async copyTextToClipboard(name: string, text: string) {
useClipboard() try {
.copy(text) await copyToClipboard(text);
.then(() => { this.notify.success(
this.notify.success( NOTIFY_COPIED_TO_CLIPBOARD.message(name || "That"),
NOTIFY_COPIED_TO_CLIPBOARD.message(name || "That"), TIMEOUTS.SHORT,
TIMEOUTS.SHORT, );
); } catch (error) {
}); this.$logAndConsole(
`Error copying ${name || "content"} to clipboard: ${error}`,
true,
);
this.notify.error(`Failed to copy ${name || "content"} to clipboard.`);
}
} }
onClickShareClaim() { onClickShareClaim() {
this.copyToClipboard("A link to this page", this.deepLinkUrl); this.copyTextToClipboard("A link to this page", this.deepLinkUrl);
window.navigator.share({ window.navigator.share({
title: "Help Connect Me", title: "Help Connect Me",
text: "I'm trying to find the people who recorded this. Can you help me?", text: "I'm trying to find the people who recorded this. Can you help me?",

18
src/libs/util.ts

@ -3,7 +3,7 @@
import axios, { AxiosResponse } from "axios"; import axios, { AxiosResponse } from "axios";
import { Buffer } from "buffer"; import { Buffer } from "buffer";
import * as R from "ramda"; import * as R from "ramda";
import { useClipboard } from "@vueuse/core"; import { copyToClipboard } from "../services/ClipboardService";
import { DEFAULT_PUSH_SERVER, NotificationIface } from "../constants/app"; import { DEFAULT_PUSH_SERVER, NotificationIface } from "../constants/app";
import { Account, AccountEncrypted } from "../db/tables/accounts"; import { Account, AccountEncrypted } from "../db/tables/accounts";
@ -232,11 +232,19 @@ export const nameForContact = (
); );
}; };
export const doCopyTwoSecRedo = (text: string, fn: () => void) => { export const doCopyTwoSecRedo = async (
text: string,
fn: () => void,
): Promise<void> => {
fn(); fn();
useClipboard() try {
.copy(text) await copyToClipboard(text);
.then(() => setTimeout(fn, 2000)); setTimeout(fn, 2000);
} catch (error) {
// Note: This utility function doesn't have access to notification system
// The calling component should handle error notifications
// Error is silently caught to avoid breaking the 2-second redo pattern
}
}; };
export interface ConfirmerData { export interface ConfirmerData {

14
src/views/AccountViewView.vue

@ -764,7 +764,7 @@ import { IIdentifier } from "@veramo/core";
import { ref } from "vue"; import { ref } from "vue";
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"; import { copyToClipboard } from "../services/ClipboardService";
import { LMap, LMarker, LTileLayer } from "@vue-leaflet/vue-leaflet"; import { LMap, LMarker, LTileLayer } from "@vue-leaflet/vue-leaflet";
import { Capacitor } from "@capacitor/core"; import { Capacitor } from "@capacitor/core";
@ -1084,11 +1084,15 @@ export default class AccountViewView extends Vue {
} }
// call fn, copy text to the clipboard, then redo fn after 2 seconds // call fn, copy text to the clipboard, then redo fn after 2 seconds
doCopyTwoSecRedo(text: string, fn: () => void): void { async doCopyTwoSecRedo(text: string, fn: () => void): Promise<void> {
fn(); fn();
useClipboard() try {
.copy(text) await copyToClipboard(text);
.then(() => setTimeout(fn, 2000)); setTimeout(fn, 2000);
} catch (error) {
this.$logAndConsole(`Error copying to clipboard: ${error}`, true);
this.notify.error("Failed to copy to clipboard.");
}
} }
async toggleShowContactAmounts(): Promise<void> { async toggleShowContactAmounts(): Promise<void> {

31
src/views/ClaimView.vue

@ -58,7 +58,7 @@
title="Copy Printable Certificate Link" title="Copy Printable Certificate Link"
aria-label="Copy printable certificate link" aria-label="Copy printable certificate link"
@click=" @click="
copyToClipboard( copyTextToClipboard(
'A link to the certificate page', 'A link to the certificate page',
`${APP_SERVER}/deep-link/claim-cert/${veriClaim.id}`, `${APP_SERVER}/deep-link/claim-cert/${veriClaim.id}`,
) )
@ -72,7 +72,9 @@
<button <button
title="Copy Link" title="Copy Link"
aria-label="Copy page link" aria-label="Copy page link"
@click="copyToClipboard('A link to this page', windowDeepLink)" @click="
copyTextToClipboard('A link to this page', windowDeepLink)
"
> >
<font-awesome icon="link" class="text-slate-500" /> <font-awesome icon="link" class="text-slate-500" />
</button> </button>
@ -399,7 +401,7 @@
contacts can see more details: contacts can see more details:
<a <a
class="text-blue-500" class="text-blue-500"
@click="copyToClipboard('A link to this page', windowDeepLink)" @click="copyTextToClipboard('A link to this page', windowDeepLink)"
>click to copy this page info</a >click to copy this page info</a
> >
and see if they can make an introduction. Someone is connected to and see if they can make an introduction. Someone is connected to
@ -422,7 +424,7 @@
If you'd like an introduction, If you'd like an introduction,
<a <a
class="text-blue-500" class="text-blue-500"
@click="copyToClipboard('A link to this page', windowDeepLink)" @click="copyTextToClipboard('A link to this page', windowDeepLink)"
>share this page with them and ask if they'll tell you more about >share this page with them and ask if they'll tell you more about
about the participants.</a about the participants.</a
> >
@ -532,7 +534,7 @@ import * as yaml from "js-yaml";
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 { Router, RouteLocationNormalizedLoaded } from "vue-router"; import { Router, RouteLocationNormalizedLoaded } from "vue-router";
import { useClipboard } from "@vueuse/core"; import { copyToClipboard } from "../services/ClipboardService";
import { GenericVerifiableCredential } from "../interfaces"; import { GenericVerifiableCredential } from "../interfaces";
import GiftedDialog from "../components/GiftedDialog.vue"; import GiftedDialog from "../components/GiftedDialog.vue";
import QuickNav from "../components/QuickNav.vue"; import QuickNav from "../components/QuickNav.vue";
@ -1129,16 +1131,21 @@ export default class ClaimView extends Vue {
); );
} }
copyToClipboard(name: string, text: string) { async copyTextToClipboard(name: string, text: string) {
useClipboard() try {
.copy(text) await copyToClipboard(text);
.then(() => { this.notify.copied(name || "That");
this.notify.copied(name || "That"); } catch (error) {
}); this.$logAndConsole(
`Error copying ${name || "content"} to clipboard: ${error}`,
true,
);
this.notify.error(`Failed to copy ${name || "content"} to clipboard.`);
}
} }
onClickShareClaim() { onClickShareClaim() {
this.copyToClipboard("A link to this page", this.windowDeepLink); this.copyTextToClipboard("A link to this page", this.windowDeepLink);
window.navigator.share({ window.navigator.share({
title: "Help Connect Me", title: "Help Connect Me",
text: "I'm trying to find the people who recorded this. Can you help me?", text: "I'm trying to find the people who recorded this. Can you help me?",

43
src/views/ConfirmGiftView.vue

@ -192,7 +192,7 @@
<span v-if="!serverUtil.isEmptyOrHiddenDid(confirmerId)"> <span v-if="!serverUtil.isEmptyOrHiddenDid(confirmerId)">
<button <button
@click=" @click="
copyToClipboard( copyTextToClipboard(
'The DID of ' + confirmerId, 'The DID of ' + confirmerId,
confirmerId, confirmerId,
) )
@ -238,7 +238,7 @@
> >
<button <button
@click=" @click="
copyToClipboard( copyTextToClipboard(
'The DID of ' + confsVisibleTo, 'The DID of ' + confsVisibleTo,
confsVisibleTo, confsVisibleTo,
) )
@ -309,7 +309,9 @@
contacts can see more details: contacts can see more details:
<a <a
class="text-blue-500" class="text-blue-500"
@click="copyToClipboard('A link to this page', windowLocation)" @click="
copyTextToClipboard('A link to this page', windowLocation)
"
>click to copy this page info</a >click to copy this page info</a
> >
and see if they can make an introduction. Someone is connected to and see if they can make an introduction. Someone is connected to
@ -332,7 +334,9 @@
If you'd like an introduction, If you'd like an introduction,
<a <a
class="text-blue-500" class="text-blue-500"
@click="copyToClipboard('A link to this page', windowLocation)" @click="
copyTextToClipboard('A link to this page', windowLocation)
"
>share this page with them and ask if they'll tell you more about >share this page with them and ask if they'll tell you more about
about the participants.</a about the participants.</a
> >
@ -360,7 +364,7 @@
<span v-if="!serverUtil.isEmptyOrHiddenDid(visDid)"> <span v-if="!serverUtil.isEmptyOrHiddenDid(visDid)">
<button <button
@click=" @click="
copyToClipboard('The DID of ' + visDid, visDid) copyTextToClipboard('The DID of ' + visDid, visDid)
" "
> >
<font-awesome <font-awesome
@ -433,7 +437,7 @@
import * as yaml from "js-yaml"; import * as yaml from "js-yaml";
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 { useClipboard } from "@vueuse/core"; import { copyToClipboard } from "../services/ClipboardService";
import { RouteLocationNormalizedLoaded, Router } from "vue-router"; import { RouteLocationNormalizedLoaded, Router } from "vue-router";
import QuickNav from "../components/QuickNav.vue"; import QuickNav from "../components/QuickNav.vue";
import { NotificationIface } from "../constants/app"; import { NotificationIface } from "../constants/app";
@ -779,16 +783,21 @@ export default class ConfirmGiftView extends Vue {
* @param description - Description of copied content * @param description - Description of copied content
* @param text - Text to copy * @param text - Text to copy
*/ */
copyToClipboard(description: string, text: string): void { async copyTextToClipboard(description: string, text: string): Promise<void> {
useClipboard() try {
.copy(text) await copyToClipboard(text);
.then(() => { this.notify.toast(
this.notify.toast( NOTIFY_COPIED_TO_CLIPBOARD.title,
NOTIFY_COPIED_TO_CLIPBOARD.title, NOTIFY_COPIED_TO_CLIPBOARD.message(description),
NOTIFY_COPIED_TO_CLIPBOARD.message(description), TIMEOUTS.SHORT,
TIMEOUTS.SHORT, );
); } catch (error) {
}); this.$logAndConsole(
`Error copying ${description} to clipboard: ${error}`,
true,
);
this.notify.error(`Failed to copy ${description} to clipboard.`);
}
} }
/** /**
@ -870,7 +879,7 @@ export default class ConfirmGiftView extends Vue {
* Handles share functionality based on platform capabilities * Handles share functionality based on platform capabilities
*/ */
async onClickShareClaim(): Promise<void> { async onClickShareClaim(): Promise<void> {
this.copyToClipboard("A link to this page", this.windowLocation); this.copyTextToClipboard("A link to this page", this.windowLocation);
window.navigator.share({ window.navigator.share({
title: "Help Connect Me", title: "Help Connect Me",
text: "I'm trying to find the full details of this claim. Can you help me?", text: "I'm trying to find the full details of this claim. Can you help me?",

3
src/views/ContactQRScanFullView.vue

@ -104,6 +104,7 @@ 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 { Router } from "vue-router"; import { Router } from "vue-router";
import { copyToClipboard } from "../services/ClipboardService";
import { logger } from "../utils/logger"; import { logger } from "../utils/logger";
import { QRScannerFactory } from "../services/QRScanner/QRScannerFactory"; import { QRScannerFactory } from "../services/QRScanner/QRScannerFactory";
@ -661,7 +662,6 @@ export default class ContactQRScanFull extends Vue {
); );
// Use the platform-specific ClipboardService for reliable iOS support // Use the platform-specific ClipboardService for reliable iOS support
const { copyToClipboard } = await import("../services/ClipboardService");
await copyToClipboard(jwtUrl); await copyToClipboard(jwtUrl);
this.notify.toast( this.notify.toast(
@ -684,7 +684,6 @@ export default class ContactQRScanFull extends Vue {
async onCopyDidToClipboard() { async onCopyDidToClipboard() {
try { try {
// Use the platform-specific ClipboardService for reliable iOS support // Use the platform-specific ClipboardService for reliable iOS support
const { copyToClipboard } = await import("../services/ClipboardService");
await copyToClipboard(this.activeDid); 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);

3
src/views/ContactQRScanShowView.vue

@ -140,6 +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 { copyToClipboard } from "../services/ClipboardService";
import { QrcodeStream } from "vue-qrcode-reader"; import { QrcodeStream } from "vue-qrcode-reader";
@ -628,7 +629,6 @@ export default class ContactQRScanShow extends Vue {
); );
// Copy the URL to clipboard // Copy the URL to clipboard
const { copyToClipboard } = await import("../services/ClipboardService");
await copyToClipboard(jwtUrl); await copyToClipboard(jwtUrl);
this.notify.toast( this.notify.toast(
NOTIFY_QR_URL_COPIED.title, NOTIFY_QR_URL_COPIED.title,
@ -647,7 +647,6 @@ export default class ContactQRScanShow extends Vue {
async onCopyDidToClipboard() { async onCopyDidToClipboard() {
//this.onScanDetect([{ rawValue: this.qrValue }]); // good for testing //this.onScanDetect([{ rawValue: this.qrValue }]); // good for testing
try { try {
const { copyToClipboard } = await import("../services/ClipboardService");
await copyToClipboard(this.activeDid); 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) { } catch (error) {

12
src/views/DatabaseMigration.vue

@ -1003,7 +1003,7 @@
<h2>Exported Data</h2> <h2>Exported Data</h2>
<span <span
class="text-blue-500 cursor-pointer hover:text-blue-700" class="text-blue-500 cursor-pointer hover:text-blue-700"
@click="copyToClipboard" @click="copyExportedDataToClipboard"
> >
Copy to Clipboard Copy to Clipboard
</span> </span>
@ -1014,7 +1014,7 @@
<script lang="ts"> <script lang="ts">
import { Component, Vue } from "vue-facing-decorator"; import { Component, Vue } from "vue-facing-decorator";
import { useClipboard } from "@vueuse/core"; import { copyToClipboard } from "../services/ClipboardService";
import { Router } from "vue-router"; import { Router } from "vue-router";
import { import {
@ -1072,8 +1072,6 @@ export default class DatabaseMigration extends Vue {
private exportedData: Record<string, any> | null = null; private exportedData: Record<string, any> | null = null;
private successMessage = ""; private successMessage = "";
useClipboard = useClipboard;
/** /**
* Computed property to get the display name for a setting * Computed property to get the display name for a setting
* Handles both live comparison data and exported JSON format * Handles both live comparison data and exported JSON format
@ -1133,13 +1131,11 @@ export default class DatabaseMigration extends Vue {
/** /**
* Copies exported data to clipboard and shows success message * Copies exported data to clipboard and shows success message
*/ */
async copyToClipboard(): Promise<void> { async copyExportedDataToClipboard(): Promise<void> {
if (!this.exportedData) return; if (!this.exportedData) return;
try { try {
await this.useClipboard().copy( await copyToClipboard(JSON.stringify(this.exportedData, null, 2));
JSON.stringify(this.exportedData, null, 2),
);
// Use global window object properly // Use global window object properly
if (typeof window !== "undefined") { if (typeof window !== "undefined") {
window.alert("Copied to clipboard!"); window.alert("Copied to clipboard!");

26
src/views/HelpView.vue

@ -584,15 +584,16 @@
<script lang="ts"> <script lang="ts">
import { Component, Vue } from "vue-facing-decorator"; import { Component, Vue } from "vue-facing-decorator";
import { Router } from "vue-router"; import { Router } from "vue-router";
import { useClipboard } from "@vueuse/core"; import { copyToClipboard } from "../services/ClipboardService";
// Capacitor import removed - using QRNavigationService instead // Capacitor import removed - using QRNavigationService instead
import * as Package from "../../package.json"; import * as Package from "../../package.json";
import QuickNav from "../components/QuickNav.vue"; import QuickNav from "../components/QuickNav.vue";
import { APP_SERVER } from "../constants/app"; import { APP_SERVER, NotificationIface } from "../constants/app";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { QRNavigationService } from "@/services/QRNavigationService"; import { QRNavigationService } from "@/services/QRNavigationService";
import { UNNAMED_ENTITY_NAME } from "@/constants/entities"; import { UNNAMED_ENTITY_NAME } from "@/constants/entities";
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
/** /**
* HelpView.vue - Comprehensive Help System Component * HelpView.vue - Comprehensive Help System Component
@ -626,8 +627,10 @@ import { UNNAMED_ENTITY_NAME } from "@/constants/entities";
}) })
export default class HelpView extends Vue { export default class HelpView extends Vue {
$router!: Router; $router!: Router;
$notify!: (notification: NotificationIface, timeout?: number) => void;
package = Package; package = Package;
notify!: ReturnType<typeof createNotifyHelpers>;
commitHash = import.meta.env.VITE_GIT_HASH; commitHash = import.meta.env.VITE_GIT_HASH;
showAlpha = false; showAlpha = false;
showBasics = false; showBasics = false;
@ -640,6 +643,13 @@ export default class HelpView extends Vue {
APP_SERVER = APP_SERVER; APP_SERVER = APP_SERVER;
// Capacitor reference removed - using QRNavigationService instead // Capacitor reference removed - using QRNavigationService instead
/**
* Initialize notification helpers
*/
created() {
this.notify = createNotifyHelpers(this.$notify);
}
/** /**
* Get the unnamed entity name constant * Get the unnamed entity name constant
*/ */
@ -660,11 +670,15 @@ export default class HelpView extends Vue {
* @param {string} text - The text to copy to clipboard * @param {string} text - The text to copy to clipboard
* @param {Function} fn - Callback function to execute before and after copying * @param {Function} fn - Callback function to execute before and after copying
*/ */
doCopyTwoSecRedo(text: string, fn: () => void): void { async doCopyTwoSecRedo(text: string, fn: () => void): Promise<void> {
fn(); fn();
useClipboard() try {
.copy(text) await copyToClipboard(text);
.then(() => setTimeout(fn, 2000)); setTimeout(fn, 2000);
} catch (error) {
this.$logAndConsole(`Error copying to clipboard: ${error}`, true);
this.notify.error("Failed to copy to clipboard.", TIMEOUTS.SHORT);
}
} }
/** /**

30
src/views/InviteOneView.vue

@ -128,7 +128,7 @@
<script lang="ts"> <script lang="ts">
import axios from "axios"; import axios from "axios";
import { Component, Vue } from "vue-facing-decorator"; import { Component, Vue } from "vue-facing-decorator";
import { useClipboard } from "@vueuse/core"; import { copyToClipboard } from "../services/ClipboardService";
import { Router } from "vue-router"; import { Router } from "vue-router";
import ContactNameDialog from "../components/ContactNameDialog.vue"; import ContactNameDialog from "../components/ContactNameDialog.vue";
@ -333,17 +333,27 @@ export default class InviteOneView extends Vue {
return `${APP_SERVER}/deep-link/invite-one-accept/${jwt}`; return `${APP_SERVER}/deep-link/invite-one-accept/${jwt}`;
} }
copyInviteAndNotify(inviteId: string, jwt: string) { async copyInviteAndNotify(inviteId: string, jwt: string) {
useClipboard().copy(this.inviteLink(jwt)); try {
this.notify.success(createInviteLinkCopyMessage(inviteId), TIMEOUTS.LONG); await copyToClipboard(this.inviteLink(jwt));
this.notify.success(createInviteLinkCopyMessage(inviteId), TIMEOUTS.LONG);
} catch (error) {
this.$logAndConsole(`Error copying invite link: ${error}`, true);
this.notify.error("Failed to copy invite link.");
}
} }
showInvite(inviteId: string, redeemed: boolean, expired: boolean) { async showInvite(inviteId: string, redeemed: boolean, expired: boolean) {
useClipboard().copy(inviteId); try {
this.notify.success( await copyToClipboard(inviteId);
createInviteIdCopyMessage(inviteId, redeemed, expired), this.notify.success(
TIMEOUTS.LONG, createInviteIdCopyMessage(inviteId, redeemed, expired),
); TIMEOUTS.LONG,
);
} catch (error) {
this.$logAndConsole(`Error copying invite ID: ${error}`, true);
this.notify.error("Failed to copy invite ID.");
}
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any

19
src/views/OnboardMeetingSetupView.vue

@ -270,7 +270,7 @@
<script lang="ts"> <script lang="ts">
import { Component, Vue } from "vue-facing-decorator"; import { Component, Vue } from "vue-facing-decorator";
import { useClipboard } from "@vueuse/core"; import { copyToClipboard } from "../services/ClipboardService";
import { RouteLocationNormalizedLoaded, Router } from "vue-router"; import { RouteLocationNormalizedLoaded, Router } from "vue-router";
import QuickNav from "../components/QuickNav.vue"; import QuickNav from "../components/QuickNav.vue";
@ -676,12 +676,17 @@ export default class OnboardMeetingView extends Vue {
this.notify.error(message, TIMEOUTS.LONG); this.notify.error(message, TIMEOUTS.LONG);
} }
copyMembersLinkToClipboard() { async copyMembersLinkToClipboard() {
useClipboard() try {
.copy(this.onboardMeetingMembersLink()) await copyToClipboard(this.onboardMeetingMembersLink());
.then(() => { this.notify.info(NOTIFY_MEETING_LINK_COPIED.message, TIMEOUTS.LONG);
this.notify.info(NOTIFY_MEETING_LINK_COPIED.message, TIMEOUTS.LONG); } catch (error) {
}); this.$logAndConsole(
`Error copying meeting link to clipboard: ${error}`,
true,
);
this.notify.error("Failed to copy meeting link to clipboard.");
}
} }
} }
</script> </script>

16
src/views/ProjectViewView.vue

@ -616,7 +616,7 @@ import * as serverUtil from "../libs/endorserServer";
import { retrieveAccountDids } from "../libs/util"; import { retrieveAccountDids } from "../libs/util";
import HiddenDidDialog from "../components/HiddenDidDialog.vue"; import HiddenDidDialog from "../components/HiddenDidDialog.vue";
import { logger } from "../utils/logger"; import { logger } from "../utils/logger";
import { useClipboard } from "@vueuse/core"; import { copyToClipboard } from "../services/ClipboardService";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify"; import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
import { NOTIFY_CONFIRM_CLAIM } from "@/constants/notifications"; import { NOTIFY_CONFIRM_CLAIM } from "@/constants/notifications";
@ -817,7 +817,7 @@ export default class ProjectViewView extends Vue {
}); });
} }
onCopyLinkClick() { async onCopyLinkClick() {
const shortestProjectId = this.projectId.startsWith( const shortestProjectId = this.projectId.startsWith(
serverUtil.ENDORSER_CH_HANDLE_PREFIX, serverUtil.ENDORSER_CH_HANDLE_PREFIX,
) )
@ -825,11 +825,13 @@ export default class ProjectViewView extends Vue {
: this.projectId; : this.projectId;
// Use production URL for sharing to avoid localhost issues in development // Use production URL for sharing to avoid localhost issues in development
const deepLink = `${APP_SERVER}/deep-link/project/${shortestProjectId}`; const deepLink = `${APP_SERVER}/deep-link/project/${shortestProjectId}`;
useClipboard() try {
.copy(deepLink) await copyToClipboard(deepLink);
.then(() => { this.notify.copied("link to this project", TIMEOUTS.SHORT);
this.notify.copied("link to this project", TIMEOUTS.SHORT); } catch (error) {
}); this.$logAndConsole(`Error copying project link: ${error}`, true);
this.notify.error("Failed to copy project link.");
}
} }
// Isn't there a better way to make this available to the template? // Isn't there a better way to make this available to the template?

32
src/views/QuickActionBvcEndView.vue

@ -127,7 +127,7 @@ import { DateTime } from "luxon";
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 { Router } from "vue-router"; import { Router } from "vue-router";
import { useClipboard } from "@vueuse/core"; import { copyToClipboard } from "../services/ClipboardService";
import QuickNav from "../components/QuickNav.vue"; import QuickNav from "../components/QuickNav.vue";
import TopMessage from "../components/TopMessage.vue"; import TopMessage from "../components/TopMessage.vue";
@ -305,23 +305,21 @@ export default class QuickActionBvcEndView extends Vue {
(this.$router as Router).push(route); (this.$router as Router).push(route);
} }
copyContactsLinkToClipboard() { async copyContactsLinkToClipboard() {
const deepLinkUrl = `${APP_SERVER}/deep-link/did/${this.activeDid}`; const deepLinkUrl = `${APP_SERVER}/deep-link/did/${this.activeDid}`;
useClipboard() try {
.copy(deepLinkUrl) await copyToClipboard(deepLinkUrl);
.then(() => { this.notify.success(
this.notify.success( NOTIFY_COPIED_TO_CLIPBOARD.message("Your info link"),
NOTIFY_COPIED_TO_CLIPBOARD.message("Your info link"), TIMEOUTS.SHORT,
TIMEOUTS.SHORT, );
); } catch (error) {
}) logger.error("Failed to copy to clipboard:", error);
.catch((error) => { this.notify.error(
logger.error("Failed to copy to clipboard:", error); "Failed to copy link to clipboard. Please try again.",
this.notify.error( TIMEOUTS.SHORT,
"Failed to copy link to clipboard. Please try again.", );
TIMEOUTS.SHORT, }
);
});
} }
async record() { async record() {

14
src/views/SeedBackupView.vue

@ -106,7 +106,7 @@
<script lang="ts"> <script lang="ts">
import { Component, Vue } from "vue-facing-decorator"; import { Component, Vue } from "vue-facing-decorator";
import { useClipboard } from "@vueuse/core"; import { copyToClipboard } from "../services/ClipboardService";
import QuickNav from "../components/QuickNav.vue"; import QuickNav from "../components/QuickNav.vue";
import { NotificationIface } from "../constants/app"; import { NotificationIface } from "../constants/app";
@ -279,11 +279,15 @@ export default class SeedBackupView extends Vue {
* @param text - The text to copy to clipboard * @param text - The text to copy to clipboard
* @param fn - Callback function to execute for feedback (called twice - immediately and after 2 seconds) * @param fn - Callback function to execute for feedback (called twice - immediately and after 2 seconds)
*/ */
doCopyTwoSecRedo(text: string, fn: () => void) { async doCopyTwoSecRedo(text: string, fn: () => void) {
fn(); fn();
useClipboard() try {
.copy(text) await copyToClipboard(text);
.then(() => setTimeout(fn, 2000)); setTimeout(fn, 2000);
} catch (error) {
this.$logAndConsole(`Error copying to clipboard: ${error}`, true);
this.notify.error("Failed to copy to clipboard.");
}
} }
} }
</script> </script>

11
src/views/ShareMyContactInfoView.vue

@ -54,6 +54,7 @@ import { generateEndorserJwtUrlForAccount } from "../libs/endorserServer";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { Settings } from "@/db/tables/settings"; import { Settings } from "@/db/tables/settings";
import { Account } from "@/db/tables/accounts"; import { Account } from "@/db/tables/accounts";
import { copyToClipboard } from "../services/ClipboardService";
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify"; import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
// Constants for magic numbers // Constants for magic numbers
@ -99,7 +100,7 @@ export default class ShareMyContactInfoView extends Vue {
} }
const message = await this.generateContactMessage(settings, account); const message = await this.generateContactMessage(settings, account);
await this.copyToClipboard(message); await copyToClipboard(message);
await this.showSuccessNotifications(); await this.showSuccessNotifications();
this.navigateToContacts(); this.navigateToContacts();
} catch (error) { } catch (error) {
@ -140,14 +141,6 @@ export default class ShareMyContactInfoView extends Vue {
); );
} }
/**
* Copy the contact message to clipboard
*/
private async copyToClipboard(message: string): Promise<void> {
const { copyToClipboard } = await import("../services/ClipboardService");
await copyToClipboard(message);
}
/** /**
* Show success notifications after copying * Show success notifications after copying
*/ */

16
src/views/UserProfileView.vue

@ -108,7 +108,7 @@ import { didInfo, getHeaders } from "../libs/endorserServer";
import { UserProfile } from "../libs/partnerServer"; import { UserProfile } from "../libs/partnerServer";
import { retrieveAccountDids } from "../libs/util"; import { retrieveAccountDids } from "../libs/util";
import { logger } from "../utils/logger"; import { logger } from "../utils/logger";
import { useClipboard } from "@vueuse/core"; import { copyToClipboard } from "../services/ClipboardService";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify"; import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
import { NOTIFY_PROFILE_LOAD_ERROR } from "@/constants/notifications"; import { NOTIFY_PROFILE_LOAD_ERROR } from "@/constants/notifications";
@ -240,14 +240,16 @@ export default class UserProfileView extends Vue {
* Creates a deep link to the profile and copies it to the clipboard * Creates a deep link to the profile and copies it to the clipboard
* Shows success notification when completed * Shows success notification when completed
*/ */
onCopyLinkClick() { async onCopyLinkClick() {
// Use production URL for sharing to avoid localhost issues in development // Use production URL for sharing to avoid localhost issues in development
const deepLink = `${APP_SERVER}/deep-link/user-profile/${this.profile?.rowId}`; const deepLink = `${APP_SERVER}/deep-link/user-profile/${this.profile?.rowId}`;
useClipboard() try {
.copy(deepLink) await copyToClipboard(deepLink);
.then(() => { this.notify.copied("profile link", TIMEOUTS.STANDARD);
this.notify.copied("profile link", TIMEOUTS.STANDARD); } catch (error) {
}); this.$logAndConsole(`Error copying profile link: ${error}`, true);
this.notify.error("Failed to copy profile link.");
}
} }
/** /**

Loading…
Cancel
Save