|  | @ -9,7 +9,7 @@ | 
			
		
	
		
		
			
				
					|  |  |         ></router-link> |  |  |         ></router-link> | 
			
		
	
		
		
			
				
					|  |  |       </li> |  |  |       </li> | 
			
		
	
		
		
			
				
					|  |  |       <!-- Search --> |  |  |       <!-- Search --> | 
			
		
	
		
		
			
				
					
					|  |  |       <li class="basis-1/5 rounded-md bg-slate-400 text-white"> |  |  |       <li class="basis-1/5 rounded-md text-slate-500"> | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |         <router-link |  |  |         <router-link | 
			
		
	
		
		
			
				
					|  |  |           :to="{ name: 'discover' }" |  |  |           :to="{ name: 'discover' }" | 
			
		
	
		
		
			
				
					|  |  |           class="block text-center py-3 px-1" |  |  |           class="block text-center py-3 px-1" | 
			
		
	
	
		
		
			
				
					|  | @ -25,7 +25,7 @@ | 
			
		
	
		
		
			
				
					|  |  |         ></router-link> |  |  |         ></router-link> | 
			
		
	
		
		
			
				
					|  |  |       </li> |  |  |       </li> | 
			
		
	
		
		
			
				
					|  |  |       <!-- Contacts --> |  |  |       <!-- Contacts --> | 
			
		
	
		
		
			
				
					
					|  |  |       <li class="basis-1/5 rounded-md text-slate-500"> |  |  |       <li class="basis-1/5 rounded-md bg-slate-400 text-white"> | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |         <router-link |  |  |         <router-link | 
			
		
	
		
		
			
				
					|  |  |           :to="{ name: 'contacts' }" |  |  |           :to="{ name: 'contacts' }" | 
			
		
	
		
		
			
				
					|  |  |           class="block text-center py-3 px-1" |  |  |           class="block text-center py-3 px-1" | 
			
		
	
	
		
		
			
				
					|  | @ -46,7 +46,7 @@ | 
			
		
	
		
		
			
				
					|  |  |   <section id="Content" class="p-6 pb-24"> |  |  |   <section id="Content" class="p-6 pb-24"> | 
			
		
	
		
		
			
				
					|  |  |     <!-- Heading --> |  |  |     <!-- Heading --> | 
			
		
	
		
		
			
				
					|  |  |     <h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8"> |  |  |     <h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8"> | 
			
		
	
		
		
			
				
					
					|  |  |       My Contacts |  |  |       Your Contacts | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |     </h1> |  |  |     </h1> | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |     <!-- New Contact --> |  |  |     <!-- New Contact --> | 
			
		
	
	
		
		
			
				
					|  | @ -65,7 +65,7 @@ | 
			
		
	
		
		
			
				
					|  |  |       </button> |  |  |       </button> | 
			
		
	
		
		
			
				
					|  |  |     </div> |  |  |     </div> | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |     <div class="flex justify-between" v-if="showGiveTotals"> |  |  |     <div class="flex justify-between" v-if="showGiveNumbers"> | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |       <div class="w-full text-right"> |  |  |       <div class="w-full text-right"> | 
			
		
	
		
		
			
				
					|  |  |         Hours to Add: |  |  |         Hours to Add: | 
			
		
	
		
		
			
				
					|  |  |         <input |  |  |         <input | 
			
		
	
	
		
		
			
				
					|  | @ -81,6 +81,22 @@ | 
			
		
	
		
		
			
				
					|  |  |           placeholder="Description" |  |  |           placeholder="Description" | 
			
		
	
		
		
			
				
					|  |  |           v-model="hourDescriptionInput" |  |  |           v-model="hourDescriptionInput" | 
			
		
	
		
		
			
				
					|  |  |         /> |  |  |         /> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         <br /> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         <br /> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         <button | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           href="" | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           class="text-center text-md text-white px-1.5 py-2 rounded-md mb-6" | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           v-bind:class="showGiveAmountsClassNames()" | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           @click="toggleShowGiveTotals()" | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         > | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           {{ | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             showGiveTotals | 
			
		
	
		
		
			
				
					|  |  |  |  |  |               ? "Totals" | 
			
		
	
		
		
			
				
					|  |  |  |  |  |               : showGiveConfirmed | 
			
		
	
		
		
			
				
					|  |  |  |  |  |               ? "Confirmed" | 
			
		
	
		
		
			
				
					|  |  |  |  |  |               : "Unconfirmed" | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           }} | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         </button> | 
			
		
	
		
		
			
				
					|  |  |       </div> |  |  |       </div> | 
			
		
	
		
		
			
				
					|  |  |     </div> |  |  |     </div> | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  | @ -96,17 +112,84 @@ | 
			
		
	
		
		
			
				
					|  |  |             {{ contact.name || "(no name)" }} |  |  |             {{ contact.name || "(no name)" }} | 
			
		
	
		
		
			
				
					|  |  |           </h2> |  |  |           </h2> | 
			
		
	
		
		
			
				
					|  |  |           <div class="text-sm truncate">{{ contact.did }}</div> |  |  |           <div class="text-sm truncate">{{ contact.did }}</div> | 
			
		
	
		
		
			
				
					
					|  |  |           <div class="text-sm truncate">{{ contact.publicKeyBase64 }}</div> |  |  |           <div class="text-sm truncate" v-if="contact.publicKeyBase64"> | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |           <div v-if="showGiveTotals" class="float-right"> |  |  |             Public Key (base 64): {{ contact.publicKeyBase64 }} | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					|  |  |  |  |  |           </div> | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           <button | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             v-if="contact.seesMe" | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             class="tooltip" | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             @click="setVisibility(contact, false)" | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           > | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             <fa icon="eye" class="text-slate-900 fa-fw ml-1" /> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             <span class="tooltiptext">Can see you</span> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           </button> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           <button v-else class="tooltip" @click="setVisibility(contact, true)"> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             <span class="tooltiptext">Cannot see you</span> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             <fa icon="eye-slash" class="text-slate-900 fa-fw ml-1" /> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           </button> | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           <button class="tooltip" @click="checkVisibility(contact)"> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             <span class="tooltiptext">Check Visibility</span> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             <fa icon="rotate" class="text-slate-900 fa-fw ml-1" /> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           </button> | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           <button v-if="contact.registered" class="tooltip"> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             <span class="tooltiptext">Registered</span> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             <fa icon="person-circle-check" class="text-slate-900 fa-fw ml-1" /> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           </button> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           <button v-else @click="register(contact)" class="tooltip"> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             <span class="tooltiptext">Maybe not registered</span> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             <fa | 
			
		
	
		
		
			
				
					|  |  |  |  |  |               icon="person-circle-question" | 
			
		
	
		
		
			
				
					|  |  |  |  |  |               class="text-slate-900 fa-fw ml-1" | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             /> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           </button> | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           <button @click="deleteContact(contact)" class="px-9 tooltip"> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             <span class="tooltiptext">Delete!</span> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             <fa icon="trash-can" class="text-red-600 fa-fw ml-1" /> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           </button> | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           <div v-if="showGiveNumbers" class="float-right"> | 
			
		
	
		
		
			
				
					|  |  |             <div class="float-right"> |  |  |             <div class="float-right"> | 
			
		
	
		
		
			
				
					
					|  |  |               to: {{ givenByMeTotals[contact.did] || 0 }} |  |  |               <div class="tooltip"> | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |  |  |  |                 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 */ | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                 }} | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                 <span class="tooltiptext-left">{{ | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                   givenByMeDescriptions[contact.did] | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                 }}</span> | 
			
		
	
		
		
			
				
					|  |  |                 <button |  |  |                 <button | 
			
		
	
		
		
			
				
					|  |  |                   class="text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-6" |  |  |                   class="text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-6" | 
			
		
	
		
		
			
				
					|  |  |                   @click="onClickAddGive(identity.did, contact.did)" |  |  |                   @click="onClickAddGive(identity.did, contact.did)" | 
			
		
	
		
		
			
				
					|  |  |                 > |  |  |                 > | 
			
		
	
		
		
			
				
					|  |  |                   + |  |  |                   + | 
			
		
	
		
		
			
				
					|  |  |                 </button> |  |  |                 </button> | 
			
		
	
		
		
			
				
					
					|  |  |               by: {{ givenToMeTotals[contact.did] || 0 }} |  |  |               </div> | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |  |  |  |               <div class="tooltip px-2"> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                 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 */ | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                 }} | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                 <span class="tooltiptext-left"> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                   {{ givenToMeDescriptions[contact.did] }} | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                 </span> | 
			
		
	
		
		
			
				
					|  |  |                 <button |  |  |                 <button | 
			
		
	
		
		
			
				
					|  |  |                   class="text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-6" |  |  |                   class="text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-6" | 
			
		
	
		
		
			
				
					|  |  |                   @click="onClickAddGive(contact.did, identity.did)" |  |  |                   @click="onClickAddGive(contact.did, identity.did)" | 
			
		
	
	
		
		
			
				
					|  | @ -116,6 +199,7 @@ | 
			
		
	
		
		
			
				
					|  |  |               </div> |  |  |               </div> | 
			
		
	
		
		
			
				
					|  |  |             </div> |  |  |             </div> | 
			
		
	
		
		
			
				
					|  |  |           </div> |  |  |           </div> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         </div> | 
			
		
	
		
		
			
				
					|  |  |       </li> |  |  |       </li> | 
			
		
	
		
		
			
				
					|  |  |     </ul> |  |  |     </ul> | 
			
		
	
		
		
			
				
					|  |  |   </section> |  |  |   </section> | 
			
		
	
	
		
		
			
				
					|  | @ -135,13 +219,30 @@ | 
			
		
	
		
		
			
				
					|  |  | import { AxiosError } from "axios"; |  |  | import { AxiosError } from "axios"; | 
			
		
	
		
		
			
				
					|  |  | import * as didJwt from "did-jwt"; |  |  | import * as didJwt from "did-jwt"; | 
			
		
	
		
		
			
				
					|  |  | import * as R from "ramda"; |  |  | import * as R from "ramda"; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | import { IIdentifier } from "@veramo/core"; | 
			
		
	
		
		
			
				
					|  |  | import { Options, Vue } from "vue-class-component"; |  |  | import { Options, Vue } from "vue-class-component"; | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  | import { AppString } from "@/constants/app"; |  |  | import { AppString } from "@/constants/app"; | 
			
		
	
		
		
			
				
					|  |  | import { accessToken, SimpleSigner } from "@/libs/crypto"; |  |  | import { accessToken, SimpleSigner } from "@/libs/crypto"; | 
			
		
	
		
		
			
				
					
					|  |  | import { IIdentifier } from "@veramo/core"; |  |  | import { accountsDB, db } from "@/db"; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  | import { accountsDB, db } from "../db"; |  |  | import { Contact } from "@/db/tables/contacts"; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  | import { Contact } from "../db/tables/contacts"; |  |  | 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 GiveServerRecord { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   agentDid: string; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   amount: number; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   confirmed: number; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   description: string; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   fullClaim: GiveVerifiableCredential; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   handleId: string; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   recipientDid: string; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   unit: string; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  | export interface GiveVerifiableCredential { |  |  | export interface GiveVerifiableCredential { | 
			
		
	
		
		
			
				
					|  |  |   "@context": string; |  |  |   "@context": string; | 
			
		
	
	
		
		
			
				
					|  | @ -152,20 +253,38 @@ export interface GiveVerifiableCredential { | 
			
		
	
		
		
			
				
					|  |  |   recipient: { identifier: string }; |  |  |   recipient: { identifier: string }; | 
			
		
	
		
		
			
				
					|  |  | } |  |  | } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  | export interface RegisterVerifiableCredential { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   "@context": string; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   "@type": string; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   agent: { identifier: string }; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   object: string; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   recipient: { identifier: string }; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  | @Options({ |  |  | @Options({ | 
			
		
	
		
		
			
				
					|  |  |   components: {}, |  |  |   components: {}, | 
			
		
	
		
		
			
				
					|  |  | }) |  |  | }) | 
			
		
	
		
		
			
				
					|  |  | export default class ContactsView extends Vue { |  |  | export default class ContactsView extends Vue { | 
			
		
	
		
		
			
				
					|  |  |   contacts: Array<Contact> = []; |  |  |   contacts: Array<Contact> = []; | 
			
		
	
		
		
			
				
					|  |  |   contactInput = ""; |  |  |   contactInput = ""; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   // { "did:...": concatenated-descriptions } entry for each contact | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   givenByMeDescriptions: Record<string, string> = {}; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   // { "did:...": amount } entry for each contact | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   givenByMeConfirmed: Record<string, number> = {}; | 
			
		
	
		
		
			
				
					|  |  |   // { "did:...": amount } entry for each contact |  |  |   // { "did:...": amount } entry for each contact | 
			
		
	
		
		
			
				
					
					|  |  |   givenByMeTotals: Record<string, number> = {}; |  |  |   givenByMeUnconfirmed: Record<string, number> = {}; | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |  |  |  |   // { "did:...": concatenated-descriptions } entry for each contact | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   givenToMeDescriptions: Record<string, string> = {}; | 
			
		
	
		
		
			
				
					|  |  |   // { "did:...": amount } entry for each contact |  |  |   // { "did:...": amount } entry for each contact | 
			
		
	
		
		
			
				
					
					|  |  |   givenToMeTotals: Record<string, number> = {}; |  |  |   givenToMeConfirmed: Record<string, number> = {}; | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |  |  |  |   // { "did:...": amount } entry for each contact | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   givenToMeUnconfirmed: Record<string, number> = {}; | 
			
		
	
		
		
			
				
					|  |  |   hourDescriptionInput = ""; |  |  |   hourDescriptionInput = ""; | 
			
		
	
		
		
			
				
					|  |  |   hourInput = "0"; |  |  |   hourInput = "0"; | 
			
		
	
		
		
			
				
					|  |  |   identity: IIdentifier | null = null; |  |  |   identity: IIdentifier | null = null; | 
			
		
	
		
		
			
				
					
					|  |  |   showGiveTotals = false; |  |  |   showGiveNumbers = false; | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |  |  |  |   showGiveTotals = true; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   showGiveConfirmed = true; | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |   // 'created' hook runs when the Vue instance is first created |  |  |   // 'created' hook runs when the Vue instance is first created | 
			
		
	
		
		
			
				
					|  |  |   async created() { |  |  |   async created() { | 
			
		
	
	
		
		
			
				
					|  | @ -174,13 +293,16 @@ export default class ContactsView extends Vue { | 
			
		
	
		
		
			
				
					|  |  |     this.identity = JSON.parse(accounts[0].identity); |  |  |     this.identity = JSON.parse(accounts[0].identity); | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |     await db.open(); |  |  |     await db.open(); | 
			
		
	
		
		
			
				
					
					|  |  |     this.contacts = await db.contacts.toArray(); |  |  |     const settings = await db.settings.get(MASTER_SETTINGS_KEY); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  | 
 |  |  |     this.showGiveNumbers = !!settings?.showContactGivesInline; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |     const params = new URLSearchParams(window.location.search); |  |  |     if (this.showGiveNumbers) { | 
			
				
				
			
		
	
		
		
			
				
					|  |  |     this.showGiveTotals = params.get("showGiveTotals") == "true"; |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     if (this.showGiveTotals) { |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					|  |  |       this.loadGives(); |  |  |       this.loadGives(); | 
			
		
	
		
		
			
				
					|  |  |     } |  |  |     } | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const allContacts = await db.contacts.toArray(); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     this.contacts = R.sort( | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       (a: Contact, b) => (a.name || "").localeCompare(b.name || ""), | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       allContacts | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     ); | 
			
		
	
		
		
			
				
					|  |  |   } |  |  |   } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |   async onClickNewContact(): Promise<void> { |  |  |   async onClickNewContact(): Promise<void> { | 
			
		
	
	
		
		
			
				
					|  | @ -196,9 +318,18 @@ export default class ContactsView extends Vue { | 
			
		
	
		
		
			
				
					|  |  |         publicKeyBase64 = this.contactInput.substring(commaPos2 + 1).trim(); |  |  |         publicKeyBase64 = this.contactInput.substring(commaPos2 + 1).trim(); | 
			
		
	
		
		
			
				
					|  |  |       } |  |  |       } | 
			
		
	
		
		
			
				
					|  |  |     } |  |  |     } | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     // help with potential mistakes while this sharing requires copy-and-paste | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     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 }; |  |  |     const newContact = { did, name, publicKeyBase64 }; | 
			
		
	
		
		
			
				
					|  |  |     await db.contacts.add(newContact); |  |  |     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() { |  |  |   async loadGives() { | 
			
		
	
	
		
		
			
				
					|  | @ -223,18 +354,30 @@ export default class ContactsView extends Vue { | 
			
		
	
		
		
			
				
					|  |  |         Authorization: "Bearer " + token, |  |  |         Authorization: "Bearer " + token, | 
			
		
	
		
		
			
				
					|  |  |       }; |  |  |       }; | 
			
		
	
		
		
			
				
					|  |  |       const resp = await this.axios.get(url, { headers }); |  |  |       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) { |  |  |       if (resp.status === 200) { | 
			
		
	
		
		
			
				
					
					|  |  |         const contactTotals: Record<string, number> = {}; |  |  |         const contactDescriptions: Record<string, string> = {}; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |         for (const give of 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) { | 
			
		
	
		
		
			
				
					|  |  |           if (give.unit == "HUR") { |  |  |           if (give.unit == "HUR") { | 
			
		
	
		
		
			
				
					|  |  |             const recipDid: string = give.recipientDid; |  |  |             const recipDid: string = give.recipientDid; | 
			
		
	
		
		
			
				
					
					|  |  |             const prevAmount = contactTotals[recipDid] || 0; |  |  |             if (give.confirmed) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |             contactTotals[recipDid] = prevAmount + give.amount; |  |  |               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); |  |  |         } | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |         this.givenByMeTotals = contactTotals; |  |  |         //console.log("Done retrieving gives", contactConfirmed); | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					|  |  |  |  |  |         this.givenByMeDescriptions = contactDescriptions; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         this.givenByMeConfirmed = contactConfirmed; | 
			
		
	
		
		
			
				
					|  |  |       } |  |  |       } | 
			
		
	
		
		
			
				
					|  |  |     } catch (error) { |  |  |     } catch (error) { | 
			
		
	
		
		
			
				
					|  |  |       this.alertTitle = "Error from Server"; |  |  |       this.alertTitle = "Error from Server"; | 
			
		
	
	
		
		
			
				
					|  | @ -254,17 +397,29 @@ export default class ContactsView extends Vue { | 
			
		
	
		
		
			
				
					|  |  |         Authorization: "Bearer " + token, |  |  |         Authorization: "Bearer " + token, | 
			
		
	
		
		
			
				
					|  |  |       }; |  |  |       }; | 
			
		
	
		
		
			
				
					|  |  |       const resp = await this.axios.get(url, { headers }); |  |  |       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) { |  |  |       if (resp.status === 200) { | 
			
		
	
		
		
			
				
					
					|  |  |         const contactTotals: Record<string, number> = {}; |  |  |         const contactDescriptions: Record<string, string> = {}; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |         for (const give of 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) { | 
			
		
	
		
		
			
				
					|  |  |           if (give.unit == "HUR") { |  |  |           if (give.unit == "HUR") { | 
			
		
	
		
		
			
				
					
					|  |  |             const prevAmount = contactTotals[give.agentDid] || 0; |  |  |             if (give.confirmed) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |             contactTotals[give.agentDid] = prevAmount + give.amount; |  |  |               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.givenToMeTotals = contactTotals; |  |  |         this.givenToMeDescriptions = contactDescriptions; | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					|  |  |  |  |  |         this.givenToMeConfirmed = contactConfirmed; | 
			
		
	
		
		
			
				
					|  |  |       } |  |  |       } | 
			
		
	
		
		
			
				
					|  |  |     } catch (error) { |  |  |     } catch (error) { | 
			
		
	
		
		
			
				
					|  |  |       this.alertTitle = "Error from Server"; |  |  |       this.alertTitle = "Error from Server"; | 
			
		
	
	
		
		
			
				
					|  | @ -273,6 +428,187 @@ 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); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   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 = | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       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"; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         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 |  |  |   // from https://stackoverflow.com/a/175787/845494 | 
			
		
	
		
		
			
				
					|  |  |   // |  |  |   // | 
			
		
	
		
		
			
				
					|  |  |   private isNumeric(str: string): boolean { |  |  |   private isNumeric(str: string): boolean { | 
			
		
	
	
		
		
			
				
					|  | @ -281,7 +617,11 @@ export default class ContactsView extends Vue { | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |   private nameForDid(contacts: Array<Contact>, did: string): string { |  |  |   private nameForDid(contacts: Array<Contact>, did: string): string { | 
			
		
	
		
		
			
				
					|  |  |     const contact = R.find((con) => con.did == did, contacts); |  |  |     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<void> { |  |  |   async onClickAddGive(fromDid: string, toDid: string): Promise<void> { | 
			
		
	
	
		
		
			
				
					|  | @ -305,12 +645,19 @@ export default class ContactsView extends Vue { | 
			
		
	
		
		
			
				
					|  |  |       } else { |  |  |       } else { | 
			
		
	
		
		
			
				
					|  |  |         toFrom = "from " + this.nameForDid(this.contacts, fromDid) + " to you"; |  |  |         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 ( |  |  |       if ( | 
			
		
	
		
		
			
				
					|  |  |         confirm( |  |  |         confirm( | 
			
		
	
		
		
			
				
					|  |  |           "Are you sure you want to record " + |  |  |           "Are you sure you want to record " + | 
			
		
	
		
		
			
				
					|  |  |             this.hourInput + |  |  |             this.hourInput + | 
			
		
	
		
		
			
				
					|  |  |             " hours " + |  |  |             " hours " + | 
			
		
	
		
		
			
				
					|  |  |             toFrom + |  |  |             toFrom + | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             description + | 
			
		
	
		
		
			
				
					|  |  |             "?" |  |  |             "?" | 
			
		
	
		
		
			
				
					|  |  |         ) |  |  |         ) | 
			
		
	
		
		
			
				
					|  |  |       ) { |  |  |       ) { | 
			
		
	
	
		
		
			
				
					|  | @ -382,16 +729,17 @@ export default class ContactsView extends Vue { | 
			
		
	
		
		
			
				
					|  |  |           this.alertTitle = ""; |  |  |           this.alertTitle = ""; | 
			
		
	
		
		
			
				
					|  |  |           this.alertMessage = ""; |  |  |           this.alertMessage = ""; | 
			
		
	
		
		
			
				
					|  |  |           if (fromDid === identity.did) { |  |  |           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?) |  |  |             // do this to update the UI (is there a better way?) | 
			
		
	
		
		
			
				
					|  |  |             // eslint-disable-next-line no-self-assign |  |  |             // eslint-disable-next-line no-self-assign | 
			
		
	
		
		
			
				
					
					|  |  |             this.givenByMeTotals = this.givenByMeTotals; |  |  |             this.givenByMeConfirmed = this.givenByMeConfirmed; | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |           } else { |  |  |           } else { | 
			
		
	
		
		
			
				
					
					|  |  |             this.givenToMeTotals[fromDid] = |  |  |             this.givenToMeConfirmed[fromDid] = | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |               this.givenToMeTotals[fromDid] + amount; |  |  |               this.givenToMeConfirmed[fromDid] + amount; | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					|  |  |             // do this to update the UI (is there a better way?) |  |  |             // do this to update the UI (is there a better way?) | 
			
		
	
		
		
			
				
					|  |  |             // eslint-disable-next-line no-self-assign |  |  |             // eslint-disable-next-line no-self-assign | 
			
		
	
		
		
			
				
					
					|  |  |             this.givenToMeTotals = this.givenToMeTotals; |  |  |             this.givenToMeConfirmed = this.givenToMeConfirmed; | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |           } |  |  |           } | 
			
		
	
		
		
			
				
					|  |  |         } |  |  |         } | 
			
		
	
		
		
			
				
					|  |  |       } catch (error) { |  |  |       } catch (error) { | 
			
		
	
	
		
		
			
				
					|  | @ -414,6 +762,32 @@ export default class ContactsView extends Vue { | 
			
		
	
		
		
			
				
					|  |  |     } |  |  |     } | 
			
		
	
		
		
			
				
					|  |  |   } |  |  |   } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   public selectedGiveTotal( | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     contactGivesConfirmed: Record<string, number>, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     contactGivesUnconfirmed: Record<string, number>, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     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 = ""; |  |  |   alertTitle = ""; | 
			
		
	
		
		
			
				
					|  |  |   alertMessage = ""; |  |  |   alertMessage = ""; | 
			
		
	
		
		
			
				
					|  |  |   isAlertVisible = false; |  |  |   isAlertVisible = false; | 
			
		
	
	
		
		
			
				
					|  | @ -440,5 +814,62 @@ export default class ContactsView extends Vue { | 
			
		
	
		
		
			
				
					|  |  |       "duration-300": true, |  |  |       "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, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     }; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   } | 
			
		
	
		
		
			
				
					|  |  | } |  |  | } | 
			
		
	
		
		
			
				
					|  |  | </script> |  |  | </script> | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  | <style> | 
			
		
	
		
		
			
				
					|  |  |  |  |  | /* Tooltip from https://www.w3schools.com/css/css_tooltip.asp */ | 
			
		
	
		
		
			
				
					|  |  |  |  |  | /* Tooltip container */ | 
			
		
	
		
		
			
				
					|  |  |  |  |  | .tooltip { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   position: relative; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   display: inline-block; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   border-bottom: 1px dotted black; /* If you want dots under the hoverable text */ | 
			
		
	
		
		
			
				
					|  |  |  |  |  | } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  | /* Tooltip text */ | 
			
		
	
		
		
			
				
					|  |  |  |  |  | .tooltip .tooltiptext { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   visibility: hidden; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   width: 200px; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   background-color: black; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   color: #fff; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   text-align: center; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   padding: 5px 0; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   border-radius: 6px; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   position: absolute; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   z-index: 1; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | /* How do we share with the above so code isn't duplicated? */ | 
			
		
	
		
		
			
				
					|  |  |  |  |  | .tooltip .tooltiptext-left { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   visibility: hidden; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   width: 200px; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   background-color: black; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   color: #fff; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   text-align: center; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   padding: 5px 0; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   border-radius: 6px; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   position: absolute; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   z-index: 1; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   bottom: 0%; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   right: 105%; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   margin-left: -60px; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  | /* Show the tooltip text when you mouse over the tooltip container */ | 
			
		
	
		
		
			
				
					|  |  |  |  |  | .tooltip:hover .tooltiptext { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   visibility: visible; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | .tooltip:hover .tooltiptext-left { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   visibility: visible; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | </style> | 
			
		
	
	
		
		
			
				
					|  | 
 |