Browse Source

feat: switch ContactQRScanShowView to URL-based contact sharing

- Replace CSV QR value copying with URL generation for better UX
- Add generateEndorserJwtUrlForAccount import and Account type
- Implement proper error handling for URL generation failures
- Maintain consistency with ContactQRScanFullView behavior
- Add documentation for the URL solution implementation

Recipients can now click shared URLs to add contacts directly instead of
manually pasting CSV data into input fields.

addresses:  https://app.clickup.com/t/86b63xhz4
get-get-hash
Matthew Raymer 1 week ago
parent
commit
6868a322f1
  1. 84
      docs/contact-sharing-url-solution.md
  2. 6
      src/utils/PlatformServiceMixin.ts
  3. 39
      src/views/ContactQRScanShowView.vue

84
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.

6
src/utils/PlatformServiceMixin.ts

@ -1062,9 +1062,9 @@ export const PlatformServiceMixin = {
: null, : null,
contactMethods: contactMethods:
contact.contactMethods !== undefined contact.contactMethods !== undefined
? (Array.isArray(contact.contactMethods) ? Array.isArray(contact.contactMethods)
? JSON.stringify(contact.contactMethods) ? JSON.stringify(contact.contactMethods)
: contact.contactMethods) : contact.contactMethods
: null, : null,
}; };

39
src/views/ContactQRScanShowView.vue

@ -153,6 +153,7 @@ import {
CONTACT_IMPORT_CONFIRM_URL_PATH_TIME_SAFARI, CONTACT_IMPORT_CONFIRM_URL_PATH_TIME_SAFARI,
register, register,
setVisibilityUtil, setVisibilityUtil,
generateEndorserJwtUrlForAccount,
} from "../libs/endorserServer"; } from "../libs/endorserServer";
import { decodeEndorserJwt, ETHR_DID_PREFIX } from "../libs/crypto/vc"; import { decodeEndorserJwt, ETHR_DID_PREFIX } from "../libs/crypto/vc";
import * as libsUtil from "../libs/util"; import * as libsUtil from "../libs/util";
@ -187,6 +188,7 @@ import {
QR_TIMEOUT_STANDARD, QR_TIMEOUT_STANDARD,
QR_TIMEOUT_LONG, QR_TIMEOUT_LONG,
} from "@/constants/notifications"; } from "@/constants/notifications";
import { Account } from "@/db/tables/accounts";
interface QRScanResult { interface QRScanResult {
rawValue?: string; rawValue?: string;
@ -610,16 +612,33 @@ export default class ContactQRScanShow extends Vue {
} }
async onCopyUrlToClipboard() { async onCopyUrlToClipboard() {
// Copy the CSV format QR code value instead of generating a deep link try {
useClipboard() // Generate URL for sharing
.copy(this.qrValue) const account = (await libsUtil.retrieveFullyDecryptedAccount(
.then(() => { this.activeDid,
this.notify.toast( )) as Account;
"Copied", const jwtUrl = await generateEndorserJwtUrlForAccount(
NOTIFY_QR_URL_COPIED.message, account,
QR_TIMEOUT_MEDIUM, 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() { toastQRCodeHelp() {

Loading…
Cancel
Save