Browse Source

show a 'give' button directly on offers in the ProjectView

kb/add-usage-guide
Trent Larson 10 months ago
parent
commit
be6ec6745a
  1. 4
      project.task.yaml
  2. 9
      src/components/GiftedDialog.vue
  3. 2
      src/libs/endorserServer.ts
  4. 38
      src/libs/util.ts
  5. 2
      src/main.ts
  6. 62
      src/views/ClaimView.vue
  7. 60
      src/views/ProjectViewView.vue

4
project.task.yaml

@ -1,9 +1,9 @@
tasks:
- make give action executable right from an offer
- make a confirmation action executable right from a give
- link to the project claim from the project screen
- supply the projectId to the OfferDialog just like we do with the offerId
- the confirm button on each give on the ProjectViewView page doesn't have all the context of the ClaimView page, so it can show sometimes inappropriately; consider consolidation
- choose an agent via a contact chooser (not just copy-paste a DID)
- make the "give" on contact screen work like other give (allowing donation vs current blank)
- check that 'show more contacts' from the contact-give-list on the project screen includes project ID

9
src/components/GiftedDialog.vue

@ -90,7 +90,6 @@ export default class GiftedDialog extends Vue {
@Prop message = "";
@Prop projectId = "";
@Prop offerId = "";
@Prop showGivenToUser = false;
activeDid = "";
@ -103,6 +102,7 @@ export default class GiftedDialog extends Vue {
description = "";
givenToUser = false;
isTrade = false;
offerId = "";
unitCode = "HUR";
visible = false;
@ -117,8 +117,8 @@ export default class GiftedDialog extends Vue {
/* eslint-disable prettier/prettier */
UNIT_LONG: Record<string, string> = {
"BTC": "BTC",
"ETH": "ETH",
"BTC": "Bitcoin",
"ETH": "Ethereum",
"HUR": "hours",
"USD": "dollars",
};
@ -152,7 +152,7 @@ export default class GiftedDialog extends Vue {
}
}
open(giver: GiverInputInfo) {
open(giver?: GiverInputInfo, offerId?: string) {
this.description = "";
this.giver = giver || {};
if (!this.giver.name) {
@ -166,6 +166,7 @@ export default class GiftedDialog extends Vue {
// if we show "given to user" selection, default checkbox to true
this.givenToUser = this.showGivenToUser;
this.amountInput = "0";
this.offerId = offerId || "";
this.visible = true;
}

2
src/libs/endorserServer.ts

@ -76,6 +76,8 @@ export interface GiveServerRecord {
export interface OfferServerRecord {
amount: number;
amountGiven: number;
fullClaim: OfferVerifiableCredential;
handleId: string;
offeredByDid: string;
recipientDid: string;
requirementsMet: boolean;

38
src/libs/util.ts

@ -7,6 +7,7 @@ import { accountsDB, db } from "@/db/index";
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
import { deriveAddress, generateSeed, newIdentifier } from "@/libs/crypto";
import { GenericServerRecord, containsHiddenDid } from "@/libs/endorserServer";
import * as serverUtil from "@/libs/endorserServer";
// eslint-disable-next-line @typescript-eslint/no-var-requires
const Buffer = require("buffer/").Buffer;
@ -20,23 +21,54 @@ export const isGlobalUri = (uri: string) => {
export const ONBOARD_MESSAGE =
"1) Check that they have entered their name on the profile page in their device. 2) Add them to your Contacts by scanning with the QR icon that is by the input box. 3) Click the person icon to register them. 4) Have them go to their Contact page and scan your QR to add you to their list.";
export const isConfirmable = (veriClaim: GenericServerRecord) => {
export const giveIsConfirmable = (veriClaim: GenericServerRecord) => {
return veriClaim.claimType === "GiveAction";
};
export const userCanConfirm = (
/**
* @returns true if the user can confirm the claim
* @param veriClaim is expected to have fields: claim, claimType, and issuer
*/
export const giveRecordTheUserCanConfirm = (
veriClaim: GenericServerRecord,
activeDid: string,
confirmerIdList: string[] = [],
) => {
return (
isConfirmable(veriClaim) &&
giveIsConfirmable(veriClaim) &&
!confirmerIdList.includes(activeDid) &&
veriClaim.issuer !== activeDid &&
!containsHiddenDid(veriClaim.claim)
);
};
/**
* @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: GenericServerRecord,
) => string | undefined = (veriClaim) => {
let giver;
if (
veriClaim.claim.offeredBy?.identifier &&
!serverUtil.isHiddenDid(veriClaim.claim.offeredBy.identifier as string)
) {
giver = veriClaim.claim.offeredBy.identifier;
} else if (veriClaim.issuer && !serverUtil.isHiddenDid(veriClaim.issuer)) {
giver = veriClaim.issuer;
}
return giver;
};
/**
* @returns true if the user can fulfill the offer
* @param veriClaim is expected to have fields: claim, claimType, and issuer
*/
export const canFulfillOffer = (veriClaim: GenericServerRecord) => {
return !!(veriClaim.claimType === "Offer" && offerGiverDid(veriClaim));
};
/**
* Generates a new identity, saves it to the database, and sets it as the active identity.
* @return {Promise<string>} with the DID of the new identity

2
src/main.ts

@ -39,6 +39,7 @@ import {
faGift,
faGlobe,
faHand,
faHandHoldingHeart,
faHouseChimney,
faLocationDot,
faLongArrowAltLeft,
@ -93,6 +94,7 @@ library.add(
faGift,
faGlobe,
faHand,
faHandHoldingHeart,
faHouseChimney,
faLocationDot,
faLongArrowAltLeft,

62
src/views/ClaimView.vue

@ -47,27 +47,31 @@
<div class="columns-3">
<button
class="col-span-1 bg-blue-600 text-white px-4 py-2 rounded-md"
v-if="userCanConfirm(veriClaim, activeDid, confirmerIdList)"
v-if="
libsUtil.giveRecordTheUserCanConfirm(
veriClaim,
activeDid,
confirmerIdList,
)
"
@click="confirmClaim(veriClaim.id)"
>
Confirm
<fa icon="circle-check" class="ml-2 text-white cursor-pointer" />
</button>
<button
v-if="canFulfillOffer()"
@click="openGiftDialog()"
v-if="libsUtil.canFulfillOffer(veriClaim)"
@click="openFulfillGiftDialog()"
class="col-span-1 block w-fit text-center text-md bg-blue-600 text-white px-1.5 py-2 rounded-md"
>
Affirm Delivery
<fa icon="hand-holding-heart" class="ml-2 text-white cursor-pointer" />
</button>
</div>
<GiftedDialog
ref="customGiveDialog"
message="Offer fulfilled by"
:offerId="veriClaim.handleId"
/>
<GiftedDialog ref="customGiveDialog" message="Offer fulfilled by" />
<div v-if="isConfirmable(veriClaim)">
<div v-if="libsUtil.giveIsConfirmable(veriClaim)">
<h2 class="font-bold uppercase text-xl mt-8 mb-2">Confirmations</h2>
<span v-if="totalConfirmers() === 0">Nobody has confirmed this.</span>
@ -148,7 +152,7 @@
You cannot confirm this because you issued this claim, so you already
count as confirming it.
</div>
<div v-else-if="containsHiddenDid(veriClaim.claim)">
<div v-else-if="serverUtil.containsHiddenDid(veriClaim.claim)">
You cannot confirm this because it contains hidden identifiers.
</div>
</div>
@ -206,7 +210,7 @@ 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 { isConfirmable, userCanConfirm } from "@/libs/util";
import * as libsUtil from "@/libs/util";
import QuickNav from "@/components/QuickNav.vue";
import EntityIcon from "@/components/EntityIcon.vue";
import { Account } from "@/db/tables/accounts";
@ -240,9 +244,8 @@ export default class ClaimView extends Vue {
veriClaimDump = "";
yaml = yaml;
containsHiddenDid = serverUtil.containsHiddenDid;
isConfirmable = isConfirmable;
userCanConfirm = userCanConfirm;
libsUtil = libsUtil;
serverUtil = serverUtil;
async created() {
await db.open();
@ -284,28 +287,6 @@ export default class ClaimView extends Vue {
: text[0].toUpperCase() + text.substr(1).replace(/([A-Z])/g, " $1");
}
offerGiverDid(): string | undefined {
let giver;
if (
this.veriClaim.claim.offeredBy?.identifier &&
!serverUtil.isHiddenDid(
this.veriClaim.claim.offeredBy.identifier as string,
)
) {
giver = this.veriClaim.claim.offeredBy.identifier;
} else if (
this.veriClaim.issuer &&
!serverUtil.isHiddenDid(this.veriClaim.issuer)
) {
giver = this.veriClaim.issuer;
}
return giver;
}
canFulfillOffer() {
return this.veriClaim.claimType === "Offer" && this.offerGiverDid();
}
totalConfirmers() {
return (
this.numConfsNotVisible +
@ -531,11 +512,14 @@ export default class ClaimView extends Vue {
}
}
openGiftDialog() {
openFulfillGiftDialog() {
const giver: GiverInputInfo = {
did: this.offerGiverDid(),
did: libsUtil.offerGiverDid(this.veriClaim),
};
(this.$refs.customGiveDialog as GiftedDialog).open(giver);
(this.$refs.customGiveDialog as GiftedDialog).open(
giver,
this.veriClaim.handleId,
);
}
}
</script>

60
src/views/ProjectViewView.vue

@ -123,7 +123,7 @@
<ul class="grid grid-cols-4 gap-x-3 gap-y-5 text-center mb-5">
<li @click="openGiftDialog()">
<EntityIcon
:entityId="null"
:entityId="undefined"
:iconSize="64"
class="mx-auto border border-slate-300 rounded-md mb-1"
></EntityIcon>
@ -198,12 +198,24 @@
</span>
</div>
<div v-if="offer.objectDescription" class="text-slate-500">
<fa icon="comment" class="fa-fw text-slate-400"></fa>
<fa icon="comment" class="fa-fw text-slate-400" />
{{ offer.objectDescription }}
</div>
<div class="flex justify-between">
<a @click="onClickLoadClaim(offer.jwtId)" class="cursor-pointer">
<fa icon="circle-info" class="pl-2 pt-1 text-blue-500"></fa>
<a
@click="onClickLoadClaim(offer.jwtId as string)"
class="cursor-pointer"
>
<fa icon="circle-info" class="pl-2 pt-1 text-blue-500" />
</a>
<a
v-if="checkIsFulfillable(offer)"
@click="onClickFulfillGiveToOffer(offer)"
>
<fa
icon="hand-holding-heart"
class="text-blue-500 cursor-pointer"
/>
</a>
</div>
</li>
@ -243,15 +255,15 @@
</span>
</div>
<div v-if="give.description" class="text-slate-500">
<fa icon="comment" class="fa-fw text-slate-400"></fa>
<fa icon="comment" class="fa-fw text-slate-400" />
{{ give.description }}
</div>
<div class="flex justify-between">
<a @click="onClickLoadClaim(give.jwtId)">
<fa icon="circle-info" class="text-blue-500 cursor-pointer"></fa>
<fa icon="circle-info" class="text-blue-500 cursor-pointer" />
</a>
<a v-if="checkIsConfirmable(give)" @click="confirmClaim(give)">
<fa icon="circle-check" class="text-blue-500 cursor-pointer"></fa>
<fa icon="circle-check" class="text-blue-500 cursor-pointer" />
</a>
</div>
</li>
@ -359,6 +371,7 @@ export default class ProjectViewView extends Vue {
truncateLength = 40;
url = "";
libsUtil = libsUtil;
serverUtil = serverUtil;
async created() {
@ -370,7 +383,7 @@ export default class ProjectViewView extends Vue {
await accountsDB.open();
const accounts = accountsDB.accounts;
const accountsArr = await accounts?.toArray();
const accountsArr: Account[] = await accounts?.toArray();
this.allMyDids = accountsArr.map((acc) => acc.did);
const account = accountsArr.find((acc) => acc.did === this.activeDid);
const identity = JSON.parse(account?.identity || "null");
@ -659,7 +672,7 @@ export default class ProjectViewView extends Vue {
);
}
openGiftDialog(contact: GiverInputInfo) {
openGiftDialog(contact?: GiverInputInfo) {
(this.$refs.customGiveDialog as GiftedDialog).open(contact);
}
@ -674,6 +687,33 @@ export default class ProjectViewView extends Vue {
this.$router.push(route);
}
checkIsFulfillable(offer: OfferServerRecord) {
const offerRecord: GenericServerRecord = {
...BLANK_GENERIC_SERVER_RECORD,
claim: offer.fullClaim,
claimType: "Offer",
issuer: offer.offeredByDid,
};
console.log(
"checking for can fulfill ",
libsUtil.canFulfillOffer(offerRecord),
offerRecord,
);
return libsUtil.canFulfillOffer(offerRecord);
}
onClickFulfillGiveToOffer(offer: OfferServerRecord) {
const offerRecord: GenericServerRecord = {
...BLANK_GENERIC_SERVER_RECORD,
claim: offer.fullClaim,
issuer: offer.offeredByDid,
};
const giver: GiverInputInfo = {
did: libsUtil.offerGiverDid(offerRecord),
};
(this.$refs.customGiveDialog as GiftedDialog).open(giver, offer.handleId);
}
UNIT_CODES: Record<string, Record<string, string>> = {
BTC: {
name: "Bitcoin",
@ -728,7 +768,7 @@ export default class ProjectViewView extends Vue {
claimType: "GiveAction",
issuer: give.agentDid,
};
return libsUtil.userCanConfirm(giveDetails, this.activeDid);
return libsUtil.giveRecordTheUserCanConfirm(giveDetails, this.activeDid);
}
// similar code is found in ClaimView

Loading…
Cancel
Save