17 changed files with 1066 additions and 66 deletions
			
			
		| @ -0,0 +1,878 @@ | |||
| <template> | |||
|   <QuickNav /> | |||
|   <!-- CONTENT --> | |||
|   <section id="Content" class="p-6 pb-24 max-w-3xl mx-auto"> | |||
|     <!-- Breadcrumb --> | |||
|     <div id="ViewBreadcrumb" class="mb-8"> | |||
|       <h1 class="text-lg text-center font-light relative px-7"> | |||
|         <!-- Back --> | |||
|         <button | |||
|           @click="$router.go(-1)" | |||
|           class="text-lg text-center px-2 py-1 absolute -left-2 -top-1" | |||
|         > | |||
|           <fa icon="chevron-left" class="fa-fw" /> | |||
|         </button> | |||
|         <span | |||
|           v-if=" | |||
|             libsUtil.isGiveRecordTheUserCanConfirm( | |||
|               veriClaim, | |||
|               activeDid, | |||
|               confirmerIdList, | |||
|             ) | |||
|           " | |||
|         > | |||
|           Do you agree? | |||
|         </span> | |||
|         <span v-else> Details </span> | |||
|       </h1> | |||
|     </div> | |||
| 
 | |||
|     <div v-if="giveDetails"> | |||
|       <div class="flex justify-center"> | |||
|         <button | |||
|           class="col-span-1 bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-4 py-2 rounded-md" | |||
|           v-if=" | |||
|             libsUtil.isGiveRecordTheUserCanConfirm( | |||
|               veriClaim, | |||
|               activeDid, | |||
|               confirmerIdList, | |||
|             ) | |||
|           " | |||
|           @click="confirmConfirmClaim()" | |||
|         > | |||
|           Confirm | |||
|           <fa icon="circle-check" class="ml-2 text-white cursor-pointer" /> | |||
|         </button> | |||
|         <button | |||
|           v-else | |||
|           @click="notifyWhyCannotConfirm()" | |||
|           class="col-span-1 bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-4 py-2 rounded-md" | |||
|         > | |||
|           Confirm | |||
|           <fa icon="circle-check" class="ml-2 text-white cursor-pointer" /> | |||
|         </button> | |||
|         <a | |||
|           class="col-span-1 bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white ml-2 px-4 py-2 rounded-md" | |||
|           :href="urlForNewGive" | |||
|         > | |||
|           Record a Similar One | |||
|         </a> | |||
|       </div> | |||
| 
 | |||
|       <!-- Details --> | |||
|       <div class="bg-slate-100 rounded-md overflow-hidden px-4 py-3 mt-4"> | |||
|         <div class="block flex gap-4 overflow-hidden"> | |||
|           <div class="overflow-hidden"> | |||
|             <div class="text-sm"> | |||
|               <div> | |||
|                 <fa icon="arrow-down" class="fa-fw text-slate-400" /> | |||
|                 {{ giverName }} | |||
|               </div> | |||
|               <div class="ml-6">gave</div> | |||
|               <div v-if="giveDetails.amount"> | |||
|                 <fa icon="hand-holding-dollar" class="fa-fw text-slate-400" /> | |||
|                 {{ displayAmount(giveDetails.unit, giveDetails.amount) }} | |||
|               </div> | |||
|               <div v-if="giveDetails.description"> | |||
|                 <fa icon="message" class="fa-fw text-slate-400" /> | |||
|                 {{ giveDetails.amount ? "and:" : "" }} | |||
|                 {{ giveDetails.description }} | |||
|               </div> | |||
|               <div class="ml-6">to</div> | |||
|               <div> | |||
|                 <fa icon="arrow-up" class="fa-fw text-slate-400" /> | |||
|                 {{ recipientName }} | |||
|               </div> | |||
|               <div> | |||
|                 <fa icon="calendar" class="fa-fw text-slate-400" /> | |||
|                 on | |||
|                 {{ giveDetails.issuedAt.substring(0, 10) }} | |||
|               </div> | |||
| 
 | |||
|               <!-- Fullfills Links --> | |||
| 
 | |||
|               <!-- fullfills links for a give --> | |||
|               <div class="mt-2" v-if="giveDetails?.fulfillsPlanHandleId"> | |||
|                 <router-link | |||
|                   :to=" | |||
|                     '/project/' + | |||
|                     encodeURIComponent(giveDetails?.fulfillsPlanHandleId) | |||
|                   " | |||
|                   class="text-blue-500 mt-2 cursor-pointer" | |||
|                   target="_blank" | |||
|                 > | |||
|                   This fulfills a bigger plan | |||
|                   <fa icon="arrow-up-right-from-square" class="fa-fw" /> | |||
|                 </router-link> | |||
|               </div> | |||
|               <!-- if there's another, it's probably fulfilling an offer, too --> | |||
|               <div | |||
|                 v-if=" | |||
|                   giveDetails?.fulfillsType && | |||
|                   giveDetails?.fulfillsType !== 'PlanAction' && | |||
|                   giveDetails?.fulfillsHandleId | |||
|                 " | |||
|               > | |||
|                 <!-- router-link to /claim/ only changes URL path --> | |||
|                 <router-link | |||
|                   :to=" | |||
|                     '/claim/' + | |||
|                     encodeURIComponent(giveDetails?.fulfillsHandleId) | |||
|                   " | |||
|                   class="text-blue-500 mt-2 cursor-pointer" | |||
|                   target="_blank" | |||
|                 > | |||
|                   This fulfills | |||
|                   {{ | |||
|                     capitalizeAndInsertSpacesBeforeCapsWithAPrefix( | |||
|                       giveDetails.fulfillsType, | |||
|                     ) | |||
|                   }} | |||
|                   <fa icon="arrow-up-right-from-square" class="fa-fw" /> | |||
|                 </router-link> | |||
|               </div> | |||
|             </div> | |||
|           </div> | |||
|         </div> | |||
|       </div> | |||
|       <div class="mt-2"> | |||
|         <fa icon="comment" class="text-slate-400" /> | |||
|         {{ issuerName }} posted that. | |||
|       </div> | |||
| 
 | |||
|       <div v-if="libsUtil.isGiveAction(veriClaim)" class="mt-4"> | |||
|         <h2 class="font-bold uppercase text-xl mt-8 mb-2">Confirmations</h2> | |||
| 
 | |||
|         <span v-if="totalConfirmers() === 0">Nobody has confirmed this.</span> | |||
|         <span v-else-if="totalConfirmers() === 1"> | |||
|           One person has confirmed this. | |||
|         </span> | |||
|         <span v-else> | |||
|           {{ totalConfirmers() }} people have confirmed this. | |||
|         </span> | |||
| 
 | |||
|         <div v-if="totalConfirmers() > 0"> | |||
|           <div | |||
|             v-if=" | |||
|               confirmerIdList.length === 0 && confsVisibleToIdList.length === 0 | |||
|             " | |||
|           > | |||
|             Nobody that you know confirmed this claim, nor do they have any | |||
|             confirmers in their network. | |||
|           </div> | |||
| 
 | |||
|           <div | |||
|             v-if=" | |||
|               confirmerIdList.length === 0 && confsVisibleToIdList.length > 0 | |||
|             " | |||
|           > | |||
|             <!-- Only show if this person has links to confirmers (below). --> | |||
|             Nobody that you know has issued or confirmed this claim. | |||
|           </div> | |||
|           <div v-if="confirmerIdList.length > 0"> | |||
|             The following people have issued or confirmed this claim. | |||
|             <ul class="ml-4"> | |||
|               <li | |||
|                 v-for="confirmerId in confirmerIdList" | |||
|                 :key="confirmerId" | |||
|                 class="list-disc ml-4" | |||
|               > | |||
|                 <div class="flex gap-4"> | |||
|                   <div class="grow overflow-hidden"> | |||
|                     <div class="text-sm"> | |||
|                       {{ didInfo(confirmerId) }} | |||
|                       <span v-if="!serverUtil.isEmptyOrHiddenDid(confirmerId)"> | |||
|                         <button | |||
|                           @click=" | |||
|                             copyToClipboard( | |||
|                               'The DID of ' + confirmerId, | |||
|                               confirmerId, | |||
|                             ) | |||
|                           " | |||
|                         > | |||
|                           <fa icon="copy" class="text-slate-400 fa-fw" /> | |||
|                         </button> | |||
|                       </span> | |||
|                     </div> | |||
|                   </div> | |||
|                 </div> | |||
|               </li> | |||
|             </ul> | |||
|           </div> | |||
| 
 | |||
|           <!-- | |||
|            Never need to show this message: | |||
|            "Nobody that you know can see someone who has confirmed this claim." | |||
| 
 | |||
|            If there is nobody in the confirmerIdList then we'll show the combined "nobody" message. | |||
|            If there is somebody in the confirmerIdList then that's all they need to show. | |||
|            --> | |||
| 
 | |||
|           <!-- Now show anyone linked to confirmers. --> | |||
|           <div v-if="confsVisibleToIdList.length > 0"> | |||
|             The following people can connect you with people who have issued or | |||
|             confirmed this claim. | |||
|             <ul class="ml-4"> | |||
|               <li | |||
|                 v-for="confsVisibleTo in confsVisibleToIdList" | |||
|                 :key="confsVisibleTo" | |||
|                 class="list-disc ml-4" | |||
|               > | |||
|                 <div class="flex gap-4"> | |||
|                   <div class="grow overflow-hidden"> | |||
|                     <div class="text-sm"> | |||
|                       {{ didInfo(confsVisibleTo) }} | |||
|                       <span | |||
|                         v-if="!serverUtil.isEmptyOrHiddenDid(confsVisibleTo)" | |||
|                       > | |||
|                         <button | |||
|                           @click=" | |||
|                             copyToClipboard( | |||
|                               'The DID of ' + confsVisibleTo, | |||
|                               confsVisibleTo, | |||
|                             ) | |||
|                           " | |||
|                         > | |||
|                           <fa icon="copy" class="text-slate-400 fa-fw" /> | |||
|                         </button> | |||
|                       </span> | |||
|                     </div> | |||
|                   </div> | |||
|                 </div> | |||
|               </li> | |||
|             </ul> | |||
|           </div> | |||
|         </div> | |||
| 
 | |||
|         <!-- explain if user cannot confirm --> | |||
|         <!-- Note that these conditions are mirrored in userCanConfirm(). --> | |||
|         <div v-if="confirmerIdList.includes(activeDid)"> | |||
|           You have confirmed this claim. | |||
|         </div> | |||
|         <div v-else-if="giveDetails.agentDid == activeDid"> | |||
|           You cannot confirm this because you issued this claim, so you already | |||
|           count as confirming it. | |||
|         </div> | |||
|         <div v-else-if="serverUtil.containsHiddenDid(veriClaim.claim)"> | |||
|           You cannot confirm this because it contains hidden identifiers. | |||
|         </div> | |||
|       </div> | |||
| 
 | |||
|       <h2 | |||
|         class="font-bold uppercase text-xl text-blue-500 mt-8 mb-2 cursor-pointer" | |||
|         @click="showDetails = !showDetails" | |||
|       > | |||
|         {{ serverUtil.containsHiddenDid(veriClaim) ? "Visible " : "" }}Details | |||
|         <span v-if="!showDetails"><fa icon="chevron-down" /></span> | |||
|         <span v-else><fa icon="chevron-up" /></span> | |||
|       </h2> | |||
|       <div v-if="showDetails"> | |||
|         <div | |||
|           v-if=" | |||
|             serverUtil.containsHiddenDid(veriClaim) && | |||
|             R.isEmpty(veriClaimDidsVisible) | |||
|           " | |||
|           class="mb-2" | |||
|         > | |||
|           Some of the details are not visible to you; they show as "HIDDEN". | |||
|           They are not visible to any of your direct contacts, either. | |||
|           <span v-if="canShare"> | |||
|             If you'd like to ask any of your contacts to take a look and see if | |||
|             their contacts can see more details, | |||
|             <a @click="onClickShareClaim()" class="text-blue-500" | |||
|               >click to send them this info</a | |||
|             > | |||
|             and see if they are willing to make an introduction. | |||
|           </span> | |||
|           <span v-else> | |||
|             If you'd like to ask any of your contacts to take a look and see if | |||
|             their contacts can see more details, | |||
|             <a | |||
|               @click="copyToClipboard('Location', windowLocation.href)" | |||
|               class="text-blue-500" | |||
|               >share this page with them</a | |||
|             > | |||
|             and see if they are willing to make an introduction. | |||
|           </span> | |||
|         </div> | |||
| 
 | |||
|         <div v-if="!R.isEmpty(veriClaimDidsVisible)"> | |||
|           Some of the details are not visible to you but they are visible to | |||
|           some of your contacts. | |||
|           <span v-if="canShare"> | |||
|             If you'd like an introduction, | |||
|             <a @click="onClickShareClaim()" class="text-blue-500" | |||
|               >click to share the information with them and ask if they'll tell | |||
|               you more about the participants.</a | |||
|             > | |||
|           </span> | |||
|           <span v-else> | |||
|             If you'd like an introduction, | |||
|             <a | |||
|               @click="copyToClipboard('Location', windowLocation.href)" | |||
|               class="text-blue-500" | |||
|               >share this page with them and ask if they'll tell you more about | |||
|               about the participants.</a | |||
|             > | |||
|           </span> | |||
| 
 | |||
|           <div | |||
|             v-for="(visibleDidPath, index) of Object.keys(veriClaimDidsVisible)" | |||
|             :key="index" | |||
|             class="list-disc p-4" | |||
|           > | |||
|             <div class="text-sm"> | |||
|               <fa icon="minus" class="fa-fw" /> | |||
|               The {{ visibleDidPath }} is visible to: | |||
|             </div> | |||
|             <div class="ml-12 p-1"> | |||
|               <ul> | |||
|                 <li | |||
|                   v-for="(visDid, idx2) of veriClaimDidsVisible[visibleDidPath]" | |||
|                   :key="idx2" | |||
|                   class="list-disc" | |||
|                 > | |||
|                   <div class="text-sm mt-2"> | |||
|                     <span> | |||
|                       {{ didInfo(visDid) }} | |||
|                       <span v-if="!serverUtil.isEmptyOrHiddenDid(visDid)"> | |||
|                         <button | |||
|                           @click=" | |||
|                             copyToClipboard('The DID of ' + visDid, visDid) | |||
|                           " | |||
|                         > | |||
|                           <fa icon="copy" class="text-slate-400 fa-fw" /> | |||
|                         </button> | |||
|                       </span> | |||
|                       <span v-if="veriClaim.publicUrls?.[visDid]" | |||
|                         >, found at | |||
|                         <fa icon="globe" class="fa-fw text-slate-400" /> | |||
|                         <a | |||
|                           :href="veriClaim.publicUrls?.[visDid]" | |||
|                           class="text-blue-500" | |||
|                           >{{ | |||
|                             veriClaim.publicUrls[visDid].substring( | |||
|                               veriClaim.publicUrls[visDid].indexOf("//") + 2, | |||
|                             ) | |||
|                           }} | |||
|                         </a> | |||
|                       </span> | |||
|                     </span> | |||
|                   </div> | |||
|                 </li> | |||
|               </ul> | |||
|             </div> | |||
|           </div> | |||
|         </div> | |||
|         <!-- Keep the dump contents directly between > and < to avoid weird spacing. --> | |||
|         <pre | |||
|           class="text-sm overflow-x-scroll px-4 py-3 bg-slate-100 rounded-md" | |||
|           >{{ veriClaimDump }}</pre | |||
|         > | |||
|       </div> | |||
|     </div> | |||
|     <div v-else>This does not have details to confirm.</div> | |||
| 
 | |||
|     <div class="mt-4"> | |||
|       <a | |||
|         @click="showClaimPage(veriClaim.id)" | |||
|         class="text-blue-500 cursor-pointer" | |||
|       > | |||
|         <fa icon="file-lines" class="pl-2" /> | |||
|         All Generic Info | |||
|       </a> | |||
|     </div> | |||
|   </section> | |||
| </template> | |||
| 
 | |||
| <script lang="ts"> | |||
| import { AxiosError, RawAxiosRequestHeaders } from "axios"; | |||
| import * as yaml from "js-yaml"; | |||
| import * as R from "ramda"; | |||
| import { IIdentifier } from "@veramo/core"; | |||
| import { Component, Vue } from "vue-facing-decorator"; | |||
| import { useClipboard } from "@vueuse/core"; | |||
| 
 | |||
| import GiftedDialog from "@/components/GiftedDialog.vue"; | |||
| import QuickNav from "@/components/QuickNav.vue"; | |||
| import { NotificationIface } from "@/constants/app"; | |||
| import { accountsDB, db } from "@/db/index"; | |||
| import { Account } from "@/db/tables/accounts"; | |||
| import { Contact } from "@/db/tables/contacts"; | |||
| import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings"; | |||
| import { accessToken } from "@/libs/crypto"; | |||
| import * as serverUtil from "@/libs/endorserServer"; | |||
| import { displayAmount, GiverReceiverInputInfo } from "@/libs/endorserServer"; | |||
| import * as libsUtil from "@/libs/util"; | |||
| import { isGiveAction } from "@/libs/util"; | |||
| 
 | |||
| @Component({ | |||
|   methods: { displayAmount }, | |||
|   components: { GiftedDialog, QuickNav }, | |||
| }) | |||
| export default class ClaimView extends Vue { | |||
|   $notify!: (notification: NotificationIface, timeout?: number) => void; | |||
| 
 | |||
|   accountIdentityStr: string = "null"; | |||
|   activeDid = ""; | |||
|   allMyDids: Array<string> = []; | |||
|   allContacts: Array<Contact> = []; | |||
|   apiServer = ""; | |||
| 
 | |||
|   canShare = false; | |||
|   confirmerIdList: string[] = []; // list of DIDs that have confirmed this claim excluding the issuer | |||
|   confsVisibleErrorMessage = ""; | |||
|   confsVisibleToIdList: string[] = []; // list of DIDs that can see any confirmer | |||
|   giveDetails = null; | |||
|   giverName = ""; | |||
|   issuerName = ""; | |||
|   numConfsNotVisible = 0; // number of hidden DIDs in the confirmerIdList, minus the issuer if they aren't visible | |||
|   recipientName = ""; | |||
|   showDetails = false; | |||
|   urlForNewGive = ""; | |||
|   veriClaim = serverUtil.BLANK_GENERIC_SERVER_RECORD; | |||
|   veriClaimDump = ""; | |||
|   veriClaimDidsVisible = {}; | |||
|   windowLocation = window.location; | |||
| 
 | |||
|   R = R; | |||
|   yaml = yaml; | |||
|   libsUtil = libsUtil; | |||
|   serverUtil = serverUtil; | |||
| 
 | |||
|   resetThisValues() { | |||
|     this.confirmerIdList = []; | |||
|     this.confsVisibleErrorMessage = ""; | |||
|     this.confsVisibleToIdList = []; | |||
|     this.giveDetails = null; | |||
|     this.numConfsNotVisible = 0; | |||
|     this.urlForNewGive = ""; | |||
|     this.veriClaim = serverUtil.BLANK_GENERIC_SERVER_RECORD; | |||
|     this.veriClaimDump = ""; | |||
|   } | |||
| 
 | |||
|   async mounted() { | |||
|     await db.open(); | |||
|     const settings = (await db.settings.get(MASTER_SETTINGS_KEY)) as Settings; | |||
|     this.activeDid = settings?.activeDid || ""; | |||
|     this.apiServer = settings?.apiServer || ""; | |||
|     this.allContacts = await db.contacts.toArray(); | |||
| 
 | |||
|     await accountsDB.open(); | |||
|     const accounts = accountsDB.accounts; | |||
|     const accountsArr: Array<Account> = await accounts?.toArray(); | |||
|     this.allMyDids = accountsArr.map((acc) => acc.did); | |||
|     const account = accountsArr.find((acc) => acc.did === this.activeDid); | |||
|     this.accountIdentityStr = (account?.identity as string) || "null"; | |||
|     const identity = JSON.parse(this.accountIdentityStr); | |||
| 
 | |||
|     const pathParam = window.location.pathname.substring( | |||
|       "/confirm-gift/".length, | |||
|     ); | |||
|     let claimId; | |||
|     if (pathParam) { | |||
|       claimId = decodeURIComponent(pathParam); | |||
|       await this.loadClaim(claimId, identity); | |||
|     } else { | |||
|       this.$notify( | |||
|         { | |||
|           group: "alert", | |||
|           type: "danger", | |||
|           title: "Error", | |||
|           text: "No claim ID was provided.", | |||
|         }, | |||
|         3000, | |||
|       ); | |||
|     } | |||
| 
 | |||
|     // When Chrome compatibility is fixed https://developer.mozilla.org/en-US/docs/Web/API/Web_Share_API#api.navigator.canshare | |||
|     // then use this truer check: navigator.canShare && navigator.canShare() | |||
|     this.canShare = !!navigator.share; | |||
|   } | |||
| 
 | |||
|   // insert a space before any capital letters except the initial letter | |||
|   // (and capitalize initial letter, just in case) | |||
|   capitalizeAndInsertSpacesBeforeCaps(text: string) { | |||
|     return !text | |||
|       ? "" | |||
|       : text[0].toUpperCase() + text.substr(1).replace(/([A-Z])/g, " $1"); | |||
|   } | |||
| 
 | |||
|   capitalizeAndInsertSpacesBeforeCapsWithAPrefix(text: string) { | |||
|     const word = this.capitalizeAndInsertSpacesBeforeCaps(text); | |||
|     if (word) { | |||
|       // if the word starts with a vowel, use "an" instead of "a" | |||
|       const firstLetter = word[0].toLowerCase(); | |||
|       const vowels = ["a", "e", "i", "o", "u"]; | |||
|       const particle = vowels.includes(firstLetter) ? "an" : "a"; | |||
|       return particle + " " + word; | |||
|     } else { | |||
|       return ""; | |||
|     } | |||
|   } | |||
| 
 | |||
|   totalConfirmers() { | |||
|     return ( | |||
|       this.numConfsNotVisible + | |||
|       this.confirmerIdList.length + | |||
|       this.confsVisibleToIdList.length | |||
|     ); | |||
|   } | |||
| 
 | |||
|   public async getIdentity(activeDid: string): Promise<IIdentifier> { | |||
|     await accountsDB.open(); | |||
|     const account = (await accountsDB.accounts | |||
|       .where("did") | |||
|       .equals(activeDid) | |||
|       .first()) as Account; | |||
|     const identity = JSON.parse(account?.identity || "null"); | |||
| 
 | |||
|     if (!identity) { | |||
|       throw new Error( | |||
|         "Attempted to load project records with no identifier available.", | |||
|       ); | |||
|     } | |||
|     return identity; | |||
|   } | |||
| 
 | |||
|   public async getHeaders(identity: IIdentifier) { | |||
|     const headers: RawAxiosRequestHeaders = { | |||
|       "Content-Type": "application/json", | |||
|     }; | |||
|     if (identity) { | |||
|       const token = await accessToken(identity); | |||
|       headers["Authorization"] = "Bearer " + token; | |||
|     } | |||
|     return headers; | |||
|   } | |||
| 
 | |||
|   // Isn't there a better way to make this available to the template? | |||
|   didInfo(did: string | undefined) { | |||
|     return serverUtil.didInfo( | |||
|       did, | |||
|       this.activeDid, | |||
|       this.allMyDids, | |||
|       this.allContacts, | |||
|     ); | |||
|   } | |||
| 
 | |||
|   async loadClaim(claimId: string, identity: IIdentifier) { | |||
|     const urlPath = libsUtil.isGlobalUri(claimId) | |||
|       ? "/api/claim/byHandle/" | |||
|       : "/api/claim/"; | |||
|     const url = this.apiServer + urlPath + encodeURIComponent(claimId); | |||
| 
 | |||
|     try { | |||
|       const headers = await this.getHeaders(identity); | |||
|       const resp = await this.axios.get(url, { headers }); | |||
|       // resp.data is: | |||
|       // - a Jwt from https://api.endorser.ch/api-docs/ | |||
|       // - with a Give from https://endorser.ch/doc/html/transactions.html#id3 | |||
|       if (resp.status === 200) { | |||
|         this.veriClaim = resp.data; | |||
|         this.veriClaimDump = yaml.dump(this.veriClaim); | |||
|         this.veriClaimDidsVisible = libsUtil.findAllVisibleToDids( | |||
|           this.veriClaim, | |||
|           true, | |||
|         ); | |||
|       } else { | |||
|         // actually, axios typically throws an error so we never get here | |||
|         console.error("Error getting claim:", resp); | |||
|         this.$notify( | |||
|           { | |||
|             group: "alert", | |||
|             type: "danger", | |||
|             title: "Error", | |||
|             text: "There was a problem retrieving that claim.", | |||
|           }, | |||
|           3000, | |||
|         ); | |||
|         return; | |||
|       } | |||
| 
 | |||
|       // retrieve more details on Give, Offer, or Plan | |||
|       if (this.veriClaim.claimType !== "GiveAction") { | |||
|         // no need to go further... this page is for gifts | |||
|         return; | |||
|       } | |||
| 
 | |||
|       this.issuerName = this.didInfo(this.veriClaim.issuer); | |||
| 
 | |||
|       // use give record when possible since it may include edits | |||
|       const giveUrl = | |||
|         this.apiServer + | |||
|         "/api/v2/report/gives?handleId=" + | |||
|         encodeURIComponent(this.veriClaim.handleId as string); | |||
|       const giveHeaders = await this.getHeaders(identity); | |||
|       const giveResp = await this.axios.get(giveUrl, { | |||
|         headers: giveHeaders, | |||
|       }); | |||
|       // giveResp.data is a Give from https://api.endorser.ch/api-docs/ | |||
|       if (giveResp.status === 200) { | |||
|         this.giveDetails = giveResp.data.data[0]; | |||
|       } else { | |||
|         console.error("Error getting detailed give info:", giveResp); | |||
|         this.$notify( | |||
|           { | |||
|             group: "alert", | |||
|             type: "danger", | |||
|             title: "Error", | |||
|             text: "Something went wrong retrieving gift data.", | |||
|           }, | |||
|           3000, | |||
|         ); | |||
|       } | |||
| 
 | |||
|       this.urlForNewGive = "/gifted-details?"; | |||
|       if (this.giveDetails.amount) { | |||
|         this.urlForNewGive += | |||
|           "&amountInput=" + encodeURIComponent(String(this.giveDetails.amount)); | |||
|       } | |||
|       if (this.giveDetails.unit) { | |||
|         this.urlForNewGive += | |||
|           "&unitCode=" + encodeURIComponent(this.giveDetails.unit); | |||
|       } | |||
|       if (this.giveDetails.description) { | |||
|         this.urlForNewGive += | |||
|           "&description=" + encodeURIComponent(this.giveDetails.description); | |||
|       } | |||
|       this.giverName = this.didInfo(this.giveDetails.agentDid); | |||
|       if (this.giveDetails.agentDid) { | |||
|         this.urlForNewGive += | |||
|           "&giverDid=" + | |||
|           encodeURIComponent(this.giveDetails.agentDid) + | |||
|           "&giverName=" + | |||
|           encodeURIComponent(this.giverName); | |||
|       } | |||
|       this.recipientName = this.didInfo(this.giveDetails.recipientDid); | |||
|       if (this.giveDetails.recipientDid) { | |||
|         this.urlForNewGive += | |||
|           "&recipientDid=" + | |||
|           encodeURIComponent(this.giveDetails.recipientDid) + | |||
|           "&recipientName=" + | |||
|           encodeURIComponent(this.recipientName); | |||
|       } | |||
|       if (this.giveDetails.fullClaim.image) { | |||
|         this.urlForNewGive += | |||
|           "&image=" + encodeURIComponent(this.giveDetails.fullClaim.image); | |||
|       } | |||
|       if ( | |||
|         this.giveDetails.type == "Offer" && | |||
|         this.giveDetails.fulfillsHandleId | |||
|       ) { | |||
|         this.urlForNewGive += | |||
|           "&offerId=" + encodeURIComponent(this.giveDetails.fulfillsHandleId); | |||
|       } | |||
|       if (this.giveDetails.fulfillsPlanHandleId) { | |||
|         this.urlForNewGive += | |||
|           "&projectId=" + | |||
|           encodeURIComponent(this.giveDetails.fulfillsPlanHandleId); | |||
|       } | |||
| 
 | |||
|       // retrieve the list of confirmers | |||
|       const confirmUrl = | |||
|         this.apiServer + | |||
|         "/api/report/issuersWhoClaimedOrConfirmed?claimId=" + | |||
|         encodeURIComponent(serverUtil.stripEndorserPrefix(claimId)); | |||
|       const confirmHeaders = await this.getHeaders(identity); | |||
|       const response = await this.axios.get(confirmUrl, { | |||
|         headers: confirmHeaders, | |||
|       }); | |||
|       if (response.status === 200) { | |||
|         const resultList1 = response.data.result || []; | |||
|         const resultList2 = R.reject(serverUtil.isHiddenDid, resultList1); | |||
|         const resultList3 = R.reject( | |||
|           (did: string) => did === this.giveDetails.agentDid, | |||
|           resultList2, | |||
|         ); | |||
|         this.confirmerIdList = resultList3; | |||
|         this.numConfsNotVisible = resultList1.length - resultList2.length; | |||
|         if (resultList3.length === resultList2.length) { | |||
|           // the issuer was not in the "visible" list so they must be hidden | |||
|           // so subtract them from the non-visible confirmers count | |||
|           this.numConfsNotVisible = this.numConfsNotVisible - 1; | |||
|         } | |||
|         this.confsVisibleToIdList = | |||
|           response.data.result.resultVisibleToDids || []; | |||
|       } else { | |||
|         this.confsVisibleErrorMessage = | |||
|           "Had problems retrieving confirmations."; | |||
|       } | |||
|     } catch (error: unknown) { | |||
|       const serverError = error as AxiosError; | |||
|       console.error("Error retrieving claim:", serverError); | |||
|       this.$notify( | |||
|         { | |||
|           group: "alert", | |||
|           type: "danger", | |||
|           title: "Error", | |||
|           text: "Something went wrong retrieving claim data.", | |||
|         }, | |||
|         3000, | |||
|       ); | |||
|     } | |||
|   } | |||
| 
 | |||
|   confirmConfirmClaim() { | |||
|     this.$notify( | |||
|       { | |||
|         group: "modal", | |||
|         type: "confirm", | |||
|         title: "Confirm", | |||
|         text: "Do you personally confirm that this is true?", | |||
|         onYes: async () => { | |||
|           await this.confirmClaim(); | |||
|         }, | |||
|       }, | |||
|       -1, | |||
|     ); | |||
|   } | |||
| 
 | |||
|   // similar code is found in ProjectViewView | |||
|   async confirmClaim() { | |||
|     // similar logic is found in endorser-mobile | |||
|     const goodClaim = serverUtil.removeSchemaContext( | |||
|       serverUtil.removeVisibleToDids( | |||
|         serverUtil.addLastClaimOrHandleAsIdIfMissing( | |||
|           this.veriClaim.claim, | |||
|           this.veriClaim.id, | |||
|           this.veriClaim.handleId, | |||
|         ), | |||
|       ), | |||
|     ); | |||
|     const confirmationClaim: serverUtil.GenericVerifiableCredential = { | |||
|       "@context": "https://schema.org", | |||
|       "@type": "AgreeAction", | |||
|       object: goodClaim, | |||
|     }; | |||
|     const result = await serverUtil.createAndSubmitClaim( | |||
|       confirmationClaim, | |||
|       await this.getIdentity(this.activeDid), | |||
|       this.apiServer, | |||
|       this.axios, | |||
|     ); | |||
|     if (result.type === "success") { | |||
|       this.$notify( | |||
|         { | |||
|           group: "alert", | |||
|           type: "success", | |||
|           title: "Success", | |||
|           text: "Confirmation submitted.", | |||
|         }, | |||
|         3000, | |||
|       ); | |||
|     } else { | |||
|       console.error("Got error submitting the confirmation:", result); | |||
|       this.$notify( | |||
|         { | |||
|           group: "alert", | |||
|           type: "danger", | |||
|           title: "Error", | |||
|           text: "There was a problem submitting the confirmation. See logs for more info.", | |||
|         }, | |||
|         5000, | |||
|       ); | |||
|     } | |||
|   } | |||
| 
 | |||
|   showClaimPage(claimId: string) { | |||
|     const route = { | |||
|       path: "/claim/" + encodeURIComponent(claimId), | |||
|     }; | |||
|     this.$router.push(route).then(async () => { | |||
|       this.resetThisValues(); | |||
|       await this.loadClaim(claimId, JSON.parse(this.accountIdentityStr)); | |||
|     }); | |||
|   } | |||
| 
 | |||
|   openFulfillGiftDialog() { | |||
|     const giver: GiverReceiverInputInfo = { | |||
|       did: libsUtil.offerGiverDid(this.veriClaim), | |||
|     }; | |||
|     (this.$refs.customGiveDialog as GiftedDialog).open( | |||
|       giver, | |||
|       undefined, | |||
|       this.giveDetails.handleId, | |||
|       "Offer fulfilled by " + (giver?.name || "someone not named"), | |||
|     ); | |||
|   } | |||
| 
 | |||
|   copyToClipboard(name: string, text: string) { | |||
|     useClipboard() | |||
|       .copy(text) | |||
|       .then(() => { | |||
|         this.$notify( | |||
|           { | |||
|             group: "alert", | |||
|             type: "toast", | |||
|             title: "Copied", | |||
|             text: (name || "That") + " was copied to the clipboard.", | |||
|           }, | |||
|           2000, | |||
|         ); | |||
|       }); | |||
|   } | |||
| 
 | |||
|   notifyWhyCannotConfirm() { | |||
|     if (!isGiveAction(this.veriClaim)) { | |||
|       this.$notify( | |||
|         { | |||
|           group: "alert", | |||
|           type: "info", | |||
|           title: "Not A Give", | |||
|           text: "This is not a giving action to confirm.", | |||
|         }, | |||
|         3000, | |||
|       ); | |||
|     } else if (this.confirmerIdList.includes(this.activeDid)) { | |||
|       this.$notify( | |||
|         { | |||
|           group: "alert", | |||
|           type: "info", | |||
|           title: "Already Confirmed", | |||
|           text: "You have already confirmed this claim.", | |||
|         }, | |||
|         3000, | |||
|       ); | |||
|     } else if (this.giveDetails.agentDid == this.activeDid) { | |||
|       this.$notify( | |||
|         { | |||
|           group: "alert", | |||
|           type: "info", | |||
|           title: "Cannot Confirm", | |||
|           text: "You cannot confirm this because you issued this claim.", | |||
|         }, | |||
|         3000, | |||
|       ); | |||
|     } else if (serverUtil.containsHiddenDid(this.giveDetails.fullClaim)) { | |||
|       this.$notify( | |||
|         { | |||
|           group: "alert", | |||
|           type: "info", | |||
|           title: "Cannot Confirm", | |||
|           text: "You cannot confirm this because it contains hidden identifiers.", | |||
|         }, | |||
|         3000, | |||
|       ); | |||
|     } else { | |||
|       this.$notify( | |||
|         { | |||
|           group: "alert", | |||
|           type: "info", | |||
|           title: "Cannot Confirm", | |||
|           text: "You cannot confirm this claim.", | |||
|         }, | |||
|         3000, | |||
|       ); | |||
|     } | |||
|   } | |||
| 
 | |||
|   onClickShareClaim() { | |||
|     window.navigator.share({ | |||
|       title: "Help Connect Me", | |||
|       text: "I'm trying to find the full details of this claim. Can you help me?", | |||
|       url: this.windowLocation.href, | |||
|     }); | |||
|   } | |||
| } | |||
| </script> | |||
					Loading…
					
					
				
		Reference in new issue