From 5e851e442f11fba239941d66f41687fedd1a8506 Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Mon, 16 Jun 2025 15:38:11 -0600 Subject: [PATCH] shrink the contents of the QR code so people can scan it --- src/components/OfferDialog.vue | 5 +- src/components/TopMessage.vue | 3 +- src/interfaces/deepLinks.ts | 2 +- src/libs/util.ts | 65 ++++++++++++++ src/services/deepLinks.ts | 6 +- src/views/ContactQRScanFullView.vue | 126 ++++++++++++++++------------ src/views/ContactQRScanShowView.vue | 119 +++++++++++++++----------- src/views/ContactsView.vue | 45 +--------- src/views/QuickActionBvcEndView.vue | 37 ++++---- 9 files changed, 233 insertions(+), 175 deletions(-) diff --git a/src/components/OfferDialog.vue b/src/components/OfferDialog.vue index 1ca6dc2b..c5c54e7a 100644 --- a/src/components/OfferDialog.vue +++ b/src/components/OfferDialog.vue @@ -83,10 +83,7 @@ import { Vue, Component, Prop } from "vue-facing-decorator"; import { NotificationIface, USE_DEXIE_DB } from "../constants/app"; -import { - createAndSubmitOffer, - serverMessageForUser, -} from "../libs/endorserServer"; +import { createAndSubmitOffer } from "../libs/endorserServer"; import * as libsUtil from "../libs/util"; import * as databaseUtil from "../db/databaseUtil"; import { retrieveSettingsForActiveAccount } from "../db/index"; diff --git a/src/components/TopMessage.vue b/src/components/TopMessage.vue index c50d9709..54b6a01b 100644 --- a/src/components/TopMessage.vue +++ b/src/components/TopMessage.vue @@ -44,8 +44,7 @@ export default class TopMessage extends Vue { settings.apiServer === AppString.PROD_ENDORSER_API_SERVER ) { const didPrefix = settings.activeDid?.slice(11, 15); - this.message = - "You are using prod, user " + didPrefix; + this.message = "You are using prod, user " + didPrefix; } } catch (err: unknown) { this.$notify( diff --git a/src/interfaces/deepLinks.ts b/src/interfaces/deepLinks.ts index 3f6a1d03..b56862c1 100644 --- a/src/interfaces/deepLinks.ts +++ b/src/interfaces/deepLinks.ts @@ -61,7 +61,7 @@ export const deepLinkSchemas = { "user-profile": z.object({ id: z.string(), }), - "project": z.object({ + project: z.object({ id: z.string(), }), "onboard-meeting-setup": z.object({ diff --git a/src/libs/util.ts b/src/libs/util.ts index 3bc86866..03ee7637 100644 --- a/src/libs/util.ts +++ b/src/libs/util.ts @@ -882,6 +882,71 @@ export const contactToCsvLine = (contact: Contact): string => { return fields.join(","); }; +/** + * Parses a CSV line into a Contact object. See contactToCsvLine for the format. + * @param lineRaw - The CSV line to parse + * @returns A Contact object + */ +export const csvLineToContact = (lineRaw: string): Contact => { + // Note that Endorser Mobile puts name first, then did, etc. + let line = lineRaw.trim(); + let did, publicKeyInput, seesMe, registered; + let name; + let commaPos1 = -1; + if (line.startsWith('"')) { + let doubleDoubleQuotePos = line.lastIndexOf('""') + 2; + if (doubleDoubleQuotePos === -1) { + doubleDoubleQuotePos = 1; + } + const quote2Pos = line.indexOf('"', doubleDoubleQuotePos); + if (quote2Pos > -1) { + commaPos1 = line.indexOf(",", quote2Pos); + name = line.substring(1, quote2Pos).trim(); + name = name.replace(/""/g, '"'); + } else { + // something is weird with one " to start, so ignore it and start after " + line = line.substring(1); + commaPos1 = line.indexOf(","); + name = line.substring(0, commaPos1).trim(); + } + } else { + commaPos1 = line.indexOf(","); + name = line.substring(0, commaPos1).trim(); + } + if (commaPos1 > -1) { + did = line.substring(commaPos1 + 1).trim(); + const commaPos2 = line.indexOf(",", commaPos1 + 1); + if (commaPos2 > -1) { + did = line.substring(commaPos1 + 1, commaPos2).trim(); + publicKeyInput = line.substring(commaPos2 + 1).trim(); + const commaPos3 = line.indexOf(",", commaPos2 + 1); + if (commaPos3 > -1) { + publicKeyInput = line.substring(commaPos2 + 1, commaPos3).trim(); + seesMe = line.substring(commaPos3 + 1).trim() == "true"; + const commaPos4 = line.indexOf(",", commaPos3 + 1); + if (commaPos4 > -1) { + seesMe = line.substring(commaPos3 + 1, commaPos4).trim() == "true"; + registered = line.substring(commaPos4 + 1).trim() == "true"; + } + } + } + } + // help with potential mistakes while this sharing requires copy-and-paste + let publicKeyBase64 = publicKeyInput; + if (publicKeyBase64 && /^[0-9A-Fa-f]{66}$/i.test(publicKeyBase64)) { + // it must be all hex (compressed public key), so convert + publicKeyBase64 = Buffer.from(publicKeyBase64, "hex").toString("base64"); + } + const newContact: Contact = { + did: did || "", + name, + publicKeyBase64, + seesMe, + registered, + }; + return newContact; +}; + /** * Interface for the JSON export format of database tables */ diff --git a/src/services/deepLinks.ts b/src/services/deepLinks.ts index 04ff1a44..149665fc 100644 --- a/src/services/deepLinks.ts +++ b/src/services/deepLinks.ts @@ -81,15 +81,15 @@ export class DeepLinkHandler { string, { name: string; paramKey?: string } > = { - "claim": { name: "claim" }, + claim: { name: "claim" }, "claim-add-raw": { name: "claim-add-raw" }, "claim-cert": { name: "claim-cert" }, "confirm-gift": { name: "confirm-gift" }, - "did": { name: "did", paramKey: "did" }, + did: { name: "did", paramKey: "did" }, "invite-one-accept": { name: "invite-one-accept" }, "onboard-meeting-members": { name: "onboard-meeting-members" }, "onboard-meeting-setup": { name: "onboard-meeting-setup" }, - "project": { name: "project" }, + project: { name: "project" }, "user-profile": { name: "user-profile" }, }; diff --git a/src/views/ContactQRScanFullView.vue b/src/views/ContactQRScanFullView.vue index 64f58829..49366fbd 100644 --- a/src/views/ContactQRScanFullView.vue +++ b/src/views/ContactQRScanFullView.vue @@ -104,6 +104,7 @@