Browse Source

Updates to contacts UI. Sweep for buildIdentity and buildHeaders

pull/35/head
Matthew Raymer 1 year ago
parent
commit
08137eb000
  1. 74
      src/views/AccountViewView.vue
  2. 48
      src/views/ContactAmountsView.vue
  3. 31
      src/views/ContactQRScanShowView.vue
  4. 191
      src/views/ContactsView.vue
  5. 11
      src/views/HomeView.vue
  6. 23
      src/views/NewEditProjectView.vue
  7. 34
      src/views/ProjectViewView.vue

74
src/views/AccountViewView.vue

@ -321,6 +321,29 @@ export default class AccountViewView extends Vue {
showB64Copy = false; showB64Copy = false;
showPubCopy = false; showPubCopy = false;
public async getIdentity(activeDid) {
await accountsDB.open();
const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === activeDid, accounts);
const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error(
"Attempted to load Give records with no identity available.",
);
}
return identity;
}
public async getHeaders(identity) {
const token = await accessToken(identity);
const headers = {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
};
return headers;
}
// call fn, copy text to the clipboard, then redo fn after 2 seconds // call fn, copy text to the clipboard, then redo fn after 2 seconds
doCopyTwoSecRedo(text, fn) { doCopyTwoSecRedo(text, fn) {
fn(); fn();
@ -356,16 +379,7 @@ export default class AccountViewView extends Vue {
this.lastName = settings?.lastName || ""; this.lastName = settings?.lastName || "";
this.showContactGives = !!settings?.showContactGivesInline; this.showContactGives = !!settings?.showContactGivesInline;
await accountsDB.open(); const identity = await this.getIdentity(this.activeDid);
const accounts = await accountsDB.accounts.toArray();
this.numAccounts = accounts.length;
const account = R.find((acc) => acc.did === this.activeDid, accounts);
const identity = JSON.parse(account?.identity || "null");
if (!identity) {
this.limitsMessage = "No identity.";
this.loadingLimits = false;
return;
}
this.publicHex = identity.keys[0].publicKeyHex; this.publicHex = identity.keys[0].publicKeyHex;
this.publicBase64 = Buffer.from(this.publicHex, "hex").toString("base64"); this.publicBase64 = Buffer.from(this.publicHex, "hex").toString("base64");
this.derivationPath = identity.keys[0].meta.derivationPath; this.derivationPath = identity.keys[0].meta.derivationPath;
@ -375,12 +389,23 @@ export default class AccountViewView extends Vue {
}); });
this.checkLimits(); this.checkLimits();
} catch (err) { } catch (err) {
if (
err.message ===
"Attempted to load Give records with no identity available."
) {
this.limitsMessage = "No identity.";
this.loadingLimits = false;
} else {
this.alertMessage = this.alertMessage =
"Clear your cache and start over (after data backup)."; "Clear your cache and start over (after data backup).";
console.error("Telling user to clear cache at page create because:", err); console.error(
"Telling user to clear cache at page create because:",
err,
);
this.alertTitle = "Error Creating Account"; this.alertTitle = "Error Creating Account";
} }
} }
}
public async updateShowContactAmounts() { public async updateShowContactAmounts() {
try { try {
@ -424,37 +449,32 @@ export default class AccountViewView extends Vue {
this.loadingLimits = true; this.loadingLimits = true;
this.limitsMessage = ""; this.limitsMessage = "";
try {
const url = this.apiServer + "/api/report/rateLimits"; const url = this.apiServer + "/api/report/rateLimits";
await accountsDB.open(); const identity = await this.getIdentity(this.activeDid);
const accounts = await accountsDB.accounts.toArray(); const headers = await this.getHeaders(identity);
const account = R.find((acc) => acc.did === this.activeDid, accounts);
const identity = JSON.parse(account?.identity || "null");
if (!identity) {
this.limitsMessage = "No identity.";
this.loadingLimits = false;
return;
}
const token = await accessToken(identity);
const headers = {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
};
try {
const resp = await this.axios.get(url, { headers }); const resp = await this.axios.get(url, { headers });
// axios throws an exception on a 400 // axios throws an exception on a 400
if (resp.status === 200) { if (resp.status === 200) {
this.limits = resp.data; this.limits = resp.data;
} }
} catch (error: unknown) { } catch (error: unknown) {
if (
error.message ===
"Attempted to load Give records with no identity available."
) {
this.limitsMessage = "No identity.";
this.loadingLimits = false;
} else {
const serverError = error as AxiosError; const serverError = error as AxiosError;
console.error("Bad response retrieving limits: ", serverError); console.error("Bad response retrieving limits: ", serverError);
// Anybody know how to access items inside "response.data" without this? // Anybody know how to access items inside "response.data" without this?
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const data: any = serverError.response?.data; const data: any = serverError.response?.data;
this.limitsMessage = data?.error?.message || "Bad server response."; this.limitsMessage = data?.error?.message || "Bad server response.";
} }
}
this.loadingLimits = false; this.loadingLimits = false;
} }

48
src/views/ContactAmountsView.vue

@ -143,7 +143,29 @@ export default class ContactsView extends Vue {
alertTitle = ""; alertTitle = "";
alertMessage = ""; alertMessage = "";
// 'created' hook runs when the Vue instance is first created public async getIdentity(activeDid) {
await accountsDB.open();
const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === activeDid, accounts);
const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error(
"Attempted to load Give records with no identity available.",
);
}
return identity;
}
public async getHeaders(identity) {
const token = await accessToken(identity);
const headers = {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
};
return headers;
}
async created() { async created() {
try { try {
await db.open(); await db.open();
@ -166,21 +188,9 @@ export default class ContactsView extends Vue {
} }
async loadGives(activeDid: string, contact: Contact) { async loadGives(activeDid: string, contact: Contact) {
// only load the private keys temporarily when needed
await accountsDB.open();
const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === activeDid, accounts);
const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error(
"An ID is chosen but there are no keys for it so it cannot be used to talk with the service.",
);
}
// load all the time I have given to them
try { try {
const identity = await this.getIdentity(this.activeDid);
let result = []; let result = [];
const url = const url =
this.apiServer + this.apiServer +
"/api/v2/report/gives?agentDid=" + "/api/v2/report/gives?agentDid=" +
@ -268,15 +278,7 @@ export default class ContactsView extends Vue {
}; };
// Create a signature using private key of identity // Create a signature using private key of identity
await accountsDB.open(); const identity = await this.getIdentity(this.activeDid);
const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === this.activeDid, accounts);
const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error(
"An ID is chosen but there are no keys for it so it cannot be used to talk with the service.",
);
}
if (identity.keys[0].privateKeyHex !== null) { if (identity.keys[0].privateKeyHex !== null) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const privateKeyHex: string = identity.keys[0].privateKeyHex!; const privateKeyHex: string = identity.keys[0].privateKeyHex!;

31
src/views/ContactQRScanShowView.vue

@ -50,6 +50,29 @@ export default class ContactQRScanShow extends Vue {
apiServer = ""; apiServer = "";
qrValue = ""; qrValue = "";
public async getIdentity(activeDid) {
await accountsDB.open();
const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === activeDid, accounts);
const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error(
"Attempted to load Give records with no identity available.",
);
}
return identity;
}
public async getHeaders(identity) {
const token = await accessToken(identity);
const headers = {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
};
return headers;
}
async created() { async created() {
await db.open(); await db.open();
const settings = await db.settings.get(MASTER_SETTINGS_KEY); const settings = await db.settings.get(MASTER_SETTINGS_KEY);
@ -62,13 +85,7 @@ export default class ContactQRScanShow extends Vue {
if (!account) { if (!account) {
this.alertMessage = "You have no identity yet."; this.alertMessage = "You have no identity yet.";
} else { } else {
const identity = JSON.parse(account?.identity || "null"); const identity = await this.getIdentity(this.activeDid);
if (!identity) {
throw new Error(
"An ID is chosen but there are no keys for it so it cannot be used to talk with the service.",
);
}
const publicKeyHex = identity.keys[0].publicKeyHex; const publicKeyHex = identity.keys[0].publicKeyHex;
const publicEncKey = Buffer.from(publicKeyHex, "hex").toString("base64"); const publicEncKey = Buffer.from(publicKeyHex, "hex").toString("base64");
const contactInfo = { const contactInfo = {

191
src/views/ContactsView.vue

@ -70,7 +70,7 @@
</div> </div>
<!-- Results List --> <!-- Results List -->
<ul class=""> <ul v-if="contacts.length > 0">
<li <li
class="border-b border-slate-300" class="border-b border-slate-300"
v-for="contact in contacts" v-for="contact in contacts"
@ -187,6 +187,7 @@
</div> </div>
</li> </li>
</ul> </ul>
<p v-else>This identity has no contacts.</p>
<AlertMessage <AlertMessage
:alertTitle="alertTitle" :alertTitle="alertTitle"
:alertMessage="alertMessage" :alertMessage="alertMessage"
@ -204,7 +205,6 @@ import { Contact } from "@/db/tables/contacts";
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings"; import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
import { accessToken, SimpleSigner } from "@/libs/crypto"; import { accessToken, SimpleSigner } from "@/libs/crypto";
import { import {
GiveServerRecord,
GiveVerifiableCredential, GiveVerifiableCredential,
RegisterVerifiableCredential, RegisterVerifiableCredential,
SERVICE_ID, SERVICE_ID,
@ -261,10 +261,10 @@ export default class ContactsView extends Vue {
); );
} }
async loadGives() { public async getIdentity(activeDid) {
await accountsDB.open(); await accountsDB.open();
const accounts = await accountsDB.accounts.toArray(); const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === this.activeDid, accounts); const account = R.find((acc) => acc.did === activeDid, accounts);
const identity = JSON.parse(account?.identity || "null"); const identity = JSON.parse(account?.identity || "null");
if (!identity) { if (!identity) {
@ -272,45 +272,43 @@ export default class ContactsView extends Vue {
"Attempted to load Give records with no identity available.", "Attempted to load Give records with no identity available.",
); );
} }
return identity;
}
// load all the time I have given public async getHeaders(identity) {
try {
const url =
this.apiServer +
"/api/v2/report/gives?agentDid=" +
encodeURIComponent(identity.did);
const token = await accessToken(identity); const token = await accessToken(identity);
const headers = { const headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
Authorization: "Bearer " + token, Authorization: "Bearer " + token,
}; };
const resp = await this.axios.get(url, { headers }); return headers;
//console.log("All gifts you've given:", resp.data); }
public async getHeadersAndIdentity(activeDid) {
const identity = await this.getIdentity(activeDid);
const headers = await this.getHeaders(identity);
return { headers, identity };
}
async loadGives() {
const handleResponse = (resp, descriptions, confirmed, unconfirmed) => {
if (resp.status === 200) { if (resp.status === 200) {
const contactDescriptions: Record<string, string> = {}; const allData = resp.data.data;
const contactConfirmed: Record<string, number> = {};
const contactUnconfirmed: Record<string, number> = {};
const allData: Array<GiveServerRecord> = resp.data.data;
for (const give of allData) { for (const give of allData) {
if (give.unit == "HUR") { if (give.unit === "HUR") {
const recipDid: string = give.recipientDid;
if (give.amountConfirmed) { if (give.amountConfirmed) {
const prevAmount = contactConfirmed[recipDid] || 0; const prevAmount = confirmed[give.agentDid] || 0;
contactConfirmed[recipDid] = prevAmount + give.amount; confirmed[give.agentDid] = prevAmount + give.amount;
} else { } else {
const prevAmount = contactUnconfirmed[recipDid] || 0; const prevAmount = unconfirmed[give.agentDid] || 0;
contactUnconfirmed[recipDid] = prevAmount + give.amount; unconfirmed[give.agentDid] = prevAmount + give.amount;
} }
if (!contactDescriptions[recipDid] && give.description) { if (!descriptions[give.agentDid] && give.description) {
// Since many make the tooltip too big, we'll just use the latest. descriptions[give.agentDid] = give.description;
contactDescriptions[recipDid] = give.description;
} }
} }
} }
//console.log("Done retrieving gives", contactConfirmed);
this.givenByMeDescriptions = contactDescriptions;
this.givenByMeConfirmed = contactConfirmed;
this.givenByMeUnconfirmed = contactUnconfirmed;
} else { } else {
console.error( console.error(
"Got bad response status & data of", "Got bad response status & data of",
@ -319,60 +317,56 @@ export default class ContactsView extends Vue {
); );
this.alertTitle = "Error With Server"; this.alertTitle = "Error With Server";
this.alertMessage = this.alertMessage =
"Got an error retrieving your given time from the server."; "Got an error retrieving your " +
} resp.config.url.includes("recipientDid")
} catch (error) { ? "received"
this.alertTitle = "Error With Server"; : "given" + " time from the server.";
this.alertMessage = error as string;
} }
};
// load all the time I have received
try { try {
const url = const { headers, identity } = await this.getHeadersAndIdentity(
this.activeDid,
);
const givenByUrl =
this.apiServer +
"/api/v2/report/gives?agentDid=" +
encodeURIComponent(identity.did);
const givenToUrl =
this.apiServer + this.apiServer +
"/api/v2/report/gives?recipientDid=" + "/api/v2/report/gives?recipientDid=" +
encodeURIComponent(identity.did); encodeURIComponent(identity.did);
const token = await accessToken(identity);
const headers = { const [givenByMeResp, givenToMeResp] = await Promise.all([
"Content-Type": "application/json", this.axios.get(givenByUrl, { headers }),
Authorization: "Bearer " + token, this.axios.get(givenToUrl, { headers }),
}; ]);
const resp = await this.axios.get(url, { headers });
//console.log("All gifts you've recieved:", resp.data); const givenByMeDescriptions = {};
if (resp.status === 200) { const givenByMeConfirmed = {};
const contactDescriptions: Record<string, string> = {}; const givenByMeUnconfirmed = {};
const contactConfirmed: Record<string, number> = {}; handleResponse(
const contactUnconfirmed: Record<string, number> = {}; givenByMeResp,
const allData: Array<GiveServerRecord> = resp.data.data; givenByMeDescriptions,
for (const give of allData) { givenByMeConfirmed,
if (give.unit == "HUR") { givenByMeUnconfirmed,
if (give.amountConfirmed) {
const prevAmount = contactConfirmed[give.agentDid] || 0;
contactConfirmed[give.agentDid] = prevAmount + give.amount;
} else {
const prevAmount = contactUnconfirmed[give.agentDid] || 0;
contactUnconfirmed[give.agentDid] = prevAmount + give.amount;
}
if (!contactDescriptions[give.agentDid] && give.description) {
// Since many make the tooltip too big, we'll just use the latest.
contactDescriptions[give.agentDid] = give.description;
}
}
}
//console.log("Done retrieving receipts", contactConfirmed);
this.givenToMeDescriptions = contactDescriptions;
this.givenToMeConfirmed = contactConfirmed;
this.givenToMeUnconfirmed = contactUnconfirmed;
} else {
console.error(
"Got bad response status & data of",
resp.status,
resp.data,
); );
this.alertTitle = "Error With Server"; this.givenByMeDescriptions = givenByMeDescriptions;
this.alertMessage = this.givenByMeConfirmed = givenByMeConfirmed;
"Got an error retrieving your received time from the server."; this.givenByMeUnconfirmed = givenByMeUnconfirmed;
}
const givenToMeDescriptions = {};
const givenToMeConfirmed = {};
const givenToMeUnconfirmed = {};
handleResponse(
givenToMeResp,
givenToMeDescriptions,
givenToMeConfirmed,
givenToMeUnconfirmed,
);
this.givenToMeDescriptions = givenToMeDescriptions;
this.givenToMeConfirmed = givenToMeConfirmed;
this.givenToMeUnconfirmed = givenToMeUnconfirmed;
} catch (error) { } catch (error) {
this.alertTitle = "Error With Server"; this.alertTitle = "Error With Server";
this.alertMessage = error as string; this.alertMessage = error as string;
@ -430,15 +424,8 @@ export default class ContactsView extends Vue {
"?", "?",
) )
) { ) {
await accountsDB.open(); const identity = await this.getIdentity(this.activeDid);
const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === this.activeDid, accounts);
const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error("No identity found.");
}
// Make a claim
const vcClaim: RegisterVerifiableCredential = { const vcClaim: RegisterVerifiableCredential = {
"@context": "https://schema.org", "@context": "https://schema.org",
"@type": "RegisterAction", "@type": "RegisterAction",
@ -518,19 +505,8 @@ export default class ContactsView extends Vue {
this.apiServer + this.apiServer +
"/api/report/" + "/api/report/" +
(visibility ? "canSeeMe" : "cannotSeeMe"); (visibility ? "canSeeMe" : "cannotSeeMe");
await accountsDB.open(); const identity = await this.getIdentity(this.activeDid);
const accounts = await accountsDB.accounts.toArray(); const headers = await this.getHeaders(identity);
const account = R.find((acc) => acc.did === this.activeDid, accounts);
const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error("No identity found.");
}
const token = await accessToken(identity);
const headers = {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
};
const payload = JSON.stringify({ did: contact.did }); const payload = JSON.stringify({ did: contact.did });
try { try {
@ -558,19 +534,6 @@ export default class ContactsView extends Vue {
this.apiServer + this.apiServer +
"/api/report/canDidExplicitlySeeMe?did=" + "/api/report/canDidExplicitlySeeMe?did=" +
encodeURIComponent(contact.did); encodeURIComponent(contact.did);
await accountsDB.open();
const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === this.activeDid, accounts);
const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error("No identity found.");
}
const token = await accessToken(identity);
const headers = {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
};
try { try {
const resp = await this.axios.get(url, { headers }); const resp = await this.axios.get(url, { headers });
@ -615,13 +578,7 @@ export default class ContactsView extends Vue {
} }
async onClickAddGive(fromDid: string, toDid: string): Promise<void> { async onClickAddGive(fromDid: string, toDid: string): Promise<void> {
await accountsDB.open(); const identity = this.getIdentity(this.activeDid);
const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === this.activeDid, accounts);
const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error("No identity found.");
}
// if they have unconfirmed amounts, ask to confirm those first // if they have unconfirmed amounts, ask to confirm those first
if (toDid == identity?.did && this.givenToMeUnconfirmed[fromDid] > 0) { if (toDid == identity?.did && this.givenToMeUnconfirmed[fromDid] > 0) {

11
src/views/HomeView.vue

@ -17,7 +17,7 @@
@click="openDialog(contact)" @click="openDialog(contact)"
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-2" class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-2"
> >
{{ contact.name }} {{ contact.name || "(no name)" }}
</button> </button>
<span v-if="allContacts.length > 0">&nbsp;or&nbsp;</span> <span v-if="allContacts.length > 0">&nbsp;or&nbsp;</span>
<button @click="openDialog()" class="text-blue-500"> <button @click="openDialog()" class="text-blue-500">
@ -269,14 +269,7 @@ export default class HomeView extends Vue {
return; return;
} }
const account = this.allAccounts.find((acc) => acc.did === this.activeDid); const identity = await this.getIdentity(this.activeDid);
const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error(
"An ID is chosen but there are no keys for it so it cannot be used to talk with the service.",
);
}
createAndSubmitGive( createAndSubmitGive(
this.axios, this.axios,

23
src/views/NewEditProjectView.vue

@ -101,6 +101,29 @@ export default class NewEditProjectView extends Vue {
description = ""; description = "";
errorMessage = ""; errorMessage = "";
public async getIdentity(activeDid) {
await accountsDB.open();
const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === activeDid, accounts);
const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error(
"Attempted to load Give records with no identity available.",
);
}
return identity;
}
public async getHeaders(identity) {
const token = await accessToken(identity);
const headers = {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
};
return headers;
}
projectId = localStorage.getItem("projectId") || ""; projectId = localStorage.getItem("projectId") || "";
isHiddenSave = false; isHiddenSave = false;
isHiddenSpinner = true; isHiddenSpinner = true;

34
src/views/ProjectViewView.vue

@ -176,6 +176,29 @@ export default class ProjectViewView extends Vue {
alertMessage = ""; alertMessage = "";
alertTitle = ""; alertTitle = "";
public async getIdentity(activeDid) {
await accountsDB.open();
const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === activeDid, accounts);
const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error(
"Attempted to load Give records with no identity available.",
);
}
return identity;
}
public async getHeaders(identity) {
const token = await accessToken(identity);
const headers = {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
};
return headers;
}
onEditClick() { onEditClick() {
localStorage.setItem("projectId", this.projectId as string); localStorage.setItem("projectId", this.projectId as string);
const route = { const route = {
@ -293,17 +316,8 @@ export default class ProjectViewView extends Vue {
return; return;
} }
const accounts = await accountsDB.accounts.toArray();
const account = accounts.find((acc) => acc.did === this.activeDid);
const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error(
"An ID is chosen but there are no keys for it so it cannot be used to talk with the service.",
);
}
try { try {
const identity = await this.getIdentity(this.activeDid);
const result = await createAndSubmitGive( const result = await createAndSubmitGive(
this.axios, this.axios,
this.apiServer, this.apiServer,

Loading…
Cancel
Save