From d293d0c3e2721f91cdf87688713213d72a004757 Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Mon, 20 Mar 2023 09:19:40 -0600 Subject: [PATCH 01/10] show/hide given totals based on setting rather than URL parameter --- project.yaml | 2 ++ src/views/ContactsView.vue | 14 +++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/project.yaml b/project.yaml index a94ad5a..6b17aae 100644 --- a/project.yaml +++ b/project.yaml @@ -17,6 +17,8 @@ - .5 Add page to show seed. - 01 Provide a way to import the non-sensitive data. - 01 Provide way to share your contact info. + - 01 register others + - .2 move all "identity" references to temporary account access - .1 remove "scan new contact" - contacts v+ : diff --git a/src/views/ContactsView.vue b/src/views/ContactsView.vue index 0f0ccce..fa91041 100644 --- a/src/views/ContactsView.vue +++ b/src/views/ContactsView.vue @@ -135,13 +135,14 @@ import { AxiosError } from "axios"; import * as didJwt from "did-jwt"; import * as R from "ramda"; +import { IIdentifier } from "@veramo/core"; import { Options, Vue } from "vue-class-component"; import { AppString } from "@/constants/app"; import { accessToken, SimpleSigner } from "@/libs/crypto"; -import { IIdentifier } from "@veramo/core"; -import { accountsDB, db } from "../db"; -import { Contact } from "../db/tables/contacts"; +import { accountsDB, db } from "@/db"; +import { Contact } from "@/db/tables/contacts"; +import { MASTER_SETTINGS_KEY } from "@/db/tables/settings"; export interface GiveVerifiableCredential { "@context": string; @@ -174,13 +175,12 @@ export default class ContactsView extends Vue { this.identity = JSON.parse(accounts[0].identity); await db.open(); - this.contacts = await db.contacts.toArray(); - - const params = new URLSearchParams(window.location.search); - this.showGiveTotals = params.get("showGiveTotals") == "true"; + const settings = await db.settings.get(MASTER_SETTINGS_KEY); + this.showGiveTotals = !!settings?.showContactGivesInline; if (this.showGiveTotals) { this.loadGives(); } + this.contacts = await db.contacts.toArray(); } async onClickNewContact(): Promise { From 5c75ad80afa2b3a1bb598be98a333d1cb1d9c9b3 Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Mon, 20 Mar 2023 18:58:55 -0600 Subject: [PATCH 02/10] add correct encodings for public keys, plus some instructions for entering a contact --- package-lock.json | 7 ++++--- package.json | 1 + project.yaml | 3 ++- src/views/AccountViewView.vue | 30 ++++++++++++++++++------------ src/views/ContactsView.vue | 8 ++++++++ src/views/HelpView.vue | 12 ++++++++++++ 6 files changed, 45 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3814b71..eb4d759 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "@vueuse/core": "^9.6.0", "@zxing/text-encoding": "^0.9.0", "axios": "^1.2.2", + "buffer": "^6.0.3", "class-transformer": "^0.5.1", "core-js": "^3.26.1", "dexie": "^3.2.2", @@ -7510,9 +7511,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.11.9", - "resolved": "https://registry.npmmirror.com/@types/node/-/node-18.11.9.tgz", - "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==" + "version": "18.15.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.5.tgz", + "integrity": "sha512-Ark2WDjjZO7GmvsyFFf81MXuGTA/d6oP38anyxWOL6EREyBKAxKoFHwBhaZxCfLRLpO8JgVXwqOwSwa7jRcjew==" }, "node_modules/@types/normalize-package-data": { "version": "2.4.1", diff --git a/package.json b/package.json index 9a7a3cb..b805938 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "@vueuse/core": "^9.6.0", "@zxing/text-encoding": "^0.9.0", "axios": "^1.2.2", + "buffer": "^6.0.3", "class-transformer": "^0.5.1", "core-js": "^3.26.1", "dexie": "^3.2.2", diff --git a/project.yaml b/project.yaml index 6b17aae..097be2e 100644 --- a/project.yaml +++ b/project.yaml @@ -12,7 +12,7 @@ - replace user-affecting console.logs with error messages (eg. catches) - contacts v1 : - - .2 show gives with new setting + - remove 'copy' until it works - 01 show gives with confirmations - .5 Add page to show seed. - 01 Provide a way to import the non-sensitive data. @@ -20,6 +20,7 @@ - 01 register others - .2 move all "identity" references to temporary account access - .1 remove "scan new contact" + - get 'copy' to work on account page - contacts v+ : - .5 make advanced "show/hide amounts" button into a nice UI toggle diff --git a/src/views/AccountViewView.vue b/src/views/AccountViewView.vue index 43407be..a0c8ba6 100644 --- a/src/views/AccountViewView.vue +++ b/src/views/AccountViewView.vue @@ -110,10 +110,20 @@ -
Public Key
+
Public Key (base 64)
- {{ publicHex }} + + {{ publicBase64 }} + + +
+ +
Public Key (hex)
+
+ + {{ publicHex }} @@ -138,15 +148,6 @@ Edit Identity -

Contact Actions

- - - Scan New Contact - -

Data

Make sure you have your backup file (above), then contact us.
+ +

+ How do I add someone to my contacts? +

+

+ Tell them to copy their ID, which typically starts with "did:ethr:...", + and send it to you. Go to the Contacts + page and enter that into the top + form. You may add a name by adding a comma followed by their name; you + may also add their public key by adding another comma followed by the + key. +

From fdac4f26652c0370b9b3e2d24a5610bb25b58565 Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Mon, 20 Mar 2023 19:29:58 -0600 Subject: [PATCH 03/10] allow deletion of a contact --- src/views/ContactsView.vue | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/views/ContactsView.vue b/src/views/ContactsView.vue index 9c67299..6500b80 100644 --- a/src/views/ContactsView.vue +++ b/src/views/ContactsView.vue @@ -96,7 +96,12 @@ {{ contact.name || "(no name)" }}
{{ contact.did }}
-
{{ contact.publicKeyBase64 }}
+
+ Public Key (base 64): {{ contact.publicKeyBase64 }} +
+
to: {{ givenByMeTotals[contact.did] || 0 }} @@ -183,7 +188,11 @@ export default class ContactsView extends Vue { if (this.showGiveTotals) { this.loadGives(); } - this.contacts = await db.contacts.toArray(); + const allContacts = await db.contacts.toArray(); + this.contacts = R.sort( + (a: Contact, b) => (a.name || "").localeCompare(b.name || ""), + allContacts + ); } async onClickNewContact(): Promise { @@ -206,7 +215,11 @@ export default class ContactsView extends Vue { } const newContact = { did, name, publicKeyBase64 }; await db.contacts.add(newContact); - this.contacts = this.contacts.concat([newContact]); + const allContacts = this.contacts.concat([newContact]); + this.contacts = R.sort( + (a: Contact, b) => (a.name || "").localeCompare(b.name || ""), + allContacts + ); } async loadGives() { @@ -281,6 +294,22 @@ export default class ContactsView extends Vue { } } + async deleteContact(contact: Contact) { + if ( + confirm( + "Are you sure you want to delete " + + this.nameForDid(this.contacts, contact.did) + + " with DID " + + contact.did + + "?" + ) + ) { + await db.open(); + await db.contacts.delete(contact.did); + this.contacts = R.without([contact], this.contacts); + } + } + // from https://stackoverflow.com/a/175787/845494 // private isNumeric(str: string): boolean { From 0ea123e02852181c42bd0d50409c98f197a435e9 Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Mon, 20 Mar 2023 19:46:17 -0600 Subject: [PATCH 04/10] fix highlights on bottom row --- project.yaml | 2 +- src/views/ContactsView.vue | 4 ++-- src/views/ProjectsView.vue | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/project.yaml b/project.yaml index 097be2e..8a72024 100644 --- a/project.yaml +++ b/project.yaml @@ -17,9 +17,9 @@ - .5 Add page to show seed. - 01 Provide a way to import the non-sensitive data. - 01 Provide way to share your contact info. + - 01 allow visibility to others - 01 register others - .2 move all "identity" references to temporary account access - - .1 remove "scan new contact" - get 'copy' to work on account page - contacts v+ : diff --git a/src/views/ContactsView.vue b/src/views/ContactsView.vue index 6500b80..9a57079 100644 --- a/src/views/ContactsView.vue +++ b/src/views/ContactsView.vue @@ -9,7 +9,7 @@ > -
  • +
  • -
  • +
  • -
  • +
  • -
  • +
  • Date: Tue, 21 Mar 2023 18:49:40 -0600 Subject: [PATCH 05/10] add functions for visibility to contacts --- project.yaml | 1 + src/main.ts | 54 +++++++------- src/views/ContactsView.vue | 140 ++++++++++++++++++++++++++++++++++++- 3 files changed, 168 insertions(+), 27 deletions(-) diff --git a/project.yaml b/project.yaml index 8a72024..32c82a5 100644 --- a/project.yaml +++ b/project.yaml @@ -13,6 +13,7 @@ - contacts v1 : - remove 'copy' until it works + - switch to prod server - 01 show gives with confirmations - .5 Add page to show seed. - 01 Provide a way to import the non-sensitive data. diff --git a/src/main.ts b/src/main.ts index 8fc26a4..4549049 100644 --- a/src/main.ts +++ b/src/main.ts @@ -10,46 +10,52 @@ import "./assets/styles/tailwind.css"; import { library } from "@fortawesome/fontawesome-svg-core"; import { + faCalendar, faChevronLeft, - faHouseChimney, - faMagnifyingGlass, - faFolderOpen, - faHand, + faCircleCheck, faCircleUser, faCopy, - faShareNodes, - faQrcode, - faUser, - faUsers, + faEllipsisVertical, + faEye, + faEyeSlash, + faFolderOpen, + faHand, + faHouseChimney, + faMagnifyingGlass, faPen, faPlus, - faTrashCan, - faCalendar, - faEllipsisVertical, + faQrcode, + faRotate, + faShareNodes, faSpinner, - faCircleCheck, + faTrashCan, + faUser, + faUsers, faXmark, } from "@fortawesome/free-solid-svg-icons"; library.add( + faCalendar, faChevronLeft, - faHouseChimney, - faMagnifyingGlass, - faFolderOpen, - faHand, + faCircleCheck, faCircleUser, faCopy, - faShareNodes, - faQrcode, - faUser, - faUsers, + faEllipsisVertical, + faEye, + faEyeSlash, + faFolderOpen, + faHand, + faHouseChimney, + faMagnifyingGlass, faPen, faPlus, - faTrashCan, - faCalendar, - faEllipsisVertical, + faQrcode, + faRotate, + faShareNodes, faSpinner, - faCircleCheck, + faTrashCan, + faUser, + faUsers, faXmark ); diff --git a/src/views/ContactsView.vue b/src/views/ContactsView.vue index 9a57079..3cad6b9 100644 --- a/src/views/ContactsView.vue +++ b/src/views/ContactsView.vue @@ -99,8 +99,26 @@
    Public Key (base 64): {{ contact.publicKeyBase64 }}
    - + + + +
    @@ -310,6 +328,89 @@ export default class ContactsView extends Vue { } } + async setVisibility(contact: Contact, visibility: boolean) { + const endorserApiServer = AppString.DEFAULT_ENDORSER_API_SERVER; + const url = + endorserApiServer + + "/api/report/" + + (visibility ? "canSeeMe" : "cannotSeeMe"); + await accountsDB.open(); + const accounts = await accountsDB.accounts.toArray(); + const identity = JSON.parse(accounts[0].identity); + const token = await accessToken(identity); + const headers = { + "Content-Type": "application/json", + Authorization: "Bearer " + token, + }; + const payload = JSON.stringify({ did: contact.did }); + + try { + const resp = await this.axios.post(url, payload, { headers }); + if (resp.status === 200) { + contact.seesMe = visibility; + db.contacts.update(contact.did, { seesMe: visibility }); + } else { + this.alertTitle = "Error from Server"; + console.log("Bad response setting visibility: ", resp.data); + if (resp.data.error?.message) { + this.alertMessage = resp.data.error?.message; + } else { + this.alertMessage = "Bad server response of " + resp.status; + } + this.isAlertVisible = true; + } + } catch (err) { + this.alertTitle = "Error from Server"; + this.alertMessage = err as string; + this.isAlertVisible = true; + } + } + + async checkVisibility(contact: Contact) { + const endorserApiServer = AppString.DEFAULT_ENDORSER_API_SERVER; + const url = + endorserApiServer + + "/api/report/canDidExplicitlySeeMe?did=" + + encodeURIComponent(contact.did); + await accountsDB.open(); + const accounts = await accountsDB.accounts.toArray(); + const identity = JSON.parse(accounts[0].identity); + const token = await accessToken(identity); + const headers = { + "Content-Type": "application/json", + Authorization: "Bearer " + token, + }; + + try { + const resp = await this.axios.get(url, { headers }); + if (resp.status === 200) { + const visibility = resp.data; + contact.seesMe = visibility; + db.contacts.update(contact.did, { seesMe: visibility }); + this.alertTitle = "Refreshed"; + this.alertMessage = + this.nameForContact(contact, true) + + " can " + + (visibility ? "" : "not ") + + "see your activity."; + this.isAlertVisible = true; + } else { + this.alertTitle = "Error from Server"; + console.log("Bad response checking visibility: ", resp.data); + if (resp.data.error?.message) { + this.alertMessage = resp.data.error?.message; + } else { + this.alertMessage = "Bad server response of " + resp.status; + } + this.isAlertVisible = true; + } + } catch (err) { + this.alertTitle = "Error from Server"; + this.alertMessage = err as string; + this.isAlertVisible = true; + } + } + // from https://stackoverflow.com/a/175787/845494 // private isNumeric(str: string): boolean { @@ -318,7 +419,11 @@ export default class ContactsView extends Vue { private nameForDid(contacts: Array, did: string): string { const contact = R.find((con) => con.did == did, contacts); - return contact?.name || "this unnamed user"; + return this.nameForContact(contact); + } + + private nameForContact(contact?: Contact, capitalize?: boolean): string { + return contact?.name || (capitalize ? "T" : "t") + "this unnamed user"; } async onClickAddGive(fromDid: string, toDid: string): Promise { @@ -479,3 +584,32 @@ export default class ContactsView extends Vue { } } + + From 7a7c5b6ba11b7f343dfca282db1ef03f528dcc3c Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Tue, 21 Mar 2023 20:45:53 -0600 Subject: [PATCH 06/10] add registration check and the ability to register someone --- src/main.ts | 6 ++ src/views/AccountViewView.vue | 80 +++++++++++++++++++++++- src/views/ContactsView.vue | 113 +++++++++++++++++++++++++++++++++- 3 files changed, 194 insertions(+), 5 deletions(-) diff --git a/src/main.ts b/src/main.ts index 4549049..88dbedb 100644 --- a/src/main.ts +++ b/src/main.ts @@ -13,6 +13,7 @@ import { faCalendar, faChevronLeft, faCircleCheck, + faCircleQuestion, faCircleUser, faCopy, faEllipsisVertical, @@ -23,6 +24,8 @@ import { faHouseChimney, faMagnifyingGlass, faPen, + faPersonCircleCheck, + faPersonCircleQuestion, faPlus, faQrcode, faRotate, @@ -38,6 +41,7 @@ library.add( faCalendar, faChevronLeft, faCircleCheck, + faCircleQuestion, faCircleUser, faCopy, faEllipsisVertical, @@ -48,6 +52,8 @@ library.add( faHouseChimney, faMagnifyingGlass, faPen, + faPersonCircleCheck, + faPersonCircleQuestion, faPlus, faQrcode, faRotate, diff --git a/src/views/AccountViewView.vue b/src/views/AccountViewView.vue index a0c8ba6..5507537 100644 --- a/src/views/AccountViewView.vue +++ b/src/views/AccountViewView.vue @@ -202,6 +202,29 @@
    +
    + +
    + Rate Limits +

    + You have done {{ limits.doneClaimsThisWeek }} claims out of + {{ limits.maxClaimsPerWeek }} for this week. Your claims counter + resets at {{ readableTime(limits.nextWeekBeginDateTime) }} +

    +

    + You have done {{ limits.doneRegistrationsThisMonth }} registrations + out of {{ limits.maxRegistrationsPerMonth }} for this month. Your + registrations counter resets at + {{ readableTime(limits.nextMonthBeginDateTime) }} +

    +
    +
    +
    + - + + + +
    to: {{ givenByMeTotals[contact.did] || 0 }} @@ -170,6 +185,8 @@ import { MASTER_SETTINGS_KEY } from "@/db/tables/settings"; // eslint-disable-next-line @typescript-eslint/no-var-requires const Buffer = require("buffer/").Buffer; +const SERVICE_ID = "endorser.ch"; + export interface GiveVerifiableCredential { "@context": string; "@type": string; @@ -179,6 +196,14 @@ export interface GiveVerifiableCredential { recipient: { identifier: string }; } +export interface RegisterVerifiableCredential { + "@context": string; + "@type": string; + agent: { identifier: string }; + object: string; + recipient: { identifier: string }; +} + @Options({ components: {}, }) @@ -319,7 +344,7 @@ export default class ContactsView extends Vue { this.nameForDid(this.contacts, contact.did) + " with DID " + contact.did + - "?" + " ?" ) ) { await db.open(); @@ -328,6 +353,88 @@ export default class ContactsView extends Vue { } } + async register(contact: Contact) { + if ( + confirm( + "Are you sure you want to use one of your registrations for " + + this.nameForDid(this.contacts, contact.did) + + "?" + ) + ) { + await accountsDB.open(); + const accounts = await accountsDB.accounts.toArray(); + const identity = JSON.parse(accounts[0].identity); + // Make a claim + const vcClaim: RegisterVerifiableCredential = { + "@context": "https://schema.org", + "@type": "RegisterAction", + agent: { identifier: identity.did }, + object: SERVICE_ID, + recipient: { identifier: contact.did }, + }; + // Make a payload for the claim + const vcPayload = { + vc: { + "@context": ["https://www.w3.org/2018/credentials/v1"], + type: ["VerifiableCredential"], + credentialSubject: vcClaim, + }, + }; + // Create a signature using private key of identity + if (identity.keys[0].privateKeyHex !== null) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const privateKeyHex: string = identity.keys[0].privateKeyHex!; + const signer = await SimpleSigner(privateKeyHex); + const alg = undefined; + // Create a JWT for the request + const vcJwt: string = await didJwt.createJWT(vcPayload, { + alg: alg, + issuer: identity.did, + signer: signer, + }); + + // Make the xhr request payload + const payload = JSON.stringify({ jwtEncoded: vcJwt }); + const endorserApiServer = AppString.DEFAULT_ENDORSER_API_SERVER; + const url = endorserApiServer + "/api/v2/claim"; + const token = await accessToken(identity); + const headers = { + "Content-Type": "application/json", + Authorization: "Bearer " + token, + }; + + try { + const resp = await this.axios.post(url, payload, { headers }); + //console.log("Got resp data:", resp.data); + if (resp.data?.success?.handleId) { + contact.registered = true; + db.contacts.update(contact.did, { registered: true }); + + this.alertTitle = "Registration Success"; + this.alertMessage = contact.name + " has been registered."; + this.isAlertVisible = true; + } + } catch (error) { + let userMessage = "There was an error. See logs for more info."; + const serverError = error as AxiosError; + if (serverError) { + if (serverError.message) { + userMessage = serverError.message; // Info for the user + } else { + userMessage = JSON.stringify(serverError.toJSON()); + } + } else { + userMessage = error as string; + } + // Now set that error for the user to see. + this.alertTitle = "Error with Server"; + this.alertMessage = userMessage; + this.isAlertVisible = true; + } + } + } + } + async setVisibility(contact: Contact, visibility: boolean) { const endorserApiServer = AppString.DEFAULT_ENDORSER_API_SERVER; const url = @@ -387,6 +494,7 @@ export default class ContactsView extends Vue { const visibility = resp.data; contact.seesMe = visibility; db.contacts.update(contact.did, { seesMe: visibility }); + this.alertTitle = "Refreshed"; this.alertMessage = this.nameForContact(contact, true) + @@ -396,7 +504,6 @@ export default class ContactsView extends Vue { this.isAlertVisible = true; } else { this.alertTitle = "Error from Server"; - console.log("Bad response checking visibility: ", resp.data); if (resp.data.error?.message) { this.alertMessage = resp.data.error?.message; } else { From e17140206cf10bd685d0e352cb85a3115a0a2f6f Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Wed, 22 Mar 2023 17:55:39 -0600 Subject: [PATCH 07/10] feat: add description to confirmation --- src/views/ContactsView.vue | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/views/ContactsView.vue b/src/views/ContactsView.vue index c6b2121..60ca56c 100644 --- a/src/views/ContactsView.vue +++ b/src/views/ContactsView.vue @@ -554,12 +554,19 @@ export default class ContactsView extends Vue { } else { toFrom = "from " + this.nameForDid(this.contacts, fromDid) + " to you"; } + let description; + if (this.hourDescriptionInput) { + description = " with description '" + this.hourDescriptionInput + "'"; + } else { + description = " with no description"; + } if ( confirm( "Are you sure you want to record " + this.hourInput + " hours " + toFrom + + description + "?" ) ) { From a7363eadcf3c83c8476d4ca3256b41388e15ecee Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Wed, 22 Mar 2023 21:09:15 -0600 Subject: [PATCH 08/10] feat: add tooltip for latest give description --- src/views/ContactsView.vue | 97 ++++++++++++++++++++++++++++++-------- src/views/ProjectsView.vue | 2 +- 2 files changed, 78 insertions(+), 21 deletions(-) diff --git a/src/views/ContactsView.vue b/src/views/ContactsView.vue index 60ca56c..77b7975 100644 --- a/src/views/ContactsView.vue +++ b/src/views/ContactsView.vue @@ -46,7 +46,7 @@

    - My Contacts + Your Contacts

    @@ -137,20 +137,30 @@
    - to: {{ givenByMeTotals[contact.did] || 0 }} - - by: {{ givenToMeTotals[contact.did] || 0 }} - +
    + to: {{ givenByMeTotals[contact.did] || 0 }} + {{ + givenByMeDescriptions[contact.did] + }} + +
    +
    + by: {{ givenToMeTotals[contact.did] || 0 }} + + {{ givenByMeDescriptions[contact.did] }} + + +
    @@ -187,6 +197,17 @@ const Buffer = require("buffer/").Buffer; const SERVICE_ID = "endorser.ch"; +export interface GiveServerRecord { + agentDid: string; + amount: number; + confirmed: number; + description: string; + fullClaim: GiveVerifiableCredential; + handleId: string; + recipientDid: string; + unit: string; +} + export interface GiveVerifiableCredential { "@context": string; "@type": string; @@ -210,8 +231,12 @@ export interface RegisterVerifiableCredential { export default class ContactsView extends Vue { contacts: Array = []; contactInput = ""; + // { "did:...": concatenated-descriptions } entry for each contact + givenByMeDescriptions: Record = {}; // { "did:...": amount } entry for each contact givenByMeTotals: Record = {}; + // { "did:...": concatenated-descriptions } entry for each contact + givenToMeDescriptions: Record = {}; // { "did:...": amount } entry for each contact givenToMeTotals: Record = {}; hourDescriptionInput = ""; @@ -287,17 +312,23 @@ export default class ContactsView extends Vue { Authorization: "Bearer " + token, }; const resp = await this.axios.get(url, { headers }); - //console.log("Server response", resp.status, resp.data); + console.log("All your gifts:", resp.data); if (resp.status === 200) { + const contactDescriptions: Record = {}; const contactTotals: Record = {}; - for (const give of resp.data.data) { + const allData: Array = resp.data.data; + for (const give of allData) { if (give.unit == "HUR") { const recipDid: string = give.recipientDid; const prevAmount = contactTotals[recipDid] || 0; contactTotals[recipDid] = prevAmount + give.amount; + const prevDesc = contactDescriptions[recipDid] || ""; + // Since many make the tooltip too big, we'll just use the latest; + contactDescriptions[recipDid] = give.description || prevDesc; } } //console.log("Done retrieving gives", contactTotals); + this.givenByMeDescriptions = contactDescriptions; this.givenByMeTotals = contactTotals; } } catch (error) { @@ -318,16 +349,22 @@ export default class ContactsView extends Vue { Authorization: "Bearer " + token, }; const resp = await this.axios.get(url, { headers }); - //console.log("Server response", resp.status, resp.data); + console.log("All gifts you've recieved:", resp.data); if (resp.status === 200) { + const contactDescriptions: Record = {}; const contactTotals: Record = {}; - for (const give of resp.data.data) { + const allData: Array = resp.data.data; + for (const give of allData) { if (give.unit == "HUR") { const prevAmount = contactTotals[give.agentDid] || 0; contactTotals[give.agentDid] = prevAmount + give.amount; + const prevDesc = contactDescriptions[give.agentDid] || ""; + // Since many make the tooltip too big, we'll just use the latest; + contactDescriptions[give.agentDid] = give.description || prevDesc; } } //console.log("Done retrieving receipts", contactTotals); + this.givenToMeDescriptions = contactDescriptions; this.givenToMeTotals = contactTotals; } } catch (error) { @@ -700,6 +737,7 @@ export default class ContactsView extends Vue { diff --git a/src/views/ProjectsView.vue b/src/views/ProjectsView.vue index 90490cd..13d4e1b 100644 --- a/src/views/ProjectsView.vue +++ b/src/views/ProjectsView.vue @@ -45,7 +45,7 @@

    - My Plans + Your Plans

    From cdef139468190600906551865d1fd7e9d23a1bf0 Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Wed, 22 Mar 2023 21:11:16 -0600 Subject: [PATCH 09/10] fix: show correct description --- src/views/ContactsView.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/ContactsView.vue b/src/views/ContactsView.vue index 77b7975..c83ee75 100644 --- a/src/views/ContactsView.vue +++ b/src/views/ContactsView.vue @@ -149,10 +149,10 @@ +
    -
    +
    by: {{ givenToMeTotals[contact.did] || 0 }} - {{ givenByMeDescriptions[contact.did] }} + {{ givenToMeDescriptions[contact.did] }}
    -
    +
    Hours to Add: +
    +
    +
    @@ -135,10 +151,20 @@ -
    +
    - to: {{ givenByMeTotals[contact.did] || 0 }} + to: + {{ + /* eslint-disable prettier/prettier */ + this.showGiveTotals + ? ((givenByMeConfirmed[contact.did] || 0) + + (givenByMeUnconfirmed[contact.did] || 0)) + : this.showGiveConfirmed + ? (givenByMeConfirmed[contact.did] || 0) + : (givenByMeUnconfirmed[contact.did] || 0) + /* eslint-enable prettier/prettier */ + }} {{ givenByMeDescriptions[contact.did] }} @@ -150,7 +176,17 @@
    - by: {{ givenToMeTotals[contact.did] || 0 }} + by: + {{ + /* eslint-disable prettier/prettier */ + this.showGiveTotals + ? ((givenToMeConfirmed[contact.did] || 0) + + (givenToMeUnconfirmed[contact.did] || 0)) + : this.showGiveConfirmed + ? (givenToMeConfirmed[contact.did] || 0) + : (givenToMeUnconfirmed[contact.did] || 0) + /* eslint-enable prettier/prettier */ + }} {{ givenToMeDescriptions[contact.did] }} @@ -234,15 +270,21 @@ export default class ContactsView extends Vue { // { "did:...": concatenated-descriptions } entry for each contact givenByMeDescriptions: Record = {}; // { "did:...": amount } entry for each contact - givenByMeTotals: Record = {}; + givenByMeConfirmed: Record = {}; + // { "did:...": amount } entry for each contact + givenByMeUnconfirmed: Record = {}; // { "did:...": concatenated-descriptions } entry for each contact givenToMeDescriptions: Record = {}; // { "did:...": amount } entry for each contact - givenToMeTotals: Record = {}; + givenToMeConfirmed: Record = {}; + // { "did:...": amount } entry for each contact + givenToMeUnconfirmed: Record = {}; hourDescriptionInput = ""; hourInput = "0"; identity: IIdentifier | null = null; - showGiveTotals = false; + showGiveNumbers = false; + showGiveTotals = true; + showGiveConfirmed = true; // 'created' hook runs when the Vue instance is first created async created() { @@ -252,8 +294,8 @@ export default class ContactsView extends Vue { await db.open(); const settings = await db.settings.get(MASTER_SETTINGS_KEY); - this.showGiveTotals = !!settings?.showContactGivesInline; - if (this.showGiveTotals) { + this.showGiveNumbers = !!settings?.showContactGivesInline; + if (this.showGiveNumbers) { this.loadGives(); } const allContacts = await db.contacts.toArray(); @@ -315,21 +357,27 @@ export default class ContactsView extends Vue { console.log("All your gifts:", resp.data); if (resp.status === 200) { const contactDescriptions: Record = {}; - const contactTotals: Record = {}; + const contactConfirmed: Record = {}; + const contactUnconfirmed: Record = {}; const allData: Array = resp.data.data; for (const give of allData) { if (give.unit == "HUR") { const recipDid: string = give.recipientDid; - const prevAmount = contactTotals[recipDid] || 0; - contactTotals[recipDid] = prevAmount + give.amount; + if (give.confirmed) { + const prevAmount = contactConfirmed[recipDid] || 0; + contactConfirmed[recipDid] = prevAmount + give.amount; + } else { + const prevAmount = contactUnconfirmed[recipDid] || 0; + contactUnconfirmed[recipDid] = prevAmount + give.amount; + } const prevDesc = contactDescriptions[recipDid] || ""; // Since many make the tooltip too big, we'll just use the latest; contactDescriptions[recipDid] = give.description || prevDesc; } } - //console.log("Done retrieving gives", contactTotals); + //console.log("Done retrieving gives", contactConfirmed); this.givenByMeDescriptions = contactDescriptions; - this.givenByMeTotals = contactTotals; + this.givenByMeConfirmed = contactConfirmed; } } catch (error) { this.alertTitle = "Error from Server"; @@ -352,20 +400,26 @@ export default class ContactsView extends Vue { console.log("All gifts you've recieved:", resp.data); if (resp.status === 200) { const contactDescriptions: Record = {}; - const contactTotals: Record = {}; + const contactConfirmed: Record = {}; + const contactUnconfirmed: Record = {}; const allData: Array = resp.data.data; for (const give of allData) { if (give.unit == "HUR") { - const prevAmount = contactTotals[give.agentDid] || 0; - contactTotals[give.agentDid] = prevAmount + give.amount; + if (give.confirmed) { + 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; + } const prevDesc = contactDescriptions[give.agentDid] || ""; // Since many make the tooltip too big, we'll just use the latest; contactDescriptions[give.agentDid] = give.description || prevDesc; } } - //console.log("Done retrieving receipts", contactTotals); + //console.log("Done retrieving receipts", contactConfirmed); this.givenToMeDescriptions = contactDescriptions; - this.givenToMeTotals = contactTotals; + this.givenToMeConfirmed = contactConfirmed; } } catch (error) { this.alertTitle = "Error from Server"; @@ -675,16 +729,17 @@ export default class ContactsView extends Vue { this.alertTitle = ""; this.alertMessage = ""; if (fromDid === identity.did) { - this.givenByMeTotals[toDid] = this.givenByMeTotals[toDid] + amount; + this.givenByMeConfirmed[toDid] = + this.givenByMeConfirmed[toDid] + amount; // do this to update the UI (is there a better way?) // eslint-disable-next-line no-self-assign - this.givenByMeTotals = this.givenByMeTotals; + this.givenByMeConfirmed = this.givenByMeConfirmed; } else { - this.givenToMeTotals[fromDid] = - this.givenToMeTotals[fromDid] + amount; + this.givenToMeConfirmed[fromDid] = + this.givenToMeConfirmed[fromDid] + amount; // do this to update the UI (is there a better way?) // eslint-disable-next-line no-self-assign - this.givenToMeTotals = this.givenToMeTotals; + this.givenToMeConfirmed = this.givenToMeConfirmed; } } } catch (error) { @@ -707,6 +762,32 @@ export default class ContactsView extends Vue { } } + public selectedGiveTotal( + contactGivesConfirmed: Record, + contactGivesUnconfirmed: Record, + did: string + ) { + /* eslint-disable prettier/prettier */ + this.showGiveTotals + ? ((contactGivesConfirmed[did] || 0) + (contactGivesUnconfirmed[did] || 0)) + : this.showGiveConfirmed + ? (contactGivesConfirmed[did] || 0) + : (contactGivesUnconfirmed[did] || 0); + /* eslint-enable prettier/prettier */ + } + public toggleShowGiveTotals() { + if (this.showGiveTotals) { + this.showGiveTotals = false; + this.showGiveConfirmed = true; + } else if (this.showGiveConfirmed) { + this.showGiveTotals = false; // stays the same + this.showGiveConfirmed = false; + } else { + this.showGiveTotals = true; + this.showGiveConfirmed = true; + } + } + alertTitle = ""; alertMessage = ""; isAlertVisible = false; @@ -733,6 +814,14 @@ export default class ContactsView extends Vue { "duration-300": true, }; } + + public showGiveAmountsClassNames() { + return { + "bg-slate-900": this.showGiveTotals, + "bg-green-600": !this.showGiveTotals && this.showGiveConfirmed, + "bg-yellow-600": !this.showGiveTotals && !this.showGiveConfirmed, + }; + } }