change all copied contact URLs to contact-import, and handle multiples & singles separately

This commit is contained in:
2025-01-09 20:10:00 -07:00
parent 5f39beef55
commit 6514f52b92
5 changed files with 104 additions and 74 deletions

View File

@@ -344,7 +344,7 @@ import {
updateDefaultSettings,
} from "@/db/index";
import { Contact } from "@/db/tables/contacts";
import { getContactPayloadFromJwtUrl } from "@/libs/crypto";
import { getContactJwtFromJwtUrl } from "@/libs/crypto";
import { decodeEndorserJwt } from "@/libs/crypto/vc";
import {
CONTACT_CSV_HEADER,
@@ -416,6 +416,11 @@ export default class ContactsView extends Vue {
this.apiServer = settings.apiServer || "";
this.isRegistered = !!settings.isRegistered;
// if these detect a query parameter, they can and then redirect to this URL without a query parameter
// to avoid problems when they reload or they go forward & back and it tries to reprocess
await this.processContactJwt();
await this.processInviteJwt();
this.showGiveNumbers = !!settings.showContactGivesInline;
this.hideRegisterPromptOnNewContact =
!!settings.hideRegisterPromptOnNewContact;
@@ -430,11 +435,13 @@ export default class ContactsView extends Vue {
this.contacts = baseContacts.sort((a, b) =>
(a.name || "").localeCompare(b.name || ""),
);
}
private async processContactJwt() {
// handle a contact sent via URL
//
// Prefer use of /contact-import/:jwt with a JWT that has an array of contacts
// unless you want them to import a single contact without confirmation.
// For external links, use /contact-import/:jwt with a JWT that has an array of contacts
// because that will do better error checking for things like missing data on iOS platforms.
const importedContactJwt = (this.$route as RouteLocationNormalizedLoaded)
.query["contactJwt"] as string;
if (importedContactJwt) {
@@ -442,21 +449,25 @@ export default class ContactsView extends Vue {
const { payload } = decodeEndorserJwt(importedContactJwt);
const userInfo = payload["own"] as UserInfo;
const newContact = {
did: payload["iss"],
did: userInfo.did || payload["iss"], // ".did" is reliable as of v 0.3.49
name: userInfo.name,
nextPubKeyHashB64: userInfo.nextPublicEncKeyHash,
profileImageUrl: userInfo.profileImageUrl,
publicKeyBase64: userInfo.publicEncKey,
registered: userInfo.registered,
} as Contact;
this.addContact(newContact);
await this.addContact(newContact);
// if we're here, they haven't redirected anywhere, so we'll redirect here without a query parameter
(this.$router as Router).push({ path: "/contacts" });
}
}
private async processInviteJwt() {
// handle an invite JWT sent via URL
const importedInviteJwt = (this.$route as RouteLocationNormalizedLoaded)
.query["inviteJwt"] as string;
if (importedInviteJwt === "") {
// this happens when a platform (usually iOS) doesn't include anything after the "=" in a shared link.
// this happens when a platform (eg iOS) doesn't include anything after the "=" in a shared link.
this.$notify(
{
group: "alert",
@@ -555,6 +566,8 @@ export default class ContactsView extends Vue {
5000,
);
}
// if we're here, they haven't redirected anywhere, so we'll redirect here without a query parameter
(this.$router as Router).push({ path: "/contacts" });
}
}
@@ -717,12 +730,30 @@ export default class ContactsView extends Vue {
return;
}
if (contactInput.includes(CONTACT_IMPORT_CONFIRM_URL_PATH_TIME_SAFARI)) {
const jwt = getContactJwtFromJwtUrl(contactInput);
(this.$router as Router).push({
path: "/contact-import/" + jwt,
});
return;
}
if (
contactInput.includes(CONTACT_IMPORT_CONFIRM_URL_PATH_TIME_SAFARI) ||
contactInput.includes(CONTACT_IMPORT_ONE_URL_PATH_TIME_SAFARI) ||
contactInput.includes(CONTACT_URL_PATH_ENDORSER_CH_OLD)
) {
await this.addContactFromScan(contactInput);
const jwt = getContactJwtFromJwtUrl(contactInput);
const { payload } = decodeEndorserJwt(jwt);
const userInfo = payload["own"] as UserInfo;
const newContact = {
did: userInfo.did || payload["iss"], // "did" is reliable as of v 0.3.49
name: userInfo.name,
nextPubKeyHashB64: userInfo.nextPublicEncKeyHash,
profileImageUrl: userInfo.profileImageUrl,
publicKeyBase64: userInfo.publicEncKey,
registered: userInfo.registered,
} as Contact;
await this.addContact(newContact);
return;
}
@@ -870,38 +901,6 @@ export default class ContactsView extends Vue {
return db.contacts.add(newContact);
}
private async addContactFromScan(url: string): Promise<void> {
const payload = getContactPayloadFromJwtUrl(url);
if (!payload) {
this.$notify(
{
group: "alert",
type: "danger",
title: "No Contact Info",
text: "The contact info could not be parsed.",
},
3000,
);
return;
} else {
if (Array.isArray(payload.contacts)) {
// reroute to the ContactsImport
(this.$router as Router).push({
path: "/contact-import/" + url.substring(url.lastIndexOf("/") + 1),
});
return;
}
return this.addContact({
did: payload.iss,
name: payload.own.name,
nextPubKeyHashB64: payload.own.nextPublicEncKeyHash,
profileImageUrl: payload.own.profileImageUrl,
publicKeyBase64: payload.own.publicEncKey,
registered: payload.own.registered,
} as Contact);
}
}
private async addContact(newContact: Contact) {
if (!newContact.did) {
this.danger("Cannot add a contact without a DID.", "Incomplete Contact");
@@ -961,7 +960,7 @@ export default class ContactsView extends Vue {
},
-1,
);
}, 500);
}, 1000);
}
}
this.$notify(