diff --git a/src/components/OnboardingDialog.vue b/src/components/OnboardingDialog.vue
index abea4ec..053aa95 100644
--- a/src/components/OnboardingDialog.vue
+++ b/src/components/OnboardingDialog.vue
@@ -5,7 +5,7 @@
Welcome to Time Safari
- - Showcasing Gratitude & Magnifing Time
+ - Showcasing Gratitude & Magnifying Time
50
@@ -164,12 +192,12 @@ export default class ClaimCertificateView extends Vue {
);
}
- if (
- claimData.claim.object?.amountOfThisGood &&
- claimData.claim.object?.unitCode
- ) {
- const amount = claimData.claim.object.amountOfThisGood;
- const unit = claimData.claim.object.unitCode;
+ const possibleObject =
+ claimData.claim.object || // for GiveActions
+ claimData.claim.includesObject; // for Offers
+ if (possibleObject?.amountOfThisGood && possibleObject?.unitCode) {
+ const amount = possibleObject.amountOfThisGood;
+ const unit = possibleObject.unitCode;
const amountText = serverUtil.displayAmount(unit, amount);
const amountWidth = ctx.measureText(amountText).width;
// if there was no description then put this in that spot, otherwise put it below the description
diff --git a/src/views/NewEditProjectView.vue b/src/views/NewEditProjectView.vue
index 04d7d09..2f15508 100644
--- a/src/views/NewEditProjectView.vue
+++ b/src/views/NewEditProjectView.vue
@@ -203,8 +203,12 @@ import "leaflet/dist/leaflet.css";
import { AxiosError, AxiosRequestHeaders } from "axios";
import { DateTime } from "luxon";
import { hexToBytes } from "@noble/hashes/utils";
-import type { EventTemplate, VerifiedEvent } from "nostr-tools/lib/types/core";
-import { accountFromSeedWords } from "nostr-tools/nip06";
+// these core imports could also be included as "import type ..."
+import { EventTemplate, UnsignedEvent, VerifiedEvent } from "nostr-tools/core";
+import {
+ accountFromExtendedKey,
+ extendedKeysFromSeedWords,
+} from "nostr-tools/nip06";
import { finalizeEvent, serializeEvent } from "nostr-tools/pure";
import { Component, Vue } from "vue-facing-decorator";
import { LMap, LMarker, LTileLayer } from "@vue-leaflet/vue-leaflet";
@@ -225,7 +229,6 @@ import {
} from "@/libs/endorserServer";
import {
retrieveAccountCount,
- retrieveAccountMetadata,
retrieveFullyDecryptedAccount,
} from "@/libs/util";
@@ -472,31 +475,45 @@ export default class NewEditProjectView extends Vue {
try {
const resp = await this.axios.post(url, payload, { headers });
if (resp.data?.success?.handleId) {
+ this.$notify(
+ {
+ group: "alert",
+ type: "success",
+ title: "Saved",
+ text: "The project was saved successfully.",
+ },
+ 3000,
+ );
+
this.errorMessage = "";
const projectPath = encodeURIComponent(resp.data.success.handleId);
if (this.sendToTrustroots || this.sendToTripHopping) {
if (this.latitude && this.longitude) {
- let signedPayload: VerifiedEvent | undefined; // sign something to prove ownership of pubkey
+ let payloadAndKey; // sign something to prove ownership of pubkey
if (this.sendToTrustroots) {
- signedPayload = await this.signPayload();
+ payloadAndKey = await this.signSomePayload();
+ // not going to await... the save was successful, so we'll continue to the next page
this.sendToNostrPartner(
"NOSTR-EVENT-TRUSTROOTS",
"Trustroots",
resp.data.success.claimId,
- signedPayload,
+ payloadAndKey.signedEvent,
+ payloadAndKey.publicExtendedKey,
);
}
if (this.sendToTripHopping) {
- if (!signedPayload) {
- signedPayload = await this.signPayload();
+ if (!payloadAndKey) {
+ payloadAndKey = await this.signSomePayload();
}
+ // not going to await... the save was successful, so we'll continue to the next page
this.sendToNostrPartner(
"NOSTR-EVENT-TRIPHOPPING",
"TripHopping",
resp.data.success.claimId,
- signedPayload,
+ payloadAndKey.signedEvent,
+ payloadAndKey.publicExtendedKey,
);
}
} else {
@@ -576,19 +593,28 @@ export default class NewEditProjectView extends Vue {
}
}
- private async signPayload(): Promise {
+ /**
+ * @return a signed payload and an extended public key for later transmission
+ */
+ private async signSomePayload(): Promise<{
+ signedEvent: VerifiedEvent;
+ publicExtendedKey: string;
+ }> {
const account = await retrieveFullyDecryptedAccount(this.activeDid);
// get the last number of the derivationPath
const finalDerNum = account?.derivationPath?.split?.("/")?.reverse()[0];
// remove any trailing '
const finalDerNumNoApostrophe = finalDerNum?.replace(/'/g, "");
const accountNum = Number(finalDerNumNoApostrophe || 0);
- const pubPri = accountFromSeedWords(
+ const extPubPri = extendedKeysFromSeedWords(
account?.mnemonic as string,
"",
accountNum,
);
- const privateBytes = hexToBytes(pubPri?.privateKey);
+ const publicExtendedKey: string = extPubPri?.publicExtendedKey;
+ const privateExtendedKey = extPubPri?.privateExtendedKey;
+ const privateKey = accountFromExtendedKey(privateExtendedKey).privateKey;
+ const privateBytes = hexToBytes(privateKey);
// No real content is necessary, we just want something signed,
// so we might as well use nostr libs for nostr functions.
// Besides: someday we may create real content that we can relay.
@@ -598,9 +624,12 @@ export default class NewEditProjectView extends Vue {
content: "",
created_at: 0,
};
- // Why does IntelliJ not see matching types?
- const signedEvent = finalizeEvent(event, privateBytes);
- return signedEvent;
+ const signedEvent: VerifiedEvent = finalizeEvent(
+ // Why does IntelliJ not see matching types?
+ event as EventTemplate,
+ privateBytes,
+ ) as VerifiedEvent;
+ return { signedEvent, publicExtendedKey };
}
private async sendToNostrPartner(
@@ -608,41 +637,37 @@ export default class NewEditProjectView extends Vue {
serviceName: string,
jwtId: string,
signedPayload: VerifiedEvent,
+ publicExtendedKey: string,
) {
- // first, get the public key for nostr
- const account = await retrieveAccountMetadata(this.activeDid);
- // get the last number of the derivationPath
- const finalDerNum = account?.derivationPath?.split?.("/")?.reverse()[0];
- // remove any trailing '
- const finalDerNumNoApostrophe = finalDerNum?.replace(/'/g, "");
- const accountNum = Number(finalDerNumNoApostrophe || 0);
- const pubPri = accountFromSeedWords(
- account?.mnemonic as string,
- "",
- accountNum,
- );
- const nostrPubKey = pubPri?.publicKey;
-
- let partnerServer = DEFAULT_PARTNER_API_SERVER;
- const settings = await retrieveSettingsForActiveAccount();
- if (settings.partnerApiServer) {
- partnerServer = settings.partnerApiServer;
- }
- const endorserPartnerUrl = partnerServer + "/api/partner/link";
- const timeSafariUrl = window.location.origin + "/claim/" + jwtId;
- const content = this.fullClaim.name + " - see " + timeSafariUrl;
- // Why does IntelliJ not see matching types?
- const payload = serializeEvent(signedPayload);
- const partnerParams = {
- jwtId: jwtId,
- linkCode: linkCode,
- inputJson: JSON.stringify(content),
- pubKeyHex: nostrPubKey,
- pubKeyImage: payload,
- pubKeySigHex: signedPayload.sig,
- };
- const headers = await getHeaders(this.activeDid);
try {
+ let partnerServer = DEFAULT_PARTNER_API_SERVER;
+ const settings = await retrieveSettingsForActiveAccount();
+ if (settings.partnerApiServer) {
+ partnerServer = settings.partnerApiServer;
+ }
+ const endorserPartnerUrl = partnerServer + "/api/partner/link";
+ const timeSafariUrl = window.location.origin + "/claim/" + jwtId;
+ const content = this.fullClaim.name + " - see " + timeSafariUrl;
+ const publicKeyHex = accountFromExtendedKey(publicExtendedKey).publicKey;
+ const unsignedPayload: UnsignedEvent = {
+ // why doesn't "...signedPayload" work?
+ kind: signedPayload.kind,
+ tags: signedPayload.tags,
+ content: signedPayload.content,
+ created_at: signedPayload.created_at,
+ pubkey: publicKeyHex,
+ };
+ // Why does IntelliJ not see matching types?
+ const payload = serializeEvent(unsignedPayload as UnsignedEvent);
+ const partnerParams = {
+ jwtId: jwtId,
+ linkCode: linkCode,
+ inputJson: JSON.stringify(content),
+ pubKeyHex: publicKeyHex,
+ pubKeyImage: payload,
+ pubKeySigHex: signedPayload.sig,
+ };
+ const headers = await getHeaders(this.activeDid);
const linkResp = await this.axios.post(
endorserPartnerUrl,
partnerParams,
@@ -731,7 +756,7 @@ export default class NewEditProjectView extends Vue {
group: "alert",
type: "info",
title: "About Nostr Events",
- text: "This will cause a submission to a partner on the nostr network. It will contain your public key data which may allow correlation, so don't allow this if you're not comfortable with that.",
+ text: "This will submit this project to a partner on the nostr network. It will contain your public key data which may allow correlation, so don't allow this if you're not comfortable with that.",
},
7000,
);