- {{ message }} {{ giver?.name || "somebody not named" }}
+ {{ customTitle || message + " " + giver?.name || "somebody not named" }}
@@ -42,12 +42,15 @@
name: 'gifted-details',
query: {
amountInput,
+ customTitle,
description,
giverDid: giver?.did,
giverName: giver?.name,
message,
offerId,
projectId,
+ recipientDid: receiver?.did,
+ recipientName: receiver?.name,
unitCode,
},
}"
@@ -90,7 +93,7 @@ import { NotificationIface } from "@/constants/app";
import {
createAndSubmitGive,
didInfo,
- GiverInputInfo,
+ GiverReceiverInputInfo,
} from "@/libs/endorserServer";
import * as libsUtil from "@/libs/util";
import { accountsDB, db } from "@/db/index";
@@ -103,7 +106,6 @@ export default class GiftedDialog extends Vue {
@Prop message = "";
@Prop projectId = "";
- @Prop showGivenToUser = false;
activeDid = "";
allContacts: Array = [];
@@ -111,22 +113,32 @@ export default class GiftedDialog extends Vue {
apiServer = "";
amountInput = "0";
+ callbackOnSuccess?: (amount: number) => void = () => {};
+ customTitle?: string;
description = "";
- givenToUser = false;
- giver?: GiverInputInfo; // undefined means no identified giver agent
+ giver?: GiverReceiverInputInfo; // undefined means no identified giver agent
isTrade = false;
offerId = "";
+ receiver?: GiverReceiverInputInfo;
unitCode = "HUR";
visible = false;
libsUtil = libsUtil;
- async open(giver?: GiverInputInfo, offerId?: string) {
+ async open(
+ giver?: GiverReceiverInputInfo,
+ receiver?: GiverReceiverInputInfo,
+ offerId?: string,
+ customTitle?: string,
+ callbackOnSuccess?: (amount: number) => void,
+ ) {
+ this.customTitle = customTitle;
this.description = "";
- this.giver = giver || {};
+ this.giver = giver;
+ this.receiver = receiver;
// if we show "given to user" selection, default checkbox to true
- this.givenToUser = this.showGivenToUser;
this.amountInput = "0";
+ this.callbackOnSuccess = callbackOnSuccess;
this.offerId = offerId || "";
try {
@@ -141,7 +153,7 @@ export default class GiftedDialog extends Vue {
const allAccounts = await accountsDB.accounts.toArray();
this.allMyDids = allAccounts.map((acc) => acc.did);
- if (!this.giver.name) {
+ if (this.giver && !this.giver.name) {
this.giver.name = didInfo(
this.giver.did,
this.activeDid,
@@ -196,7 +208,6 @@ export default class GiftedDialog extends Vue {
eraseValues() {
this.description = "";
this.giver = undefined;
- this.givenToUser = this.showGivenToUser;
this.amountInput = "0";
this.unitCode = "HUR";
}
@@ -254,6 +265,7 @@ export default class GiftedDialog extends Vue {
// this is asynchronous, but we don't need to wait for it to complete
await this.recordGive(
(this.giver?.did as string) || null,
+ (this.receiver?.did as string) || null,
this.description,
parseFloat(this.amountInput),
this.unitCode,
@@ -265,14 +277,16 @@ export default class GiftedDialog extends Vue {
/**
*
* @param giverDid may be null
+ * @param recipientDid may be null
* @param description may be an empty string
- * @param amountInput may be 0
+ * @param amount may be 0
* @param unitCode may be omitted, defaults to "HUR"
*/
- public async recordGive(
+ async recordGive(
giverDid: string | null,
+ recipientDid: string | null,
description: string,
- amountInput: number,
+ amount: number,
unitCode: string = "HUR",
) {
try {
@@ -282,9 +296,9 @@ export default class GiftedDialog extends Vue {
this.apiServer,
identity,
giverDid,
- this.givenToUser ? this.activeDid : undefined,
+ this.receiver?.did as string,
description,
- amountInput,
+ amount,
unitCode,
this.projectId,
this.offerId,
@@ -316,6 +330,9 @@ export default class GiftedDialog extends Vue {
},
7000,
);
+ if (this.callbackOnSuccess) {
+ this.callbackOnSuccess(amount);
+ }
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
diff --git a/src/components/OfferDialog.vue b/src/components/OfferDialog.vue
index 980a61f11..e04888894 100644
--- a/src/components/OfferDialog.vue
+++ b/src/components/OfferDialog.vue
@@ -43,7 +43,7 @@
@@ -69,6 +69,7 @@
diff --git a/src/views/ContactQRScanShowView.vue b/src/views/ContactQRScanShowView.vue
index 86010b2ad..f233d7766 100644
--- a/src/views/ContactQRScanShowView.vue
+++ b/src/views/ContactQRScanShowView.vue
@@ -188,7 +188,6 @@ export default class ContactQRScanShow extends Vue {
if (url) {
try {
const fullData = getContactPayloadFromJwtUrl(url);
- console.log("fullData", fullData);
localStorage.setItem("contactEndorserUrl", url);
this.$router.push({ name: "contacts" });
} catch (e) {
diff --git a/src/views/ContactsView.vue b/src/views/ContactsView.vue
index aecd23393..511d22de4 100644
--- a/src/views/ContactsView.vue
+++ b/src/views/ContactsView.vue
@@ -43,6 +43,7 @@
+
+ In the following, only the most recent hours are included. To see more,
+ click
+
+
+
{{
showGiveTotals
- ? "Total"
+ ? "Showing Total"
: showGiveConfirmed
? "Confirmed"
: "Unconfirmed"
}}
-
- (Only most recent hours included. To see more, click
-
-
-
- )
@@ -189,10 +189,11 @@
>
To:
+
{{
/* eslint-disable prettier/prettier */
this.showGiveTotals
@@ -203,15 +204,17 @@
: (givenByMeUnconfirmed[contact.did] || 0)
/* eslint-enable prettier/prettier */
}}
+
From:
+
{{
/* eslint-disable prettier/prettier */
this.showGiveTotals
@@ -222,6 +225,7 @@
: (givenToMeUnconfirmed[contact.did] || 0)
/* eslint-enable prettier/prettier */
}}
+
@@ -237,7 +241,7 @@
name: 'contact-amounts',
query: { contactDid: contact.did },
}"
- class="text-sm 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-1.5 rounded-md"
+ class="text-sm 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-1.5 rounded-md border border-slate-400"
title="See more given activity"
>
@@ -249,6 +253,7 @@
There are no contacts.
+
@@ -313,8 +318,8 @@ import {
import {
CONTACT_CSV_HEADER,
CONTACT_URL_PREFIX,
+ GiverReceiverInputInfo,
GiveSummaryRecord,
- GiveVerifiableCredential,
isDid,
RegisterVerifiableCredential,
SERVICE_ID,
@@ -322,13 +327,14 @@ import {
import * as libsUtil from "@/libs/util";
import QuickNav from "@/components/QuickNav.vue";
import EntityIcon from "@/components/EntityIcon.vue";
+import GiftedDialog from "@/components/GiftedDialog.vue";
import OfferDialog from "@/components/OfferDialog.vue";
import { Account } from "@/db/tables/accounts";
import { Buffer } from "buffer/";
@Component({
- components: { EntityIcon, OfferDialog, QuickNav },
+ components: { GiftedDialog, EntityIcon, OfferDialog, QuickNav },
})
export default class ContactsView extends Vue {
$notify!: (notification: NotificationIface, timeout?: number) => void;
@@ -352,8 +358,6 @@ export default class ContactsView extends Vue {
givenToMeConfirmed: Record
= {};
// { "did:...": amount } entry for each contact
givenToMeUnconfirmed: Record = {};
- hourDescriptionInput = "";
- hourInput = "0";
isRegistered = false;
showDidCopy = false;
showGiveNumbers = false;
@@ -1041,27 +1045,33 @@ export default class ContactsView extends Vue {
}
private nameForDid(contacts: Array, did: string): string {
+ if (did === this.activeDid) {
+ return "you";
+ }
const contact = R.find((con) => con.did == did, contacts);
return this.nameForContact(contact);
}
private nameForContact(contact?: Contact, capitalize?: boolean): string {
- return contact?.name || (capitalize ? "T" : "t") + "his unnamed user";
+ return (
+ (contact?.name as string) || (capitalize ? "T" : "t") + "his unnamed user"
+ );
}
- async onClickAddGive(fromDid: string, toDid: string): Promise {
- const identity = await this.getIdentity(this.activeDid);
-
+ private showGiftedDialog(giverDid: string, recipientDid: string) {
// if they have unconfirmed amounts, ask to confirm those first
- if (toDid == identity?.did && this.givenToMeUnconfirmed[fromDid] > 0) {
- const isare = this.givenToMeUnconfirmed[fromDid] == 1 ? "is" : "are";
- const hours = this.givenToMeUnconfirmed[fromDid] == 1 ? "hour" : "hours";
+ if (
+ recipientDid == this.activeDid &&
+ this.givenToMeUnconfirmed[giverDid] > 0
+ ) {
+ const isAre = this.givenToMeUnconfirmed[giverDid] == 1 ? "is" : "are";
+ const hours = this.givenToMeUnconfirmed[giverDid] == 1 ? "hour" : "hours";
if (
confirm(
"There " +
- isare +
+ isAre +
" " +
- this.givenToMeUnconfirmed[fromDid] +
+ this.givenToMeUnconfirmed[giverDid] +
" unconfirmed " +
hours +
" from them." +
@@ -1070,178 +1080,58 @@ export default class ContactsView extends Vue {
) {
this.$router.push({
name: "contact-amounts",
- query: { contactDid: fromDid },
+ query: { contactDid: giverDid },
});
return;
}
}
- if (!libsUtil.isNumeric(this.hourInput)) {
- this.$notify(
- {
- group: "alert",
- type: "danger",
- title: "Input Error",
- text: "This is not a valid number of hours: " + this.hourInput,
- },
- 3000,
- );
- } else if (parseFloat(this.hourInput) == 0 && !this.hourDescriptionInput) {
- this.$notify(
- {
- group: "alert",
- type: "danger",
- title: "Input Error",
- text: "Giving no hours or description does nothing.",
- },
- 3000,
- );
- } else if (!identity) {
- this.$notify(
- {
- group: "alert",
- type: "danger",
- title: "Status Error",
- text: "No identifier is available.",
- },
- 3000,
- );
+
+ let giver: GiverReceiverInputInfo, receiver: GiverReceiverInputInfo;
+ if (giverDid) {
+ giver = {
+ did: giverDid,
+ name: this.nameForDid(this.contacts, giverDid),
+ };
+ }
+ if (recipientDid) {
+ receiver = {
+ did: recipientDid,
+ name: this.nameForDid(this.contacts, recipientDid),
+ };
+ }
+
+ let callback: (amount: number) => void;
+ let customTitle = "Given";
+ // choose whether to open dialog to user or from user
+ if (giverDid == this.activeDid) {
+ callback = (amount: number) => {
+ const newList = R.clone(this.givenByMeUnconfirmed);
+ newList[recipientDid] = (newList[recipientDid] || 0) + amount;
+ this.givenByMeUnconfirmed = newList;
+ };
+ customTitle = "To " + receiver.name;
} else {
- // ask to confirm amount
- let toFrom;
- if (fromDid == identity?.did) {
- toFrom = "from you to " + this.nameForDid(this.contacts, toDid);
- } else {
- toFrom = "from " + this.nameForDid(this.contacts, fromDid) + " to you";
- }
- let description;
- if (this.hourDescriptionInput) {
- description = " with description '" + this.hourDescriptionInput + "'";
- } else {
- description = " with no description";
- }
- if (
- confirm(
- "Are you sure you want to record " +
- this.hourInput +
- " hour" +
- (this.hourInput == "1" ? "" : "s") +
- " " +
- toFrom +
- description +
- "?",
- )
- ) {
- this.createAndSubmitContactGive(
- identity,
- fromDid,
- toDid,
- parseFloat(this.hourInput),
- this.hourDescriptionInput,
- );
- }
+ // must be (recipientDid == this.activeDid)
+ callback = (amount: number) => {
+ const newList = R.clone(this.givenToMeUnconfirmed);
+ newList[giverDid] = (newList[giverDid] || 0) + amount;
+ this.givenToMeUnconfirmed = newList;
+ };
+ customTitle = "From " + giver.name;
}
+ (this.$refs.customGivenDialog as GiftedDialog).open(
+ giver,
+ receiver,
+ undefined as string,
+ customTitle,
+ callback,
+ );
}
openOfferDialog(recipientDid: string) {
(this.$refs.customOfferDialog as OfferDialog).open(recipientDid);
}
- // similar function is in endorserServer.ts
- private async createAndSubmitContactGive(
- identity: IIdentifier,
- fromDid: string,
- toDid: string,
- amount: number,
- description: string,
- ): Promise {
- // Make a claim
- const vcClaim: GiveVerifiableCredential = {
- "@context": "https://schema.org",
- "@type": "GiveAction",
- agent: { identifier: fromDid },
- object: { amountOfThisGood: amount, unitCode: "HUR" },
- recipient: { identifier: toDid },
- };
- if (description) {
- vcClaim.description = description;
- }
- // Make a payload for the claim
- const vcPayload = {
- vc: {
- "@context": ["https://www.w3.org/2018/credentials/v1"],
- type: ["VerifiableCredential"],
- credentialSubject: vcClaim,
- },
- };
- // Create a signature using private key of identity
- if (identity.keys[0].privateKeyHex !== null) {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- const privateKeyHex: string = identity.keys[0].privateKeyHex!;
- const signer = await SimpleSigner(privateKeyHex);
- const alg = undefined;
- // Create a JWT for the request
- const vcJwt: string = await didJwt.createJWT(vcPayload, {
- alg: alg,
- issuer: identity.did,
- signer: signer,
- });
-
- // Make the xhr request payload
-
- const payload = JSON.stringify({ jwtEncoded: vcJwt });
- const url = this.apiServer + "/api/v2/claim";
- const headers = await this.getHeaders(identity);
-
- try {
- const resp = await this.axios.post(url, payload, { headers });
- if (resp.data?.success?.handleId) {
- this.$notify(
- {
- group: "alert",
- type: "success",
- title: "Done",
- text: "Successfully logged time to the server.",
- },
- 5000,
- );
-
- if (fromDid === identity.did) {
- const newList = R.clone(this.givenByMeUnconfirmed);
- newList[toDid] = (newList[toDid] || 0) + amount;
- this.givenByMeUnconfirmed = newList;
- } else {
- const newList = R.clone(this.givenToMeConfirmed);
- newList[fromDid] = (newList[fromDid] || 0) + amount;
- this.givenToMeConfirmed = newList;
- }
- }
- } catch (error) {
- console.error("Error in createAndSubmitContactGive: ", error);
- let userMessage = "There was an error. See logs for more info.";
- const serverError = error as AxiosError;
- if (serverError) {
- if (serverError.message) {
- userMessage = serverError.message; // Info for the user
- } else {
- userMessage = JSON.stringify(serverError.toJSON());
- }
- } else {
- userMessage = error as string;
- }
- // Now set that error for the user to see.
- this.$notify(
- {
- group: "alert",
- type: "danger",
- title: "Error Sending Give",
- text: userMessage,
- },
- 5000,
- );
- }
- }
- }
-
private async onClickCancelName() {
this.contactEdit = null;
this.contactNewName = "";
diff --git a/src/views/GiftedDetails.vue b/src/views/GiftedDetails.vue
index 5b911aefe..bf31a79dd 100644
--- a/src/views/GiftedDetails.vue
+++ b/src/views/GiftedDetails.vue
@@ -18,8 +18,12 @@
What Was Given
- {{ message }} {{ giverName || "somebody not named" }}
+ {{ customTitle || message + " " + giverName || "somebody not named" }}
+
+ From {{ giverName || "somebody not named" }}
+ to {{ recipientName || "somebody not named" }}
+
-
+
Given to you
@@ -126,12 +130,10 @@ import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings";
import { createAndSubmitGive } from "@/libs/endorserServer";
import * as libsUtil from "@/libs/util";
import { accessToken } from "@/libs/crypto";
-import GiftedDialog from "@/components/GiftedDialog.vue";
import GiftedPhotoDialog from "@/components/GiftedPhotoDialog.vue";
@Component({
components: {
- GiftedDialog,
GiftedPhotoDialog,
QuickNav,
TopMessage,
@@ -144,6 +146,7 @@ export default class GiftedDetails extends Vue {
apiServer = "";
amountInput = "0";
+ customTitle = "";
description = "";
givenToUser = false;
giverDid: string | undefined;
@@ -153,30 +156,46 @@ export default class GiftedDetails extends Vue {
message = "";
offerId = "";
projectId = "";
+ recipientDid = "";
+ recipientName = "";
+ showGivenToUser = false;
unitCode = "HUR";
libsUtil = libsUtil;
async mounted() {
this.amountInput = this.$route.query.amountInput as string;
+ this.customTitle = this.$route.query.customTitle as string;
this.description = this.$route.query.description as string;
this.giverDid = this.$route.query.giverDid as string;
this.giverName = this.$route.query.giverName as string;
+ if (this.giverDid && !this.giverName) {
+ this.giverName =
+ this.giverDid === this.activeDid ? "you" : "someone not named";
+ }
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;
+ this.recipientName = this.$route.query.recipientName as string;
+ if (this.recipientDid && !this.recipientName) {
+ this.recipientName =
+ this.recipientDid === this.activeDid ? "you" : "someone not named";
+ }
this.unitCode = this.$route.query.unitCode as string;
this.imageUrl = localStorage.getItem("imageUrl") || "";
- this.givenToUser = !this.projectId;
-
try {
await db.open();
const settings = (await db.settings.get(MASTER_SETTINGS_KEY)) as Settings;
this.apiServer = settings?.apiServer || "";
this.activeDid = settings?.activeDid || "";
+ this.givenToUser = this.recipientDid === this.activeDid;
+ this.showGivenToUser =
+ !this.projectId && this.recipientDid === this.activeDid;
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (err: any) {
console.error("Error retrieving settings from database:", err);
@@ -358,12 +377,18 @@ export default class GiftedDetails extends Vue {
public async recordGive() {
try {
const identity = await libsUtil.getIdentity(this.activeDid);
+ const recipientDid =
+ this.recipientDid === this.activeDid
+ ? this.givenToUser
+ ? this.activeDid
+ : undefined
+ : this.recipientDid;
const result = await createAndSubmitGive(
this.axios,
this.apiServer,
identity,
this.giverDid,
- this.givenToUser ? this.activeDid : undefined,
+ recipientDid,
this.description,
parseFloat(this.amountInput),
this.unitCode,
diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue
index 2eba7c87e..b6abb377b 100644
--- a/src/views/HomeView.vue
+++ b/src/views/HomeView.vue
@@ -154,13 +154,13 @@
Choose From All Contacts
Ideas...
@@ -168,11 +168,7 @@