add ability to edit a GiveAction

This commit is contained in:
2024-07-23 20:14:07 -06:00
parent 6456ce8dcc
commit d724d8093c
11 changed files with 327 additions and 102 deletions

View File

@@ -22,6 +22,16 @@
<div class="overflow-hidden">
<h2 class="text-md font-bold">
{{ capitalizeAndInsertSpacesBeforeCaps(veriClaim.claimType) }}
<button
v-if="
veriClaim.claimType === 'GiveAction' &&
veriClaim.issuer === activeDid
"
@click="onClickEditClaim"
title="Edit"
>
<fa icon="pen" class="text-sm text-blue-500 ml-2 mb-1"></fa>
</button>
</h2>
<div class="text-sm">
<div>
@@ -368,6 +378,9 @@
</div>
</div>
</div>
<span v-if="isEditedGlobalId" class="mt-2">
This record is an edited version. The latest version is being shown.
</span>
<!-- 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"
@@ -411,6 +424,7 @@ import { AxiosError } from "axios";
import * as yaml from "js-yaml";
import * as R from "ramda";
import { Component, Vue } from "vue-facing-decorator";
import { Router } from "vue-router";
import { useClipboard } from "@vueuse/core";
import GiftedDialog from "@/components/GiftedDialog.vue";
@@ -422,7 +436,11 @@ import * as serverUtil from "@/libs/endorserServer";
import * as libsUtil from "@/libs/util";
import QuickNav from "@/components/QuickNav.vue";
import { Account } from "@/db/tables/accounts";
import { GiverReceiverInputInfo } from "@/libs/endorserServer";
import {
GenericCredWrapper,
GiverReceiverInputInfo,
OfferVerifiableCredential,
} from "@/libs/endorserServer";
@Component({
components: { GiftedDialog, QuickNav },
@@ -444,6 +462,7 @@ export default class ClaimView extends Vue {
fullClaim = null;
fullClaimDump = "";
fullClaimMessage = "";
isEditedGlobalId = false;
numConfsNotVisible = 0; // number of hidden DIDs in the confirmerIdList, minus the issuer if they aren't visible
showDidCopy = false;
showIdCopy = false;
@@ -466,6 +485,7 @@ export default class ClaimView extends Vue {
this.fullClaim = null;
this.fullClaimDump = "";
this.fullClaimMessage = "";
this.isEditedGlobalId = false;
this.numConfsNotVisible = 0;
this.veriClaim = serverUtil.BLANK_GENERIC_SERVER_RECORD;
this.veriClaimDump = "";
@@ -562,6 +582,8 @@ export default class ClaimView extends Vue {
return;
}
this.isEditedGlobalId = !this.veriClaim.handleId.endsWith(claimId);
// retrieve more details on Give, Offer, or Plan
if (this.veriClaim.claimType === "GiveAction") {
const giveUrl =
@@ -753,7 +775,7 @@ export default class ClaimView extends Vue {
const route = {
path: "/claim/" + encodeURIComponent(claimId),
};
this.$router.push(route).then(async () => {
(this.$router as Router).push(route).then(async () => {
this.resetThisValues();
await this.loadClaim(claimId, this.activeDid);
});
@@ -761,7 +783,9 @@ export default class ClaimView extends Vue {
openFulfillGiftDialog() {
const giver: GiverReceiverInputInfo = {
did: libsUtil.offerGiverDid(this.veriClaim),
did: libsUtil.offerGiverDid(
this.veriClaim as GenericCredWrapper<OfferVerifiableCredential>,
),
};
(this.$refs.customGiveDialog as GiftedDialog).open(
giver,
@@ -794,5 +818,17 @@ export default class ClaimView extends Vue {
url: this.windowLocation,
});
}
onClickEditClaim() {
const route = {
name: "gifted-details",
query: {
prevCredToEdit: JSON.stringify(this.veriClaim),
destinationPathAfter:
"/claim/" + encodeURIComponent(this.veriClaim.handleId),
},
};
(this.$router as Router).push(route);
}
}
</script>

View File

@@ -407,7 +407,12 @@ import { Account } from "@/db/tables/accounts";
import { Contact } from "@/db/tables/contacts";
import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings";
import * as serverUtil from "@/libs/endorserServer";
import { displayAmount, GiverReceiverInputInfo } from "@/libs/endorserServer";
import {
displayAmount,
GenericCredWrapper,
GiverReceiverInputInfo,
OfferVerifiableCredential,
} from "@/libs/endorserServer";
import * as libsUtil from "@/libs/util";
import { isGiveAction } from "@/libs/util";
@@ -767,7 +772,9 @@ export default class ClaimView extends Vue {
openFulfillGiftDialog() {
const giver: GiverReceiverInputInfo = {
did: libsUtil.offerGiverDid(this.veriClaim),
did: libsUtil.offerGiverDid(
this.veriClaim as GenericCredWrapper<OfferVerifiableCredential>,
),
};
(this.$refs.customGiveDialog as GiftedDialog).open(
giver,

View File

@@ -175,6 +175,7 @@
<script lang="ts">
import { Component, Vue } from "vue-facing-decorator";
import { Router } from "vue-router";
import ImageMethodDialog from "@/components/ImageMethodDialog.vue";
import QuickNav from "@/components/QuickNav.vue";
@@ -183,11 +184,14 @@ import { DEFAULT_IMAGE_API_SERVER, NotificationIface } from "@/constants/app";
import { accountsDB, db } from "@/db/index";
import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings";
import {
constructGive,
createAndSubmitGive,
didInfo,
editAndSubmitGive,
GenericCredWrapper,
getHeaders,
getPlanFromCache,
GiveVerifiableCredential,
hydrateGive,
} from "@/libs/endorserServer";
import * as libsUtil from "@/libs/util";
import { Contact } from "@/db/tables/contacts";
@@ -207,7 +211,7 @@ export default class GiftedDetails extends Vue {
amountInput = "0";
description = "";
destinationNameAfter = "";
destinationPathAfter = "";
givenToProject = false;
givenToRecipient = false;
giverDid: string | undefined;
@@ -217,6 +221,7 @@ export default class GiftedDetails extends Vue {
isTrade = false;
message = "";
offerId = "";
prevCredToEdit?: GenericCredWrapper<GiveVerifiableCredential>;
projectId = "";
projectName = "a project";
recipientDid = "";
@@ -226,34 +231,80 @@ export default class GiftedDetails extends Vue {
libsUtil = libsUtil;
async mounted() {
try {
this.prevCredToEdit = (this.$route as Router).query["prevCredToEdit"]
? (JSON.parse(
(this.$route as Router).query["prevCredToEdit"],
) as GenericCredWrapper<GiveVerifiableCredential>)
: undefined;
} catch (error) {
this.$notify(
{
group: "alert",
type: "danger",
title: "Retrieval Error",
text: "The previous record isn't available for editing. If you submit, you'll create a new record.",
},
6000,
);
}
this.amountInput =
(this.$route.query.amountInput as string) || this.amountInput;
this.description = (this.$route.query.description as string) || "";
this.destinationNameAfter = this.$route.query
.destinationNameAfter as string;
this.giverDid = this.$route.query.giverDid as string;
this.$route.query.amountInput ||
String(this.prevCredToEdit?.claim?.object?.amountOfThisGood) ||
this.amountInput;
this.description =
this.$route.query.description ||
this.prevCredToEdit?.claim?.description ||
this.description;
this.destinationPathAfter = this.$route.query.destinationPathAfter;
this.giverDid = (this.$route.query.giverDid ||
this.prevCredToEdit?.claim?.agent?.identifier ||
this.giverDid) as string;
this.giverName = (this.$route.query.giverName as string) || "";
this.hideBackButton = this.$route.query.hideBackButton === "true";
this.message = (this.$route.query.message as string) || "";
this.offerId = this.$route.query.offerId as string;
this.projectId = this.$route.query.projectId as string;
this.recipientDid = this.$route.query.recipientDid as string;
// find any offer ID
const fulfills = this.prevCredToEdit?.claim?.fulfills;
const fulfillsArray = Array.isArray(fulfills)
? fulfills
: fulfills
? [fulfills]
: [];
const offer = fulfillsArray.find((rec) => rec.claimType === "Offer");
this.offerId = (this.$route.query.offerId ||
offer?.identifier ||
this.offerId) as string;
// find any project ID
const project = fulfillsArray.find((rec) => rec.claimType === "PlanAction");
this.projectId = (this.$route.query.projectId ||
project?.identifier ||
this.projectId) as string;
this.recipientDid = (this.$route.query.recipientDid ||
this.prevCredToEdit?.claim?.recipient?.identifier) as string;
this.recipientName = (this.$route.query.recipientName as string) || "";
this.unitCode = (this.$route.query.unitCode as string) || this.unitCode;
this.unitCode = (this.$route.query.unitCode ||
this.prevCredToEdit?.claim?.object?.unitCode ||
this.unitCode) as string;
this.imageUrl =
(this.$route.query.imageUrl as string) ||
this.prevCredToEdit?.claim?.image ||
localStorage.getItem("imageUrl") ||
"";
this.imageUrl;
// this is an endpoint for sharing project info to highlight something given
// https://developer.mozilla.org/en-US/docs/Web/Manifest/share_target
if (this.$route.query.shareTitle) {
this.description = this.$route.query.shareTitle as string;
this.description =
(this.$route.query.shareTitle as string) +
(this.description ? "\n" + this.description : "");
}
if (this.$route.query.shareText) {
this.description =
(this.description ? this.description + " " : "") +
(this.description ? this.description + "\n" : "") +
(this.$route.query.shareText as string);
}
if (this.$route.query.shareUrl) {
@@ -344,16 +395,16 @@ export default class GiftedDetails extends Vue {
cancel() {
this.deleteImage(); // not awaiting, so they'll go back immediately
if (this.destinationNameAfter) {
this.$router.push({ name: this.destinationNameAfter });
if (this.destinationPathAfter) {
(this.$router as Router).push({ path: this.destinationPathAfter });
} else {
this.$router.back();
(this.$router as Router).back();
}
}
cancelBack() {
this.deleteImage(); // not awaiting, so they'll go back immediately
this.$router.back();
(this.$router as Router).back();
}
openImageDialog() {
@@ -548,20 +599,40 @@ export default class GiftedDetails extends Vue {
? this.recipientDid
: undefined;
const projectId = this.givenToProject ? this.projectId : undefined;
const result = await createAndSubmitGive(
this.axios,
this.apiServer,
this.activeDid,
this.giverDid,
recipientDid,
this.description,
parseFloat(this.amountInput),
this.unitCode,
projectId,
this.offerId,
this.isTrade,
this.imageUrl,
);
let result;
if (this.prevCredToEdit) {
// don't create from a blank one in case some properties were set from a different interface
result = await editAndSubmitGive(
this.axios,
this.apiServer,
this.prevCredToEdit,
this.activeDid,
this.giverDid,
recipientDid,
this.description,
parseFloat(this.amountInput),
this.unitCode,
projectId,
this.offerId,
this.isTrade,
this.imageUrl,
);
} else {
result = await createAndSubmitGive(
this.axios,
this.apiServer,
this.activeDid,
this.giverDid,
recipientDid,
this.description,
parseFloat(this.amountInput),
this.unitCode,
projectId,
this.offerId,
this.isTrade,
this.imageUrl,
);
}
if (
result.type === "error" ||
@@ -589,10 +660,10 @@ export default class GiftedDetails extends Vue {
5000,
);
localStorage.removeItem("imageUrl");
if (this.destinationNameAfter) {
this.$router.push({ name: this.destinationNameAfter });
if (this.destinationPathAfter) {
(this.$router as Router).push({ path: this.destinationPathAfter });
} else {
this.$router.back();
(this.$router as Router).back();
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -617,7 +688,8 @@ export default class GiftedDetails extends Vue {
constructGiveParam() {
const recipientDid = this.givenToRecipient ? this.recipientDid : undefined;
const projectId = this.givenToProject ? this.projectId : undefined;
const giveClaim = constructGive(
const giveClaim = hydrateGive(
this.prevCredToEdit?.claim as GiveVerifiableCredential,
this.giverDid,
recipientDid,
this.description,
@@ -627,6 +699,7 @@ export default class GiftedDetails extends Vue {
this.offerId,
this.isTrade,
this.imageUrl,
this.prevCredToEdit?.id as string,
);
const claimStr = JSON.stringify(giveClaim);
return claimStr;

View File

@@ -89,7 +89,8 @@
:to="{ name: 'contact-qr' }"
class="block text-center text-md 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 mt-2 px-2 py-3 rounded-md"
>
Show Them {{ PASSKEYS_ENABLED ? "Default" : "Your" }} Identifier Info
Show Them {{ PASSKEYS_ENABLED ? "Default" : "Your" }} Identifier
Info
</router-link>
<div v-if="PASSKEYS_ENABLED" class="flex justify-end w-full">
<router-link

View File

@@ -74,6 +74,9 @@
v-model="fullClaim.description"
maxlength="5000"
></textarea>
<div class="text-xs text-slate-500 italic -mt-3 mb-4">
If you want to be contacted, be sure to include your contact information.
</div>
<div class="text-xs text-slate-500 italic -mt-3 mb-4">
{{ fullClaim.description?.length }}/5000 max. characters
</div>

View File

@@ -114,7 +114,7 @@ export default class SharedPhotoView extends Vue {
this.$router.push({
name: "gifted-details",
query: {
destinationNameAfter: "home",
destinationPathAfter: "/home",
hideBackButton: true,
imageUrl: url,
recipientDid: this.activeDid,