add ability to import from Endorser Mobile CSV
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
|
||||
tasks:
|
||||
|
||||
- anchor hash into BTC
|
||||
- image on give
|
||||
- Show a camera to take a picture
|
||||
- Scale the image to a reasonable size
|
||||
@@ -11,7 +12,6 @@ tasks:
|
||||
- Rates - images erased?
|
||||
- image not associated with JWT ULID since that's assigned later
|
||||
- mark a project as inactive
|
||||
- make a shortcut for BVC
|
||||
- add share button for sending a message to confirmers when we can't see the claim
|
||||
- add TimeSafari as a shareable app https://developer.mozilla.org/en-US/docs/Web/Manifest/share_target
|
||||
- choose a project's alternative agent ("authorized representative") via a contact chooser (not just copy-paste a DID)
|
||||
|
||||
@@ -8,6 +8,8 @@ import { Contact } from "@/db/tables/contacts";
|
||||
export const SCHEMA_ORG_CONTEXT = "https://schema.org";
|
||||
// the object in RegisterAction claims
|
||||
export const SERVICE_ID = "endorser.ch";
|
||||
// the header line for contacts exported via Endorser Mobile
|
||||
export const CONTACT_CSV_HEADER = "name,did,pubKeyBase64,seesMe,registered";
|
||||
// the prefix for the contact URL
|
||||
export const CONTACT_URL_PREFIX = "https://endorser.ch";
|
||||
// the suffix for the contact URL
|
||||
|
||||
@@ -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(
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user