|
|
@ -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; |
|
|
|
} |
|
|
|