|  |  | @ -99,8 +99,26 @@ | 
			
		
	
		
			
				
					|  |  |  |           <div class="text-sm truncate" v-if="contact.publicKeyBase64"> | 
			
		
	
		
			
				
					|  |  |  |             Public Key (base 64): {{ contact.publicKeyBase64 }} | 
			
		
	
		
			
				
					|  |  |  |           </div> | 
			
		
	
		
			
				
					|  |  |  |           <button @click="deleteContact(contact)"> | 
			
		
	
		
			
				
					|  |  |  |             <fa icon="trash-can" class="text-slate-900 fa-fw ml-1" /> | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |           <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 @click="deleteContact(contact)" class="px-9"> | 
			
		
	
		
			
				
					|  |  |  |             <fa icon="trash-can" class="text-red-600 fa-fw ml-1" /> | 
			
		
	
		
			
				
					|  |  |  |           </button> | 
			
		
	
		
			
				
					|  |  |  |           <div v-if="showGiveTotals" class="float-right"> | 
			
		
	
		
			
				
					|  |  |  |             <div class="float-right"> | 
			
		
	
	
		
			
				
					|  |  | @ -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<Contact>, 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<void> { | 
			
		
	
	
		
			
				
					|  |  | @ -479,3 +584,32 @@ export default class ContactsView extends Vue { | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | </script> | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | <style> | 
			
		
	
		
			
				
					|  |  |  | /* 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 the tooltip text - see examples below! */ | 
			
		
	
		
			
				
					|  |  |  |   position: absolute; | 
			
		
	
		
			
				
					|  |  |  |   z-index: 1; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | /* Show the tooltip text when you mouse over the tooltip container */ | 
			
		
	
		
			
				
					|  |  |  | .tooltip:hover .tooltiptext { | 
			
		
	
		
			
				
					|  |  |  |   visibility: visible; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | </style> | 
			
		
	
	
		
			
				
					|  |  | 
 |