|
|
@ -26,10 +26,10 @@ |
|
|
|
> |
|
|
|
<fa icon="qrcode" class="fa-fw text-2xl" /> |
|
|
|
</router-link> |
|
|
|
<input |
|
|
|
<textarea |
|
|
|
type="text" |
|
|
|
placeholder="URL or DID, Name, Public Key, Next Public Key Hash" |
|
|
|
class="block w-full rounded-l border border-r-0 border-slate-400 px-3 py-2" |
|
|
|
class="block w-full rounded-l border border-r-0 border-slate-400 px-3 py-2 h-10" |
|
|
|
v-model="contactInput" |
|
|
|
/> |
|
|
|
<button |
|
|
@ -297,6 +297,7 @@ import { |
|
|
|
SimpleSigner, |
|
|
|
} from "@/libs/crypto"; |
|
|
|
import { |
|
|
|
CONTACT_CSV_HEADER, |
|
|
|
CONTACT_URL_PREFIX, |
|
|
|
GiveServerRecord, |
|
|
|
GiveVerifiableCredential, |
|
|
@ -307,6 +308,7 @@ import * as libsUtil from "@/libs/util"; |
|
|
|
import QuickNav from "@/components/QuickNav.vue"; |
|
|
|
import EntityIcon from "@/components/EntityIcon.vue"; |
|
|
|
import { Account } from "@/db/tables/accounts"; |
|
|
|
import { IndexableType } from "dexie"; |
|
|
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-var-requires |
|
|
|
const Buffer = require("buffer/").Buffer; |
|
|
@ -365,7 +367,7 @@ export default class ContactsView extends Vue { |
|
|
|
); |
|
|
|
|
|
|
|
if (this.contactEndorserUrl) { |
|
|
|
await this.newContactFromScan(this.contactEndorserUrl); |
|
|
|
await this.addContactFromScan(this.contactEndorserUrl); |
|
|
|
localStorage.removeItem("contactEndorserUrl"); |
|
|
|
this.contactEndorserUrl = ""; |
|
|
|
} |
|
|
@ -535,7 +537,46 @@ export default class ContactsView extends Vue { |
|
|
|
} |
|
|
|
|
|
|
|
if (this.contactInput.startsWith(CONTACT_URL_PREFIX)) { |
|
|
|
await this.newContactFromScan(this.contactInput); |
|
|
|
await this.addContactFromScan(this.contactInput); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (this.contactInput.startsWith(CONTACT_CSV_HEADER)) { |
|
|
|
const lines = this.contactInput.split(/\n/); |
|
|
|
const lineAdded = []; |
|
|
|
for (const line of lines) { |
|
|
|
if (!line.trim() || line.startsWith(CONTACT_CSV_HEADER)) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
lineAdded.push(this.addContactFromEndorserMobileLine(line)); |
|
|
|
} |
|
|
|
try { |
|
|
|
await Promise.all(lineAdded); |
|
|
|
this.$notify( |
|
|
|
{ |
|
|
|
group: "alert", |
|
|
|
type: "success", |
|
|
|
title: "Contacts Added", |
|
|
|
text: "Each contact was added. Nothing was sent to the server.", |
|
|
|
}, |
|
|
|
-1, // keeping it up so that the "visibility" message is seen |
|
|
|
); |
|
|
|
} catch (e) { |
|
|
|
this.$notify( |
|
|
|
{ |
|
|
|
group: "alert", |
|
|
|
type: "danger", |
|
|
|
title: "Contacts Maybe Added", |
|
|
|
text: "An error occurred. Some contacts may have been added.", |
|
|
|
}, |
|
|
|
-1, |
|
|
|
); |
|
|
|
} |
|
|
|
const allContacts = await db.contacts.toArray(); |
|
|
|
this.contacts = R.sort( |
|
|
|
(a: Contact, b) => (a.name || "").localeCompare(b.name || ""), |
|
|
|
allContacts, |
|
|
|
); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
@ -576,7 +617,48 @@ export default class ContactsView extends Vue { |
|
|
|
await this.addContact(newContact); |
|
|
|
} |
|
|
|
|
|
|
|
async newContactFromScan(url: string): Promise<void> { |
|
|
|
async addContactFromEndorserMobileLine(line: string): Promise<IndexableType> { |
|
|
|
// Note that Endorser Mobile puts name first, then did, etc. |
|
|
|
let name = line; |
|
|
|
let did = ""; |
|
|
|
let publicKeyInput, seesMe, registered; |
|
|
|
const commaPos1 = line.indexOf(","); |
|
|
|
if (commaPos1 > -1) { |
|
|
|
name = line.substring(0, commaPos1).trim(); |
|
|
|
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 = { |
|
|
|
did, |
|
|
|
name, |
|
|
|
publicKeyBase64, |
|
|
|
seesMe, |
|
|
|
registered, |
|
|
|
}; |
|
|
|
return db.contacts.add(newContact); |
|
|
|
} |
|
|
|
|
|
|
|
async addContactFromScan(url: string): Promise<void> { |
|
|
|
const payload = getContactPayloadFromJwtUrl(url); |
|
|
|
if (!payload) { |
|
|
|
this.$notify( |
|
|
@ -661,7 +743,7 @@ export default class ContactsView extends Vue { |
|
|
|
} |
|
|
|
if (err.name === "ConstraintError") { |
|
|
|
message += |
|
|
|
"Check that the contact doesn't conflict with any you already have."; |
|
|
|
" Check that the contact doesn't conflict with any you already have."; |
|
|
|
} |
|
|
|
this.$notify( |
|
|
|
{ |
|
|
|