forked from trent_larson/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:
@@ -47,7 +47,8 @@
|
||||
giverDid: giver?.did,
|
||||
giverName: giver?.name,
|
||||
offerId,
|
||||
fulfillsProjectId: projectId,
|
||||
fulfillsProjectId: toProjectId,
|
||||
providerProjectId: fromProjectId,
|
||||
recipientDid: receiver?.did,
|
||||
recipientName: receiver?.name,
|
||||
unitCode,
|
||||
@@ -98,7 +99,8 @@ import { Contact } from "@/db/tables/contacts";
|
||||
export default class GiftedDialog extends Vue {
|
||||
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
||||
|
||||
@Prop projectId = "";
|
||||
@Prop fromProjectId = "";
|
||||
@Prop toProjectId = "";
|
||||
|
||||
activeDid = "";
|
||||
allContacts: Array<Contact> = [];
|
||||
@@ -294,9 +296,11 @@ export default class GiftedDialog extends Vue {
|
||||
description,
|
||||
amount,
|
||||
unitCode,
|
||||
this.projectId,
|
||||
this.toProjectId,
|
||||
this.offerId,
|
||||
this.isTrade,
|
||||
undefined,
|
||||
this.fromProjectId,
|
||||
);
|
||||
|
||||
if (
|
||||
|
||||
153
src/libs/util.ts
153
src/libs/util.ts
@@ -5,7 +5,7 @@ import { Buffer } from "buffer";
|
||||
import * as R from "ramda";
|
||||
import { useClipboard } from "@vueuse/core";
|
||||
|
||||
import { DEFAULT_PUSH_SERVER } from "@/constants/app";
|
||||
import { DEFAULT_PUSH_SERVER, NotificationIface } from "@/constants/app";
|
||||
import {
|
||||
accountsDB,
|
||||
retrieveSettingsForActiveAccount,
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
containsHiddenDid,
|
||||
GenericCredWrapper,
|
||||
GenericVerifiableCredential,
|
||||
GiveSummaryRecord,
|
||||
OfferVerifiableCredential,
|
||||
} from "@/libs/endorserServer";
|
||||
import { KeyMeta } from "@/libs/crypto/vc";
|
||||
@@ -101,10 +102,14 @@ export const isGlobalUri = (uri: string) => {
|
||||
return uri && uri.match(new RegExp(/^[A-Za-z][A-Za-z0-9+.-]+:/));
|
||||
};
|
||||
|
||||
export const isGiveClaimType = (claimType?: string) => {
|
||||
return claimType === "GiveAction";
|
||||
};
|
||||
|
||||
export const isGiveAction = (
|
||||
veriClaim: GenericCredWrapper<GenericVerifiableCredential>,
|
||||
) => {
|
||||
return veriClaim.claimType === "GiveAction";
|
||||
return isGiveClaimType(veriClaim.claimType);
|
||||
};
|
||||
|
||||
export const nameForDid = (
|
||||
@@ -136,16 +141,75 @@ export const doCopyTwoSecRedo = (text: string, fn: () => void) => {
|
||||
.then(() => setTimeout(fn, 2000));
|
||||
};
|
||||
|
||||
export interface ConfirmerData {
|
||||
confirmerIdList: string[];
|
||||
confsVisibleToIdList: string[];
|
||||
numConfsNotVisible: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return only confirmers, excluding the issuer and hidden DIDs
|
||||
*/
|
||||
export async function retrieveConfirmerIdList(
|
||||
apiServer: string,
|
||||
claimId: string,
|
||||
claimIssuerId: string,
|
||||
userDid: string,
|
||||
): Promise<ConfirmerData | undefined> {
|
||||
const confirmUrl =
|
||||
apiServer +
|
||||
"/api/report/issuersWhoClaimedOrConfirmed?claimId=" +
|
||||
encodeURIComponent(serverUtil.stripEndorserPrefix(claimId));
|
||||
const confirmHeaders = await serverUtil.getHeaders(userDid);
|
||||
const response = await axios.get(confirmUrl, {
|
||||
headers: confirmHeaders,
|
||||
});
|
||||
if (response.status === 200) {
|
||||
const resultList1 = response.data.result || [];
|
||||
//const publicUrls = resultList.publicUrls || [];
|
||||
delete resultList1.publicUrls;
|
||||
// exclude hidden DIDs
|
||||
const resultList2 = R.reject(serverUtil.isHiddenDid, resultList1);
|
||||
// exclude the issuer
|
||||
const resultList3 = R.reject(
|
||||
(did: string) => did === claimIssuerId,
|
||||
resultList2,
|
||||
);
|
||||
const confirmerIdList = resultList3;
|
||||
let 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
|
||||
numConfsNotVisible = numConfsNotVisible - 1;
|
||||
}
|
||||
const confsVisibleToIdList = response.data.result.resultVisibleToDids || [];
|
||||
const result: ConfirmerData = {
|
||||
confirmerIdList,
|
||||
confsVisibleToIdList,
|
||||
numConfsNotVisible,
|
||||
};
|
||||
return result;
|
||||
} else {
|
||||
console.error(
|
||||
"Bad response status of",
|
||||
response.status,
|
||||
"for confirmers:",
|
||||
response,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns true if the user can confirm the claim
|
||||
* @param veriClaim is expected to have fields: claim, claimType, and issuer
|
||||
*/
|
||||
export const isGiveRecordTheUserCanConfirm = (
|
||||
export function isGiveRecordTheUserCanConfirm(
|
||||
isRegistered: boolean,
|
||||
veriClaim: GenericCredWrapper<GenericVerifiableCredential>,
|
||||
activeDid: string,
|
||||
confirmerIdList: string[] = [],
|
||||
) => {
|
||||
): boolean {
|
||||
return (
|
||||
isRegistered &&
|
||||
isGiveAction(veriClaim) &&
|
||||
@@ -153,7 +217,78 @@ export const isGiveRecordTheUserCanConfirm = (
|
||||
veriClaim.issuer !== activeDid &&
|
||||
!containsHiddenDid(veriClaim.claim)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export function notifyWhyCannotConfirm(
|
||||
notifyFun: (notification: NotificationIface, timeout: number) => void,
|
||||
isRegistered: boolean,
|
||||
claimType: string | undefined,
|
||||
giveDetails: GiveSummaryRecord | undefined,
|
||||
activeDid: string,
|
||||
confirmerIdList: string[] = [],
|
||||
) {
|
||||
if (!isRegistered) {
|
||||
notifyFun(
|
||||
{
|
||||
group: "alert",
|
||||
type: "info",
|
||||
title: "Not Registered",
|
||||
text: "Someone needs to register you before you can confirm.",
|
||||
},
|
||||
3000,
|
||||
);
|
||||
} else if (!isGiveClaimType(claimType)) {
|
||||
notifyFun(
|
||||
{
|
||||
group: "alert",
|
||||
type: "info",
|
||||
title: "Not A Give",
|
||||
text: "This is not a giving action to confirm.",
|
||||
},
|
||||
3000,
|
||||
);
|
||||
} else if (confirmerIdList.includes(activeDid)) {
|
||||
notifyFun(
|
||||
{
|
||||
group: "alert",
|
||||
type: "info",
|
||||
title: "Already Confirmed",
|
||||
text: "You already confirmed this claim.",
|
||||
},
|
||||
3000,
|
||||
);
|
||||
} else if (giveDetails?.issuerDid == activeDid) {
|
||||
notifyFun(
|
||||
{
|
||||
group: "alert",
|
||||
type: "info",
|
||||
title: "Cannot Confirm",
|
||||
text: "You cannot confirm this because you issued this claim.",
|
||||
},
|
||||
3000,
|
||||
);
|
||||
} else if (serverUtil.containsHiddenDid(giveDetails?.fullClaim)) {
|
||||
notifyFun(
|
||||
{
|
||||
group: "alert",
|
||||
type: "info",
|
||||
title: "Cannot Confirm",
|
||||
text: "You cannot confirm this because some people are hidden.",
|
||||
},
|
||||
3000,
|
||||
);
|
||||
} else {
|
||||
notifyFun(
|
||||
{
|
||||
group: "alert",
|
||||
type: "info",
|
||||
title: "Cannot Confirm",
|
||||
text: "You cannot confirm this claim. There are no other details -- we can help more if you contact us and send us screenshots.",
|
||||
},
|
||||
3000,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function blobToBase64(blob: Blob): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -191,9 +326,9 @@ export function base64ToBlob(base64DataUrl: string, sliceSize = 512) {
|
||||
* @returns the DID of the person who offered, or undefined if hidden
|
||||
* @param veriClaim is expected to have fields: claim and issuer
|
||||
*/
|
||||
export const offerGiverDid: (
|
||||
arg0: GenericCredWrapper<OfferVerifiableCredential>,
|
||||
) => string | undefined = (veriClaim) => {
|
||||
export function offerGiverDid(
|
||||
veriClaim: GenericCredWrapper<OfferVerifiableCredential>,
|
||||
): string | undefined {
|
||||
let giver;
|
||||
if (
|
||||
veriClaim.claim.offeredBy?.identifier &&
|
||||
@@ -204,7 +339,7 @@ export const offerGiverDid: (
|
||||
giver = veriClaim.issuer;
|
||||
}
|
||||
return giver;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns true if the user can fulfill the offer
|
||||
|
||||
@@ -24,9 +24,11 @@
|
||||
{{ capitalizeAndInsertSpacesBeforeCaps(veriClaim.claimType) }}
|
||||
<button
|
||||
v-if="
|
||||
['GiveAction', 'Offer'].includes(
|
||||
['GiveAction', 'Offer', 'PlanAction'].includes(
|
||||
veriClaim.claimType as string,
|
||||
) && veriClaim.issuer === activeDid
|
||||
// a PlanAction agent also could edit one of those, but rather than add more Plan-specific logic to detect the agent
|
||||
// we'll let them click the Project link and edit from there
|
||||
"
|
||||
@click="onClickEditClaim"
|
||||
title="Edit"
|
||||
@@ -150,6 +152,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<fa icon="comment" class="text-slate-400" />
|
||||
{{ issuerName }} posted that.
|
||||
</div>
|
||||
|
||||
<div class="mt-8">
|
||||
<button
|
||||
@@ -217,7 +223,7 @@
|
||||
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.
|
||||
The following people have confirmed this claim.
|
||||
<ul class="ml-4">
|
||||
<li
|
||||
v-for="confirmerId in confirmerIdList"
|
||||
@@ -503,6 +509,7 @@ export default class ClaimView extends Vue {
|
||||
fullClaimMessage = "";
|
||||
isEditedGlobalId = false;
|
||||
isRegistered = false;
|
||||
issuerName = "";
|
||||
numConfsNotVisible = 0; // number of hidden DIDs in the confirmerIdList, minus the issuer if they aren't visible
|
||||
providersForGive: ProviderInfo[] = [];
|
||||
showIdCopy = false;
|
||||
@@ -545,6 +552,7 @@ export default class ClaimView extends Vue {
|
||||
const accounts = accountsDB.accounts;
|
||||
const accountsArr: Array<Account> = await accounts?.toArray();
|
||||
this.allMyDids = accountsArr.map((acc) => acc.did);
|
||||
this.issuerName = this.didInfo(this.veriClaim.issuer);
|
||||
|
||||
const pathParam = window.location.pathname.substring("/claim/".length);
|
||||
let claimId;
|
||||
@@ -696,32 +704,16 @@ export default class ClaimView extends Vue {
|
||||
}
|
||||
|
||||
// retrieve the list of confirmers
|
||||
const confirmUrl =
|
||||
this.apiServer +
|
||||
"/api/report/issuersWhoClaimedOrConfirmed?claimId=" +
|
||||
encodeURIComponent(serverUtil.stripEndorserPrefix(claimId));
|
||||
const confirmHeaders = await serverUtil.getHeaders(userDid);
|
||||
const response = await this.axios.get(confirmUrl, {
|
||||
headers: confirmHeaders,
|
||||
});
|
||||
if (response.status === 200) {
|
||||
const resultList1 = response.data.result || [];
|
||||
//const publicUrls = resultList.publicUrls || [];
|
||||
delete resultList1.publicUrls;
|
||||
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 || [];
|
||||
const confirmerInfo = await libsUtil.retrieveConfirmerIdList(
|
||||
this.apiServer,
|
||||
claimId,
|
||||
this.veriClaim.issuer,
|
||||
userDid,
|
||||
);
|
||||
if (confirmerInfo) {
|
||||
this.confirmerIdList = confirmerInfo.confirmerIdList;
|
||||
this.confsVisibleToIdList = confirmerInfo.confsVisibleToIdList;
|
||||
this.numConfsNotVisible = confirmerInfo.numConfsNotVisible;
|
||||
} else {
|
||||
this.confsVisibleErrorMessage =
|
||||
"Had problems retrieving confirmations.";
|
||||
@@ -736,7 +728,7 @@ export default class ClaimView extends Vue {
|
||||
title: "Error",
|
||||
text: "Something went wrong retrieving claim data.",
|
||||
},
|
||||
-1,
|
||||
3000,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -921,6 +913,12 @@ export default class ClaimView extends Vue {
|
||||
},
|
||||
};
|
||||
(this.$router as Router).push(route);
|
||||
} else if (this.veriClaim.claimType === "PlanAction") {
|
||||
const route = {
|
||||
name: "new-edit-project",
|
||||
query: { projectId: this.veriClaim.handleId },
|
||||
};
|
||||
(this.$router as Router).push(route);
|
||||
} else {
|
||||
console.error(
|
||||
"Unrecognized claim type for edit:",
|
||||
|
||||
@@ -172,7 +172,7 @@
|
||||
Nobody that you know issued or confirmed this claim.
|
||||
</div>
|
||||
<div v-if="confirmerIdList.length > 0">
|
||||
The following people issued or confirmed this claim.
|
||||
The following people confirmed this claim.
|
||||
<ul class="ml-4">
|
||||
<li
|
||||
v-for="confirmerId in confirmerIdList"
|
||||
@@ -661,34 +661,16 @@ export default class ClaimView extends Vue {
|
||||
}
|
||||
|
||||
// retrieve the list of confirmers
|
||||
const confirmUrl =
|
||||
this.apiServer +
|
||||
"/api/report/issuersWhoClaimedOrConfirmed?claimId=" +
|
||||
encodeURIComponent(serverUtil.stripEndorserPrefix(claimId));
|
||||
const confirmHeaders = await serverUtil.getHeaders(userDid);
|
||||
const response = await this.axios.get(confirmUrl, {
|
||||
headers: confirmHeaders,
|
||||
});
|
||||
if (response.status === 200) {
|
||||
const resultList1 = response.data.result || [];
|
||||
//const publicUrls = resultList.publicUrls || [];
|
||||
delete resultList1.publicUrls;
|
||||
// remove any hidden DIDs
|
||||
const resultList2 = R.reject(serverUtil.isHiddenDid, resultList1);
|
||||
// remove confirmations by this user
|
||||
const resultList3 = R.reject(
|
||||
(did: string) => did === this.giveDetails?.issuerDid,
|
||||
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 || [];
|
||||
const confirmerInfo = await libsUtil.retrieveConfirmerIdList(
|
||||
this.apiServer,
|
||||
claimId,
|
||||
this.veriClaim.issuer,
|
||||
userDid,
|
||||
);
|
||||
if (confirmerInfo) {
|
||||
this.confirmerIdList = confirmerInfo.confirmerIdList;
|
||||
this.confsVisibleToIdList = confirmerInfo.confsVisibleToIdList;
|
||||
this.numConfsNotVisible = confirmerInfo.numConfsNotVisible;
|
||||
} else {
|
||||
this.confsVisibleErrorMessage =
|
||||
"Had problems retrieving confirmations.";
|
||||
@@ -797,6 +779,17 @@ export default class ClaimView extends Vue {
|
||||
}
|
||||
|
||||
notifyWhyCannotConfirm() {
|
||||
libsUtil.notifyWhyCannotConfirm(
|
||||
this.$notify,
|
||||
this.isRegistered,
|
||||
this.veriClaim.claimType,
|
||||
this.giveDetails,
|
||||
this.activeDid,
|
||||
this.confirmerIdList,
|
||||
);
|
||||
}
|
||||
|
||||
notifyWhyCannotConfirmBak() {
|
||||
if (!this.isRegistered) {
|
||||
this.$notify(
|
||||
{
|
||||
@@ -853,7 +846,7 @@ export default class ClaimView extends Vue {
|
||||
group: "alert",
|
||||
type: "info",
|
||||
title: "Cannot Confirm",
|
||||
text: "You cannot confirm this claim.",
|
||||
text: "You cannot confirm this claim. There are no other details, but we can help more if you contact us and send us screenshots.",
|
||||
},
|
||||
3000,
|
||||
);
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<GiftedDialog ref="customDialog" :projectId="projectId" />
|
||||
<GiftedDialog ref="customDialog" :toProjectId="projectId" />
|
||||
</section>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
? fulfillsProjectName
|
||||
: givenToRecipient
|
||||
? recipientName
|
||||
: "someone unidentified"
|
||||
: "someone not named"
|
||||
}}</span
|
||||
>
|
||||
</h1>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
? projectName
|
||||
: offeredToRecipient
|
||||
? recipientName
|
||||
: "someone unidentified"
|
||||
: "someone not named"
|
||||
}}</span
|
||||
>
|
||||
</h1>
|
||||
|
||||
@@ -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