forked from jsnbuchanan/crowd-funder-for-time-pwa
refactor project screen: add action to record a give from it, and add checks to give confirmation buttons
This commit is contained in:
@@ -15,7 +15,17 @@
|
||||
<fa icon="chevron-left" class="fa-fw"></fa>
|
||||
</button>
|
||||
Idea
|
||||
<h2 class="text-xl font-semibold">{{ name }}</h2>
|
||||
<h2 class="text-xl font-semibold">
|
||||
{{ name }}
|
||||
<button
|
||||
v-if="activeDid === issuer || activeDid === agentDid"
|
||||
@click="onEditClick()"
|
||||
title="Edit"
|
||||
data-testId="editClaimButton"
|
||||
>
|
||||
<fa icon="pen" class="text-sm text-blue-500 ml-2 mb-1" />
|
||||
</button>
|
||||
</h2>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
@@ -104,15 +114,6 @@
|
||||
<fa icon="file-lines" class="pl-2 pt-1 text-blue-500" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<button
|
||||
v-if="activeDid === issuer || activeDid === agentDid"
|
||||
type="button"
|
||||
class="block w-full text-center text-md uppercase 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-1.5 py-2 rounded-md"
|
||||
@click="onEditClick()"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="grid items-start grid-cols-1 sm:grid-cols-2 gap-4 mt-4">
|
||||
@@ -159,31 +160,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="activeDid && isRegistered" class="mt-4">
|
||||
<div class="text-center">
|
||||
<button
|
||||
data-testId="offerButton"
|
||||
@click="openOfferDialog()"
|
||||
class="block w-full text-lg font-bold 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-2 py-3 rounded-md"
|
||||
>
|
||||
Offer (maybe with conditions)...
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<OfferDialog
|
||||
ref="customOfferDialog"
|
||||
:projectId="this.projectId"
|
||||
:projectName="this.name"
|
||||
/>
|
||||
|
||||
<div v-if="activeDid && isRegistered">
|
||||
<div class="text-center">
|
||||
<p class="mt-2 mt-4 text-center">Record a contribution from:</p>
|
||||
</div>
|
||||
<ul
|
||||
class="grid grid-cols-4 sm:grid-cols-5 md:grid-cols-6 gap-x-3 gap-y-5 text-center mb-5"
|
||||
class="grid grid-cols-4 sm:grid-cols-5 md:grid-cols-6 gap-x-3 gap-y-5 text-center mb-5 mt-2"
|
||||
>
|
||||
<li @click="openGiftDialog({ name: 'you', did: activeDid })">
|
||||
<li @click="openGiftDialogToProject({ name: 'you', did: activeDid })">
|
||||
<fa icon="hand" class="fa-fw text-blue-500 text-5xl cursor-pointer" />
|
||||
<h3
|
||||
class="mt-5 text-xs text-blue-500 font-medium text-ellipsis whitespace-nowrap overflow-hidden cursor-pointer"
|
||||
@@ -191,7 +175,7 @@
|
||||
You
|
||||
</h3>
|
||||
</li>
|
||||
<li @click="openGiftDialog()">
|
||||
<li @click="openGiftDialogToProject()">
|
||||
<img
|
||||
src="../assets/blank-square.svg"
|
||||
class="mx-auto border border-blue-300 rounded-md mb-1 cursor-pointer"
|
||||
@@ -203,9 +187,9 @@
|
||||
</h3>
|
||||
</li>
|
||||
<li
|
||||
v-for="contact in allContacts.slice(0, 6)"
|
||||
v-for="contact in allContacts.slice(0, 5)"
|
||||
:key="contact.did"
|
||||
@click="openGiftDialog(contact)"
|
||||
@click="openGiftDialogToProject(contact)"
|
||||
>
|
||||
<EntityIcon
|
||||
:contact="contact"
|
||||
@@ -218,27 +202,41 @@
|
||||
{{ contact.name || "(no name)" }}
|
||||
</h3>
|
||||
</li>
|
||||
<li>
|
||||
<span
|
||||
v-if="allContacts.length >= 5"
|
||||
@click="onClickAllContactsGifting()"
|
||||
class="flex align-bottom text-xs text-blue-500 mt-12 cursor-pointer"
|
||||
>
|
||||
... or someone else...
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!--
|
||||
Ideally, this button should only be visible when the active account has more than 7 or 11 contacts in their list
|
||||
(we want to limit the grid count above to 8 or 12 accounts to keep it compact)
|
||||
-->
|
||||
<a
|
||||
v-if="allContacts.length >= 7"
|
||||
@click="onClickAllContactsGifting()"
|
||||
class="block text-center text-md font-bold 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-2 py-3 rounded-md"
|
||||
>
|
||||
Show More Contacts…
|
||||
</a>
|
||||
|
||||
<GiftedDialog ref="customGiveDialog" :projectId="this.projectId" />
|
||||
<GiftedDialog ref="giveDialogToThis" :toProjectId="this.projectId" />
|
||||
</div>
|
||||
|
||||
<!-- Offers & Gifts to & from this -->
|
||||
<div class="grid items-start grid-cols-1 sm:grid-cols-3 gap-4 mt-4">
|
||||
<!-- First, offers on the left-->
|
||||
<div class="bg-slate-100 px-4 py-3 rounded-md">
|
||||
<h3 class="text-sm font-semibold mb-3">Offered To This Idea</h3>
|
||||
<div v-if="activeDid && isRegistered">
|
||||
<div class="text-center">
|
||||
<button
|
||||
data-testId="offerButton"
|
||||
@click="openOfferDialog()"
|
||||
class="block w-full bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white rounded-md"
|
||||
>
|
||||
Offer (maybe with conditions)...
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<OfferDialog
|
||||
ref="customOfferDialog"
|
||||
:projectId="this.projectId"
|
||||
:projectName="this.name"
|
||||
/>
|
||||
|
||||
<h3 class="text-lg font-bold mb-3 mt-4">Offered To This Idea</h3>
|
||||
|
||||
<div v-if="offersToThis.length === 0">
|
||||
(None yet. Wanna
|
||||
@@ -300,15 +298,27 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Now, gives TO this project in the middle -->
|
||||
<!-- (similar to "FROM" gift display below) -->
|
||||
<div class="bg-slate-100 px-4 py-3 rounded-md">
|
||||
<h3 class="text-sm font-semibold mb-3">Given To This Idea</h3>
|
||||
<div v-if="activeDid && isRegistered">
|
||||
<div class="text-center">
|
||||
<button
|
||||
@click="openGiftDialogToProject()"
|
||||
class="block w-full bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white rounded-md"
|
||||
>
|
||||
Given To This...
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 class="text-lg font-bold mb-3 mt-4">Given To This Idea</h3>
|
||||
|
||||
<div v-if="givesToThis.length === 0">
|
||||
(None yet. If you've seen something, say something by clicking a
|
||||
contact above.)
|
||||
</div>
|
||||
|
||||
<!-- similar to gift display below -->
|
||||
<ul v-else class="text-sm border-t border-slate-300">
|
||||
<li
|
||||
v-for="give in givesToThis"
|
||||
@@ -346,12 +356,22 @@
|
||||
<a @click="onClickLoadClaim(give.jwtId)">
|
||||
<fa icon="file-lines" class="text-blue-500 cursor-pointer" />
|
||||
</a>
|
||||
|
||||
<a
|
||||
v-if="checkIsConfirmable(give)"
|
||||
@click="confirmConfirmClaim(give)"
|
||||
v-if="
|
||||
checkIsConfirmable(give) &&
|
||||
!recentlyCheckedAndUnconfirmableJwts.includes(give.jwtId)
|
||||
"
|
||||
@click="deepCheckConfirmable(give)"
|
||||
>
|
||||
<fa icon="circle-check" class="text-blue-500 cursor-pointer" />
|
||||
</a>
|
||||
<a v-else-if="checkingConfirmationForJwtId === give.jwtId">
|
||||
<fa icon="spinner" class="fa-spin-pulse" />
|
||||
</a>
|
||||
<a v-else @click="shallowNotifyWhyCannotConfirm(give)">
|
||||
<fa icon="circle-check" class="text-slate-500 cursor-pointer" />
|
||||
</a>
|
||||
</div>
|
||||
<div v-if="give.fullClaim.image" class="flex justify-center">
|
||||
<a :href="give.fullClaim.image" target="_blank">
|
||||
@@ -365,58 +385,91 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid items-start grid-cols-1 gap-4">
|
||||
<div
|
||||
v-if="givesProvidedByThis.length > 0"
|
||||
class="bg-slate-100 px-4 py-3 rounded-md"
|
||||
>
|
||||
<div>
|
||||
<h3 class="text-sm font-semibold border-b">
|
||||
Individuals Getting Contributions From This
|
||||
</h3>
|
||||
<!-- similar to gift display above -->
|
||||
<ul class="text-sm border-t border-slate-300">
|
||||
<li
|
||||
v-for="give in givesProvidedByThis"
|
||||
:key="give.id"
|
||||
class="py-1.5 border-b border-slate-300"
|
||||
>
|
||||
<div class="flex justify-between gap-4">
|
||||
<span>
|
||||
{{
|
||||
serverUtil.didInfo(
|
||||
give.recipientDid,
|
||||
activeDid,
|
||||
allMyDids,
|
||||
allContacts,
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
<span v-if="give.amount" class="whitespace-nowrap">
|
||||
<fa
|
||||
:icon="libsUtil.iconForUnitCode(give.unit)"
|
||||
class="fa-fw text-slate-400"
|
||||
/>{{ give.amount }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-slate-500">
|
||||
<fa icon="calendar" class="fa-fw text-slate-400" />
|
||||
{{ give.issuedAt?.substring(0, 10) }}
|
||||
</div>
|
||||
<div v-if="give.description" class="text-slate-500">
|
||||
<fa icon="comment" class="fa-fw text-slate-400" />
|
||||
{{ give.description }}
|
||||
</div>
|
||||
<a @click="onClickLoadClaim(give.jwtId)">
|
||||
<fa icon="file-lines" class="text-blue-500 cursor-pointer" />
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div v-if="givesProvidedByHitLimit" class="text-center">
|
||||
<button @click="loadGivesProvidedBy()">Load More</button>
|
||||
</div>
|
||||
<!-- Finally, gives FROM this project on the right -->
|
||||
<!-- (similar to "TO" gift display above) -->
|
||||
<div class="bg-slate-100 px-4 py-3 rounded-md">
|
||||
<div v-if="activeDid && isRegistered">
|
||||
<div class="text-center">
|
||||
<button
|
||||
@click="openGiftDialogFromProject()"
|
||||
class="block w-full bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white rounded-md"
|
||||
>
|
||||
Given By This...
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<GiftedDialog
|
||||
ref="giveDialogFromThis"
|
||||
:fromProjectId="this.projectId"
|
||||
/>
|
||||
|
||||
<h3 class="text-lg font-bold mb-3 mt-4">Benefitted By This</h3>
|
||||
|
||||
<div v-if="givesProvidedByThis.length === 0">(None yet.)</div>
|
||||
|
||||
<ul v-else class="text-sm border-t border-slate-300">
|
||||
<li
|
||||
v-for="give in givesProvidedByThis"
|
||||
:key="give.id"
|
||||
class="py-1.5 border-b border-slate-300"
|
||||
>
|
||||
<div class="flex justify-between gap-4">
|
||||
<span>
|
||||
{{
|
||||
serverUtil.didInfo(
|
||||
give.recipientDid,
|
||||
activeDid,
|
||||
allMyDids,
|
||||
allContacts,
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
<span v-if="give.amount" class="whitespace-nowrap">
|
||||
<fa
|
||||
:icon="libsUtil.iconForUnitCode(give.unit)"
|
||||
class="fa-fw text-slate-400"
|
||||
/>{{ give.amount }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-slate-500">
|
||||
<fa icon="calendar" class="fa-fw text-slate-400" />
|
||||
{{ give.issuedAt?.substring(0, 10) }}
|
||||
</div>
|
||||
<div v-if="give.description" class="text-slate-500">
|
||||
<fa icon="comment" class="fa-fw text-slate-400" />
|
||||
{{ give.description }}
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<a @click="onClickLoadClaim(give.jwtId)">
|
||||
<fa icon="file-lines" class="text-blue-500 cursor-pointer" />
|
||||
</a>
|
||||
|
||||
<a
|
||||
v-if="
|
||||
checkIsConfirmable(give) &&
|
||||
!recentlyCheckedAndUnconfirmableJwts.includes(give.jwtId)
|
||||
"
|
||||
@click="deepCheckConfirmable(give)"
|
||||
>
|
||||
<fa icon="circle-check" class="text-blue-500 cursor-pointer" />
|
||||
</a>
|
||||
<a v-else-if="checkingConfirmationForJwtId === give.jwtId">
|
||||
<fa icon="spinner" class="fa-spin-pulse" />
|
||||
</a>
|
||||
<a v-else @click="shallowNotifyWhyCannotConfirm(give)">
|
||||
<fa icon="circle-check" class="text-slate-500 cursor-pointer" />
|
||||
</a>
|
||||
</div>
|
||||
<div v-if="give.fullClaim.image" class="flex justify-center">
|
||||
<a :href="give.fullClaim.image" target="_blank">
|
||||
<img :src="give.fullClaim.image" class="h-24 mt-2 rounded-xl" />
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div v-if="givesProvidedByHitLimit" class="text-center">
|
||||
<button @click="loadGivesProvidedBy()">Load More</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -468,6 +521,7 @@ export default class ProjectViewView extends Vue {
|
||||
allMyDids: Array<string> = [];
|
||||
allContacts: Array<Contact> = [];
|
||||
apiServer = "";
|
||||
checkingConfirmationForJwtId = "";
|
||||
description = "";
|
||||
expanded = false;
|
||||
fulfilledByThis: PlanSummaryRecord | null = null;
|
||||
@@ -486,6 +540,7 @@ export default class ProjectViewView extends Vue {
|
||||
offersToThis: Array<OfferSummaryRecord> = [];
|
||||
offersHitLimit = false;
|
||||
projectId = ""; // handle ID
|
||||
recentlyCheckedAndUnconfirmableJwts = [];
|
||||
showDidCopy = false;
|
||||
startTime = "";
|
||||
truncatedDesc = "";
|
||||
@@ -847,12 +902,21 @@ export default class ProjectViewView extends Vue {
|
||||
);
|
||||
}
|
||||
|
||||
openGiftDialog(contact?: libsUtil.GiverReceiverInputInfo) {
|
||||
(this.$refs.customGiveDialog as GiftedDialog).open(
|
||||
openGiftDialogToProject(contact?: libsUtil.GiverReceiverInputInfo) {
|
||||
(this.$refs.giveDialogToThis as GiftedDialog).open(
|
||||
contact,
|
||||
undefined,
|
||||
undefined,
|
||||
"Given by " + (contact?.name || "someone not named"),
|
||||
(contact?.name || "Someone not named") + ` gave to this project`,
|
||||
);
|
||||
}
|
||||
|
||||
openGiftDialogFromProject() {
|
||||
(this.$refs.giveDialogFromThis as GiftedDialog).open(
|
||||
undefined,
|
||||
{ did: this.activeDid, name: "You" },
|
||||
undefined,
|
||||
`This project gave to you`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -896,7 +960,7 @@ export default class ProjectViewView extends Vue {
|
||||
const giver: libsUtil.GiverReceiverInputInfo = {
|
||||
did: libsUtil.offerGiverDid(offerRecord),
|
||||
};
|
||||
(this.$refs.customGiveDialog as GiftedDialog).open(
|
||||
(this.$refs.giveDialogToThis as GiftedDialog).open(
|
||||
giver,
|
||||
undefined,
|
||||
offer.handleId,
|
||||
@@ -932,20 +996,70 @@ export default class ProjectViewView extends Vue {
|
||||
}
|
||||
}
|
||||
|
||||
checkIsConfirmable(give: GiveSummaryRecord) {
|
||||
/**
|
||||
* @param confirmerIdList optional list of DIDs who confirmed; if missing, doesn't do a full server check
|
||||
*/
|
||||
checkIsConfirmable(give: GiveSummaryRecord, confirmerIdList?: string[]) {
|
||||
const giveDetails: GenericCredWrapper<GiveVerifiableCredential> = {
|
||||
...BLANK_GENERIC_SERVER_RECORD,
|
||||
claim: give.fullClaim,
|
||||
claimType: "GiveAction",
|
||||
issuer: give.agentDid,
|
||||
issuer: give.issuerDid,
|
||||
};
|
||||
return libsUtil.isGiveRecordTheUserCanConfirm(
|
||||
this.isRegistered,
|
||||
giveDetails,
|
||||
this.activeDid,
|
||||
confirmerIdList,
|
||||
);
|
||||
}
|
||||
|
||||
shallowNotifyWhyCannotConfirm(give: GiveSummaryRecord) {
|
||||
const confirmerIds = this.recentlyCheckedAndUnconfirmableJwts.includes(
|
||||
give.jwtId,
|
||||
)
|
||||
? [this.activeDid]
|
||||
: [];
|
||||
libsUtil.notifyWhyCannotConfirm(
|
||||
this.$notify,
|
||||
this.isRegistered,
|
||||
"GiveAction",
|
||||
give,
|
||||
this.activeDid,
|
||||
confirmerIds,
|
||||
);
|
||||
}
|
||||
|
||||
async deepCheckConfirmable(give: GiveSummaryRecord) {
|
||||
this.checkingConfirmationForJwtId = give.jwtId;
|
||||
const confirmerInfo: libsUtil.ConfirmerData | undefined =
|
||||
await libsUtil.retrieveConfirmerIdList(
|
||||
this.apiServer,
|
||||
give.jwtId,
|
||||
give.issuerDid,
|
||||
this.activeDid,
|
||||
);
|
||||
if (
|
||||
this.checkIsConfirmable(give, confirmerInfo?.confirmerIdList as string[])
|
||||
) {
|
||||
this.confirmConfirmClaim(give);
|
||||
} else {
|
||||
this.recentlyCheckedAndUnconfirmableJwts = [
|
||||
...this.recentlyCheckedAndUnconfirmableJwts,
|
||||
give.jwtId,
|
||||
];
|
||||
libsUtil.notifyWhyCannotConfirm(
|
||||
this.$notify,
|
||||
this.isRegistered,
|
||||
"GiveAction",
|
||||
give,
|
||||
this.activeDid,
|
||||
confirmerInfo?.confirmerIdList as string[],
|
||||
);
|
||||
}
|
||||
this.checkingConfirmationForJwtId = "";
|
||||
}
|
||||
|
||||
confirmConfirmClaim(give: GiveSummaryRecord) {
|
||||
this.$notify(
|
||||
{
|
||||
@@ -994,6 +1108,10 @@ export default class ProjectViewView extends Vue {
|
||||
},
|
||||
5000,
|
||||
);
|
||||
this.recentlyCheckedAndUnconfirmableJwts = [
|
||||
...this.recentlyCheckedAndUnconfirmableJwts,
|
||||
give.jwtId,
|
||||
];
|
||||
} else {
|
||||
console.error("Got error submitting the confirmation:", result);
|
||||
const message =
|
||||
|
||||
Reference in New Issue
Block a user