Trent Larson
5 months ago
11 changed files with 992 additions and 34 deletions
@ -0,0 +1,933 @@ |
|||
<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="detailsForGive"> |
|||
<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 One of Your Own |
|||
</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="veriClaim.claim?.object"> |
|||
<fa icon="hand-holding-dollar" class="fa-fw text-slate-400" /> |
|||
{{ |
|||
displayAmount( |
|||
veriClaim.claim.object.unitCode, |
|||
veriClaim.claim.object.amountOfThisGood, |
|||
) |
|||
}} |
|||
600 |
|||
</div> |
|||
<div> |
|||
<fa icon="message" class="fa-fw text-slate-400" /> |
|||
{{ veriClaim.claim?.object ? "and" : "" }} |
|||
{{ veriClaim.claim?.description }} |
|||
</div> |
|||
<div> |
|||
<fa icon="arrow-up" class="fa-fw text-slate-400" /> |
|||
to {{ recipientName }} |
|||
</div> |
|||
<div> |
|||
<fa icon="calendar" class="fa-fw text-slate-400" /> |
|||
{{ veriClaim.issuedAt?.replace(/T/, " ").replace(/Z/, " UTC") }} |
|||
</div> |
|||
|
|||
<!-- Fullfills Links --> |
|||
|
|||
<!-- fullfills links for a give --> |
|||
<div class="mt-2" v-if="detailsForGive?.fulfillsPlanHandleId"> |
|||
<router-link |
|||
:to=" |
|||
'/project/' + |
|||
encodeURIComponent(detailsForGive?.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=" |
|||
detailsForGive?.fulfillsType && |
|||
detailsForGive?.fulfillsType !== 'PlanAction' && |
|||
detailsForGive?.fulfillsHandleId |
|||
" |
|||
> |
|||
<!-- router-link to /claim/ only changes URL path --> |
|||
<router-link |
|||
:to=" |
|||
'/claim/' + |
|||
encodeURIComponent(detailsForGive?.fulfillsHandleId) |
|||
" |
|||
class="text-blue-500 mt-2 cursor-pointer" |
|||
target="_blank" |
|||
> |
|||
This fulfills |
|||
{{ |
|||
capitalizeAndInsertSpacesBeforeCapsWithAPrefix( |
|||
detailsForGive.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="fa-fw text-slate-400" /> |
|||
{{ issuerName }} said that. |
|||
</div> |
|||
|
|||
<div class="flex justify-center"> |
|||
<button |
|||
v-if=" |
|||
libsUtil.isGiveRecordTheUserCanConfirm( |
|||
veriClaim, |
|||
activeDid, |
|||
confirmerIdList, |
|||
) |
|||
" |
|||
@click="confirmConfirmClaim()" |
|||
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" |
|||
> |
|||
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 One of Your Own |
|||
</a> |
|||
</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="veriClaim.issuer == 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">...</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 |
|||
detailsForGive = 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.detailsForGive = 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); |
|||
|
|||
this.urlForNewGive = "/gifted-details?"; |
|||
if (this.veriClaim.claim.object) { |
|||
if (this.veriClaim.claim.object.amountOfThisGood) { |
|||
this.urlForNewGive += |
|||
"&amountInput=" + |
|||
encodeURIComponent( |
|||
String(this.veriClaim.claim.object.amountOfThisGood), |
|||
); |
|||
} |
|||
if (this.veriClaim.claim.object.unitCode) { |
|||
this.urlForNewGive += |
|||
"&unitCode=" + |
|||
encodeURIComponent(this.veriClaim.claim.object.unitCode as string); |
|||
} |
|||
} |
|||
if (this.veriClaim.claim.description) { |
|||
this.urlForNewGive += |
|||
"&description=" + |
|||
encodeURIComponent(this.veriClaim.claim.description as string); |
|||
} |
|||
this.giverName = this.didInfo( |
|||
this.veriClaim.claim.agent?.identifier as string, |
|||
); |
|||
if (this.veriClaim.claim.agent) { |
|||
this.urlForNewGive += |
|||
"&giverDid=" + |
|||
encodeURIComponent(this.veriClaim.claim.agent.identifier as string) + |
|||
"&giverName=" + |
|||
encodeURIComponent(this.giverName); |
|||
} |
|||
this.recipientName = this.didInfo( |
|||
this.veriClaim.claim.recipient?.identifier as string, |
|||
); |
|||
if (this.veriClaim.claim.recipient) { |
|||
this.urlForNewGive += |
|||
"&recipientDid=" + |
|||
encodeURIComponent( |
|||
this.veriClaim.claim.recipient.identifier as string, |
|||
) + |
|||
"&recipientName=" + |
|||
encodeURIComponent(this.recipientName); |
|||
} |
|||
if (this.veriClaim.claim.image) { |
|||
this.urlForNewGive += |
|||
"&image=" + encodeURIComponent(this.veriClaim.claim.image as string); |
|||
} |
|||
if (this.veriClaim.claim.fulfills) { |
|||
this.urlForNewGive += this.fulfillsUrlSnippet( |
|||
this.veriClaim.claim.fulfills, |
|||
); |
|||
if (Array.isArray(this.veriClaim.claim.fulfills)) { |
|||
for (const fulfills of this.veriClaim.claim.fulfills) { |
|||
console.log("adding fulfills:", fulfills); |
|||
this.urlForNewGive += this.fulfillsUrlSnippet(fulfills); |
|||
} |
|||
} |
|||
} |
|||
|
|||
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.detailsForGive = 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, |
|||
); |
|||
} |
|||
|
|||
// 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.veriClaim.issuer, |
|||
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, |
|||
); |
|||
} |
|||
} |
|||
|
|||
fulfillsUrlSnippet(object) { |
|||
if (object["@type"] === "PlanAction") { |
|||
return "&projectId=" + encodeURIComponent(object.identifier); |
|||
} else if (object["@type"] === "OfferAction") { |
|||
return "&offerId=" + encodeURIComponent(object.identifier); |
|||
} else { |
|||
return ""; |
|||
} |
|||
} |
|||
|
|||
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.", |
|||
}, |
|||
5000, |
|||
); |
|||
} 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.veriClaim.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.veriClaim.issuer == 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.veriClaim.claim)) { |
|||
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