| 
						
						
							
								
							
						
						
					 | 
				
				 | 
				
					@ -104,6 +104,7 @@ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					</template> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					<script lang="ts"> | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { Buffer } from "buffer/"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import QRCodeVue3 from "qr-code-generator-vue3"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { Component, Vue } from "vue-facing-decorator"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { Router } from "vue-router"; | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -117,11 +118,15 @@ import { db } from "../db/index"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { Contact } from "../db/tables/contacts"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { getContactJwtFromJwtUrl } from "../libs/crypto"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { decodeEndorserJwt, ETHR_DID_PREFIX } from "../libs/crypto/vc"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import * as libsUtil from "../libs/util"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { retrieveSettingsForActiveAccount } from "../db/index"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import * as databaseUtil from "../db/databaseUtil"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { setVisibilityUtil } from "../libs/endorserServer"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  CONTACT_CSV_HEADER, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  CONTACT_IMPORT_CONFIRM_URL_PATH_TIME_SAFARI, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  setVisibilityUtil, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					} from "../libs/endorserServer"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import UserNameDialog from "../components/UserNameDialog.vue"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { generateEndorserJwtUrlForAccount } from "../libs/endorserServer"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { retrieveAccountMetadata } from "../libs/util"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { PlatformServiceFactory } from "@/services/PlatformServiceFactory"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { parseJsonField } from "../db/databaseUtil"; | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -142,7 +147,7 @@ interface IUserNameDialog { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    UserNameDialog, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  }, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					}) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					export default class ContactQRScan extends Vue { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					export default class ContactQRScanFull extends Vue { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  $notify!: (notification: NotificationIface, timeout?: number) => void; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  $router!: Router; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -151,6 +156,7 @@ export default class ContactQRScan extends Vue { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  activeDid = ""; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  apiServer = ""; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  givenName = ""; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  isRegistered = false; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  qrValue = ""; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  ETHR_DID_PREFIX = ETHR_DID_PREFIX; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -172,19 +178,21 @@ export default class ContactQRScan extends Vue { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      this.activeDid = settings.activeDid || ""; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      this.apiServer = settings.apiServer || ""; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      this.givenName = settings.firstName || ""; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      this.isRegistered = !!settings.isRegistered; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const account = await retrieveAccountMetadata(this.activeDid); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      if (account) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        const name = | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          (settings.firstName || "") + | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          (settings.lastName ? ` ${settings.lastName}` : ""); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        this.qrValue = await generateEndorserJwtUrlForAccount( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          account, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          !!settings.isRegistered, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          name, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          settings.profileImageUrl || "", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          false, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        ); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        const publicKeyBase64 = Buffer.from( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          account.publicKeyHex, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          "hex", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        ).toString("base64"); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        this.qrValue = | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          CONTACT_CSV_HEADER + | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          "\n" + | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          `"${name}",${account.did},${publicKeyBase64},false,${this.isRegistered}`; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } catch (error) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      logger.error("Error initializing component:", { | 
				
			
			
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
				 | 
				
					@ -336,57 +344,69 @@ export default class ContactQRScan extends Vue { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      logger.info("Processing QR code scan result:", rawValue); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // Extract JWT | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const jwt = getContactJwtFromJwtUrl(rawValue); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      if (!jwt) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        logger.warn("Invalid QR code format - no JWT found in URL"); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        this.$notify({ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          group: "alert", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          type: "danger", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          title: "Invalid QR Code", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          text: "This QR code does not contain valid contact information. Please scan a TimeSafari contact QR code.", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        }); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        return; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      let contact: Contact; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      if (rawValue.includes(CONTACT_IMPORT_CONFIRM_URL_PATH_TIME_SAFARI)) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        // Extract JWT | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        const jwt = getContactJwtFromJwtUrl(rawValue); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        if (!jwt) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          logger.warn("Invalid QR code format - no JWT found in URL"); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          this.$notify({ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            group: "alert", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            type: "danger", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            title: "Invalid QR Code", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            text: "This QR code does not contain valid contact information. Please scan a TimeSafari contact QR code.", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          }); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          return; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // Process JWT and contact info | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      logger.info("Decoding JWT payload from QR code"); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const decodedJwt = await decodeEndorserJwt(jwt); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      if (!decodedJwt?.payload?.own) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        logger.warn("Invalid JWT payload - missing 'own' field"); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        this.$notify({ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          group: "alert", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          type: "danger", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          title: "Invalid Contact Info", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          text: "The contact information is incomplete or invalid.", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        }); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        return; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        // Process JWT and contact info | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        logger.info("Decoding JWT payload from QR code"); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        const decodedJwt = await decodeEndorserJwt(jwt); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        if (!decodedJwt?.payload?.own) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          logger.warn("Invalid JWT payload - missing 'own' field"); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          this.$notify({ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            group: "alert", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            type: "danger", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            title: "Invalid Contact Info", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            text: "The contact information is incomplete or invalid.", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          }); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          return; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const contactInfo = decodedJwt.payload.own; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const did = contactInfo.did || decodedJwt.payload.iss; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      if (!did) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        logger.warn("Invalid contact info - missing DID"); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        const contactInfo = decodedJwt.payload.own; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        const did = contactInfo.did || decodedJwt.payload.iss; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        if (!did) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          logger.warn("Invalid contact info - missing DID"); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          this.$notify({ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            group: "alert", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            type: "danger", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            title: "Invalid Contact", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            text: "The contact DID is missing.", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          }); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          return; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        // Create contact object | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        contact = { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          did: did, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          name: contactInfo.name || "", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          publicKeyBase64: contactInfo.publicKeyBase64 || "", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          seesMe: contactInfo.seesMe || false, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          registered: contactInfo.registered || false, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        }; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      } else if (rawValue.startsWith(CONTACT_CSV_HEADER)) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        const lines = rawValue.split(/\n/); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        contact = libsUtil.csvLineToContact(lines[1]); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      } else { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        this.$notify({ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          group: "alert", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          type: "danger", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          title: "Invalid Contact", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          text: "The contact DID is missing.", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          title: "Error", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          text: "Could not determine the type of contact info. Please try again.", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        }); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        return; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // Create contact object | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const contact = { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        did: did, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        name: contactInfo.name || "", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        email: contactInfo.email || "", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        phone: contactInfo.phone || "", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        company: contactInfo.company || "", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        title: contactInfo.title || "", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        notes: contactInfo.notes || "", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      }; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      // Add contact but keep scanning | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      logger.info("Adding new contact to database:", { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        did: contact.did, | 
				
			
			
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
				 | 
				
					@ -468,7 +488,7 @@ export default class ContactQRScan extends Vue { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            title: "Contact Exists", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            text: "This contact has already been added to your list.", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          }, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          3000, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          5000, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        ); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        return; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      } | 
				
			
			
		
	
	
		
			
				
					| 
						
							
								
							
						
						
						
					 | 
				
				 | 
				
					
  |