diff --git a/docs/contact-sharing-url-solution.md b/docs/contact-sharing-url-solution.md new file mode 100644 index 00000000..891188f5 --- /dev/null +++ b/docs/contact-sharing-url-solution.md @@ -0,0 +1,84 @@ +# Contact Sharing - URL Solution + +## Overview + +Simple implementation to switch ContactQRScanShowView from copying QR value (CSV) to copying a URL for better user experience. + +## Problem + +The ContactQRScanShowView was copying QR value (CSV content) to clipboard instead of a URL, making contact sharing less user-friendly. + +## Solution + +Updated the `onCopyUrlToClipboard()` method in ContactQRScanShowView.vue to generate and copy a URL instead of the QR value. + +## Changes Made + +### ContactQRScanShowView.vue + +**Added Imports:** +```typescript +import { generateEndorserJwtUrlForAccount } from "../libs/endorserServer"; +import { Account } from "@/db/tables/accounts"; +``` + +**Updated Method:** +```typescript +async onCopyUrlToClipboard() { + try { + // Generate URL for sharing + const account = (await libsUtil.retrieveFullyDecryptedAccount( + this.activeDid, + )) as Account; + const jwtUrl = await generateEndorserJwtUrlForAccount( + account, + this.isRegistered, + this.givenName, + this.profileImageUrl, + true, + ); + + // Copy the URL to clipboard + useClipboard() + .copy(jwtUrl) + .then(() => { + this.notify.toast( + "Copied", + NOTIFY_QR_URL_COPIED.message, + QR_TIMEOUT_MEDIUM, + ); + }); + } catch (error) { + logger.error("Failed to generate contact URL:", error); + this.notify.error("Failed to generate contact URL. Please try again."); + } +} +``` + +## Benefits + +1. **Better UX**: Recipients can click the URL to add contact directly +2. **Consistency**: Both ContactQRScanShowView and ContactQRScanFullView now use URL format +3. **Error Handling**: Graceful fallback if URL generation fails +4. **Simple**: Minimal changes, no new components needed + +## User Experience + +**Before:** +- Click QR code → Copy CSV data to clipboard +- Recipient must paste CSV into input field + +**After:** +- Click QR code → Copy URL to clipboard +- Recipient clicks URL → Contact added automatically + +## Testing + +- ✅ Linting passes +- ✅ Error handling implemented +- ✅ Consistent with ContactQRScanFullView behavior +- ✅ Maintains existing notification system + +## Deployment + +Ready for deployment. No breaking changes, maintains backward compatibility. diff --git a/src/utils/PlatformServiceMixin.ts b/src/utils/PlatformServiceMixin.ts index 11684af3..361a2ff2 100644 --- a/src/utils/PlatformServiceMixin.ts +++ b/src/utils/PlatformServiceMixin.ts @@ -1062,9 +1062,9 @@ export const PlatformServiceMixin = { : null, contactMethods: contact.contactMethods !== undefined - ? (Array.isArray(contact.contactMethods) - ? JSON.stringify(contact.contactMethods) - : contact.contactMethods) + ? Array.isArray(contact.contactMethods) + ? JSON.stringify(contact.contactMethods) + : contact.contactMethods : null, }; diff --git a/src/views/ContactQRScanShowView.vue b/src/views/ContactQRScanShowView.vue index 1396ce9b..8990df30 100644 --- a/src/views/ContactQRScanShowView.vue +++ b/src/views/ContactQRScanShowView.vue @@ -153,6 +153,7 @@ import { CONTACT_IMPORT_CONFIRM_URL_PATH_TIME_SAFARI, register, setVisibilityUtil, + generateEndorserJwtUrlForAccount, } from "../libs/endorserServer"; import { decodeEndorserJwt, ETHR_DID_PREFIX } from "../libs/crypto/vc"; import * as libsUtil from "../libs/util"; @@ -187,6 +188,7 @@ import { QR_TIMEOUT_STANDARD, QR_TIMEOUT_LONG, } from "@/constants/notifications"; +import { Account } from "@/db/tables/accounts"; interface QRScanResult { rawValue?: string; @@ -610,16 +612,33 @@ export default class ContactQRScanShow extends Vue { } async onCopyUrlToClipboard() { - // Copy the CSV format QR code value instead of generating a deep link - useClipboard() - .copy(this.qrValue) - .then(() => { - this.notify.toast( - "Copied", - NOTIFY_QR_URL_COPIED.message, - QR_TIMEOUT_MEDIUM, - ); - }); + try { + // Generate URL for sharing + const account = (await libsUtil.retrieveFullyDecryptedAccount( + this.activeDid, + )) as Account; + const jwtUrl = await generateEndorserJwtUrlForAccount( + account, + this.isRegistered, + this.givenName, + this.profileImageUrl, + true, + ); + + // Copy the URL to clipboard + useClipboard() + .copy(jwtUrl) + .then(() => { + this.notify.toast( + "Copied", + NOTIFY_QR_URL_COPIED.message, + QR_TIMEOUT_MEDIUM, + ); + }); + } catch (error) { + logger.error("Failed to generate contact URL:", error); + this.notify.error("Failed to generate contact URL. Please try again."); + } } toastQRCodeHelp() {