From cf1ecdfb4ce5dec3abc1580f69f37c76fdaf8ba9 Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Sun, 22 Mar 2026 20:20:33 -0600 Subject: [PATCH] add registration for new contacts that are unregistered --- src/views/ContactQRScanFullView.vue | 104 ++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/src/views/ContactQRScanFullView.vue b/src/views/ContactQRScanFullView.vue index 6918b0c7..fa0be0c6 100644 --- a/src/views/ContactQRScanFullView.vue +++ b/src/views/ContactQRScanFullView.vue @@ -118,11 +118,13 @@ import { CONTACT_CSV_HEADER, CONTACT_IMPORT_CONFIRM_URL_PATH_TIME_SAFARI, generateEndorserJwtUrlForAccount, + register, setVisibilityUtil, } from "../libs/endorserServer"; import UserNameDialog from "../components/UserNameDialog.vue"; import { retrieveAccountMetadata } from "../libs/util"; +import { AxiosError } from "axios"; import { Account } from "@/db/tables/accounts"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; import { @@ -139,7 +141,10 @@ import { NOTIFY_QR_URL_COPIED, NOTIFY_QR_CODE_HELP, NOTIFY_QR_DID_COPIED, + NOTIFY_QR_REGISTRATION_SUBMITTED, + NOTIFY_QR_REGISTRATION_ERROR, createQRContactAddedMessage, + createQRRegistrationSuccessMessage, QR_TIMEOUT_MEDIUM, QR_TIMEOUT_STANDARD, QR_TIMEOUT_LONG, @@ -204,6 +209,7 @@ export default class ContactQRScanFull extends Vue { activeDid = ""; apiServer = ""; givenName = ""; + hideRegisterPromptOnNewContact = false; isRegistered = false; profileImageUrl = ""; qrValue = ""; @@ -278,6 +284,8 @@ export default class ContactQRScanFull extends Vue { this.apiServer = settings.apiServer || ""; this.givenName = settings.firstName || ""; + this.hideRegisterPromptOnNewContact = + !!settings.hideRegisterPromptOnNewContact; this.isRegistered = !!settings.isRegistered; this.profileImageUrl = settings.profileImageUrl || ""; @@ -575,6 +583,34 @@ export default class ContactQRScanFull extends Vue { createQRContactAddedMessage(!!this.activeDid), QR_TIMEOUT_STANDARD, ); + + if ( + this.isRegistered && + !this.hideRegisterPromptOnNewContact && + !contact.registered + ) { + setTimeout(() => { + this.$notify( + { + group: "modal", + type: "confirm", + title: "Register", + text: "Do you want to register them?", + onCancel: async (stopAsking?: boolean) => { + await this.handleRegistrationPromptResponse(stopAsking); + }, + onNo: async (stopAsking?: boolean) => { + await this.handleRegistrationPromptResponse(stopAsking); + }, + onYes: async () => { + await this.register(contact); + }, + promptToStopAsking: true, + }, + -1, + ); + }, 500); + } } catch (error) { logger.error("Error saving contact to database:", { did: contact.did, @@ -585,6 +621,74 @@ export default class ContactQRScanFull extends Vue { } } + async register(contact: Contact) { + logger.debug("Submitting contact registration", { + did: contact.did, + name: contact.name, + }); + + try { + const regResult = await register( + this.activeDid, + this.apiServer, + this.axios, + contact, + ); + this.notify.toast("Submitted", NOTIFY_QR_REGISTRATION_SUBMITTED.message); + if (regResult.success) { + contact.registered = true; + await this.$updateContact(contact.did, { registered: true }); + logger.debug("Contact registration successful", { did: contact.did }); + + this.notify.success( + createQRRegistrationSuccessMessage(contact.name || ""), + QR_TIMEOUT_LONG, + ); + } else { + this.notify.error( + (regResult.error as string) || NOTIFY_QR_REGISTRATION_ERROR.message, + QR_TIMEOUT_LONG, + ); + } + } catch (error) { + logger.error("Error registering contact:", { + did: contact.did, + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + }); + let userMessage = "There was an error."; + const serverError = error as AxiosError; + if (serverError) { + if ( + serverError.response?.data && + typeof serverError.response.data === "object" && + "message" in serverError.response.data + ) { + userMessage = (serverError.response.data as { message: string }) + .message; + } else if (serverError.message) { + userMessage = serverError.message; + } else { + userMessage = JSON.stringify(serverError.toJSON()); + } + } else { + userMessage = error as string; + } + this.notify.error(userMessage, QR_TIMEOUT_LONG); + } + } + + private async handleRegistrationPromptResponse( + stopAsking?: boolean, + ): Promise { + if (stopAsking) { + await this.$saveSettings({ + hideRegisterPromptOnNewContact: stopAsking, + }); + this.hideRegisterPromptOnNewContact = stopAsking; + } + } + /** * Vue lifecycle hook - component mounted * Sets up event listeners and starts scanning automatically