diff --git a/project.task.yaml b/project.task.yaml index c4863b1..49db271 100644 --- a/project.task.yaml +++ b/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 diff --git a/src/components/GiftedDialog.vue b/src/components/GiftedDialog.vue index 5131782..633ff46 100644 --- a/src/components/GiftedDialog.vue +++ b/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 = { - "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; } diff --git a/src/libs/endorserServer.ts b/src/libs/endorserServer.ts index 117ca7c..ec4a926 100644 --- a/src/libs/endorserServer.ts +++ b/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; diff --git a/src/libs/util.ts b/src/libs/util.ts index 61f49ce..2892c24 100644 --- a/src/libs/util.ts +++ b/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} with the DID of the new identity diff --git a/src/main.ts b/src/main.ts index 3890eb7..075126f 100644 --- a/src/main.ts +++ b/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, diff --git a/src/views/ClaimView.vue b/src/views/ClaimView.vue index b65294d..cd338e2 100644 --- a/src/views/ClaimView.vue +++ b/src/views/ClaimView.vue @@ -47,27 +47,31 @@
- + -
+

Confirmations

Nobody has confirmed this. @@ -148,7 +152,7 @@ You cannot confirm this because you issued this claim, so you already count as confirming it.
-
+
You cannot confirm this because it contains hidden identifiers.
@@ -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, + ); } } diff --git a/src/views/ProjectViewView.vue b/src/views/ProjectViewView.vue index beab62d..3d31bfd 100644 --- a/src/views/ProjectViewView.vue +++ b/src/views/ProjectViewView.vue @@ -123,7 +123,7 @@
  • @@ -198,12 +198,24 @@
- + {{ offer.objectDescription }}
@@ -243,15 +255,15 @@
- + {{ give.description }}
@@ -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> = { 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