|  | @ -72,10 +72,10 @@ | 
			
		
	
		
		
			
				
					|  |  |                 <fa icon="circle-check" class="text-green-600 fa-fw ml-1" /> |  |  |                 <fa icon="circle-check" class="text-green-600 fa-fw ml-1" /> | 
			
		
	
		
		
			
				
					|  |  |                 <span class="tooltiptext">Confirmed</span> |  |  |                 <span class="tooltiptext">Confirmed</span> | 
			
		
	
		
		
			
				
					|  |  |               </span> |  |  |               </span> | 
			
		
	
		
		
			
				
					
					|  |  |               <span v-else class="tooltip"> |  |  |               <button v-else class="tooltip" @click="confirm(record)"> | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |                 <fa icon="circle" class="text-blue-600 fa-fw ml-1" /> |  |  |                 <fa icon="circle" class="text-blue-600 fa-fw ml-1" /> | 
			
		
	
		
		
			
				
					|  |  |                 <span class="tooltiptext">Unconfirmed</span> |  |  |                 <span class="tooltiptext">Unconfirmed</span> | 
			
		
	
		
		
			
				
					
					|  |  |               </span> |  |  |               </button> | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |             </div> |  |  |             </div> | 
			
		
	
		
		
			
				
					|  |  |             <br /> |  |  |             <br /> | 
			
		
	
		
		
			
				
					|  |  |             {{ record.description }} |  |  |             {{ record.description }} | 
			
		
	
	
		
		
			
				
					|  | @ -98,10 +98,10 @@ | 
			
		
	
		
		
			
				
					|  |  |                 <fa icon="circle-check" class="text-green-600 fa-fw ml-1" /> |  |  |                 <fa icon="circle-check" class="text-green-600 fa-fw ml-1" /> | 
			
		
	
		
		
			
				
					|  |  |                 <span class="tooltiptext">Confirmed</span> |  |  |                 <span class="tooltiptext">Confirmed</span> | 
			
		
	
		
		
			
				
					|  |  |               </span> |  |  |               </span> | 
			
		
	
		
		
			
				
					
					|  |  |               <span v-else class="tooltip"> |  |  |               <button v-else class="tooltip" @click="cannotConfirmMessage()"> | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |                 <fa icon="circle" class="text-slate-600 fa-fw ml-1" /> |  |  |                 <fa icon="circle" class="text-slate-600 fa-fw ml-1" /> | 
			
		
	
		
		
			
				
					|  |  |                 <span class="tooltiptext">Unconfirmed</span> |  |  |                 <span class="tooltiptext">Unconfirmed</span> | 
			
		
	
		
		
			
				
					
					|  |  |               </span> |  |  |               </button> | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |             </div> |  |  |             </div> | 
			
		
	
		
		
			
				
					|  |  |             <br /> |  |  |             <br /> | 
			
		
	
		
		
			
				
					|  |  |             {{ record.description }} |  |  |             {{ record.description }} | 
			
		
	
	
		
		
			
				
					|  | @ -109,6 +109,17 @@ | 
			
		
	
		
		
			
				
					|  |  |         </div> |  |  |         </div> | 
			
		
	
		
		
			
				
					|  |  |       </div> |  |  |       </div> | 
			
		
	
		
		
			
				
					|  |  |     </div> |  |  |     </div> | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     <div v-bind:class="computedAlertClassNames()"> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       <button | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         class="close-button bg-slate-200 w-8 leading-loose rounded-full absolute top-2 right-2" | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         @click="onClickClose()" | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       > | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         <fa icon="xmark"></fa> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       </button> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       <h4 class="font-bold pr-5">{{ alertTitle }}</h4> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       <p>{{ alertMessage }}</p> | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     </div> | 
			
		
	
		
		
			
				
					|  |  |   </section> |  |  |   </section> | 
			
		
	
		
		
			
				
					|  |  | </template> |  |  | </template> | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  | @ -120,11 +131,20 @@ 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"; |  |  | import { MASTER_SETTINGS_KEY } from "@/db/tables/settings"; | 
			
		
	
		
		
			
				
					|  |  | import { AppString } from "@/constants/app"; |  |  | import { AppString } from "@/constants/app"; | 
			
		
	
		
		
			
				
					
					|  |  | import { accessToken } from "@/libs/crypto"; |  |  | import { accessToken, SimpleSigner } from "@/libs/crypto"; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  | import { GiveServerRecord } from "@/libs/endorserServer"; |  |  | import { | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					|  |  |  |  |  |   AgreeVerifiableCredential, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   GiveServerRecord, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   RegisterVerifiableCredential, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   SCHEMA_ORG_CONTEXT, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   SERVICE_ID, | 
			
		
	
		
		
			
				
					|  |  |  |  |  | } from "@/libs/endorserServer"; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | import * as didJwt from "did-jwt"; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | import { AxiosError } from "axios"; | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  | @Options({}) |  |  | @Options({}) | 
			
		
	
		
		
			
				
					|  |  | export default class ContactsView extends Vue { |  |  | export default class ContactsView extends Vue { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   activeDid = ""; | 
			
		
	
		
		
			
				
					|  |  |   contact: Contact | null = null; |  |  |   contact: Contact | null = null; | 
			
		
	
		
		
			
				
					|  |  |   giveRecords: Array<GiveServerRecord> = []; |  |  |   giveRecords: Array<GiveServerRecord> = []; | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  | @ -135,10 +155,10 @@ export default class ContactsView extends Vue { | 
			
		
	
		
		
			
				
					|  |  |     this.contact = (await db.contacts.get(contactDid)) || null; |  |  |     this.contact = (await db.contacts.get(contactDid)) || null; | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |     const settings = await db.settings.get(MASTER_SETTINGS_KEY); |  |  |     const settings = await db.settings.get(MASTER_SETTINGS_KEY); | 
			
		
	
		
		
			
				
					
					|  |  |     const activeDid = settings?.activeDid; |  |  |     this.activeDid = settings?.activeDid || ""; | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |     if (activeDid && this.contact) { |  |  |     if (this.activeDid && this.contact) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |       this.loadGives(activeDid, this.contact); |  |  |       this.loadGives(this.activeDid, this.contact); | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					|  |  |     } |  |  |     } | 
			
		
	
		
		
			
				
					|  |  |   } |  |  |   } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  | @ -220,6 +240,88 @@ export default class ContactsView extends Vue { | 
			
		
	
		
		
			
				
					|  |  |     } |  |  |     } | 
			
		
	
		
		
			
				
					|  |  |   } |  |  |   } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   async confirm(record: GiveServerRecord) { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     // Make claim | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     // I use clone here because otherwise it gets a Proxy object. | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const origClaim: Record<any, any> = R.clone(record.fullClaim); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     if (record.fullClaim["@context"] == SCHEMA_ORG_CONTEXT) { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       delete origClaim["@context"]; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     origClaim["identifier"] = record.handleId; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const vcClaim: AgreeVerifiableCredential = { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       "@context": SCHEMA_ORG_CONTEXT, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       "@type": "AgreeAction", | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       object: origClaim, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     }; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     // 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 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     await accountsDB.open(); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const accounts = await accountsDB.accounts.toArray(); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const account = R.find((acc) => acc.did === this.activeDid, accounts); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     const identity = JSON.parse(account?.identity || "undefined"); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     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) { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |           record.confirmed = 1; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         } | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       } 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; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |       } | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   cannotConfirmMessage() { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     this.alertTitle = "Not Allowed"; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     this.alertMessage = "Only the recipient can confirm final receipt."; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     this.isAlertVisible = true; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |   } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |   alertTitle = ""; |  |  |   alertTitle = ""; | 
			
		
	
		
		
			
				
					|  |  |   alertMessage = ""; |  |  |   alertMessage = ""; | 
			
		
	
		
		
			
				
					|  |  |   isAlertVisible = false; |  |  |   isAlertVisible = false; | 
			
		
	
	
		
		
			
				
					|  | 
 |