|
@ -145,6 +145,30 @@ |
|
|
</l-map> |
|
|
</l-map> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div |
|
|
|
|
|
v-if="showGeneralAdvanced && includeLocation && false" |
|
|
|
|
|
class="items-center mb-4" |
|
|
|
|
|
> |
|
|
|
|
|
<div class="flex"> |
|
|
|
|
|
<input |
|
|
|
|
|
type="checkbox" |
|
|
|
|
|
class="mr-2" |
|
|
|
|
|
v-model="sendToTrustroots" |
|
|
|
|
|
@click="sendToTrustroots = !sendToTrustroots" |
|
|
|
|
|
/> |
|
|
|
|
|
<label>Send to Trustroots</label> |
|
|
|
|
|
</div> |
|
|
|
|
|
<div class="flex"> |
|
|
|
|
|
<input |
|
|
|
|
|
type="checkbox" |
|
|
|
|
|
class="mr-2" |
|
|
|
|
|
v-model="sendToTripHopping" |
|
|
|
|
|
@click="sendToTripHopping = !sendToTripHopping" |
|
|
|
|
|
/> |
|
|
|
|
|
<label>Send to TripHopping</label> |
|
|
|
|
|
</div> |
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="mt-8"> |
|
|
<div class="mt-8"> |
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2"> |
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2"> |
|
|
<button |
|
|
<button |
|
@ -178,19 +202,25 @@ |
|
|
import "leaflet/dist/leaflet.css"; |
|
|
import "leaflet/dist/leaflet.css"; |
|
|
import { AxiosError, AxiosRequestHeaders } from "axios"; |
|
|
import { AxiosError, AxiosRequestHeaders } from "axios"; |
|
|
import { DateTime } from "luxon"; |
|
|
import { DateTime } from "luxon"; |
|
|
|
|
|
import { accountFromSeedWords } from "nostr-tools/nip06"; |
|
|
import { Component, Vue } from "vue-facing-decorator"; |
|
|
import { Component, Vue } from "vue-facing-decorator"; |
|
|
import { LMap, LMarker, LTileLayer } from "@vue-leaflet/vue-leaflet"; |
|
|
import { LMap, LMarker, LTileLayer } from "@vue-leaflet/vue-leaflet"; |
|
|
import { Router } from "vue-router"; |
|
|
import { Router } from "vue-router"; |
|
|
|
|
|
|
|
|
import ImageMethodDialog from "@/components/ImageMethodDialog.vue"; |
|
|
import ImageMethodDialog from "@/components/ImageMethodDialog.vue"; |
|
|
import QuickNav from "@/components/QuickNav.vue"; |
|
|
import QuickNav from "@/components/QuickNav.vue"; |
|
|
import { DEFAULT_IMAGE_API_SERVER, NotificationIface } from "@/constants/app"; |
|
|
import { |
|
|
|
|
|
DEFAULT_IMAGE_API_SERVER, |
|
|
|
|
|
DEFAULT_PARTNER_API_SERVER, |
|
|
|
|
|
NotificationIface, |
|
|
|
|
|
} from "@/constants/app"; |
|
|
import { accountsDB, retrieveSettingsForActiveAccount } from "@/db/index"; |
|
|
import { accountsDB, retrieveSettingsForActiveAccount } from "@/db/index"; |
|
|
import { |
|
|
import { |
|
|
createEndorserJwtVcFromClaim, |
|
|
createEndorserJwtVcFromClaim, |
|
|
getHeaders, |
|
|
getHeaders, |
|
|
PlanVerifiableCredential, |
|
|
PlanVerifiableCredential, |
|
|
} from "@/libs/endorserServer"; |
|
|
} from "@/libs/endorserServer"; |
|
|
|
|
|
import { getAccount } from "@/libs/util"; |
|
|
|
|
|
|
|
|
@Component({ |
|
|
@Component({ |
|
|
components: { ImageMethodDialog, LMap, LMarker, LTileLayer, QuickNav }, |
|
|
components: { ImageMethodDialog, LMap, LMarker, LTileLayer, QuickNav }, |
|
@ -224,6 +254,9 @@ export default class NewEditProjectView extends Vue { |
|
|
numAccounts = 0; |
|
|
numAccounts = 0; |
|
|
projectId = ""; |
|
|
projectId = ""; |
|
|
projectIssuerDid = ""; |
|
|
projectIssuerDid = ""; |
|
|
|
|
|
sendToTrustroots = false; |
|
|
|
|
|
sendToTripHopping = false; |
|
|
|
|
|
showGeneralAdvanced = false; |
|
|
startDateInput?: string; |
|
|
startDateInput?: string; |
|
|
startTimeInput?: string; |
|
|
startTimeInput?: string; |
|
|
zoneName = DateTime.local().zoneName; |
|
|
zoneName = DateTime.local().zoneName; |
|
@ -236,6 +269,7 @@ export default class NewEditProjectView extends Vue { |
|
|
const settings = await retrieveSettingsForActiveAccount(); |
|
|
const settings = await retrieveSettingsForActiveAccount(); |
|
|
this.activeDid = settings.activeDid || ""; |
|
|
this.activeDid = settings.activeDid || ""; |
|
|
this.apiServer = settings.apiServer || ""; |
|
|
this.apiServer = settings.apiServer || ""; |
|
|
|
|
|
this.showGeneralAdvanced = !!settings.showGeneralAdvanced; |
|
|
|
|
|
|
|
|
this.projectId = (this.$route as Router).query["projectId"] || ""; |
|
|
this.projectId = (this.$route as Router).query["projectId"] || ""; |
|
|
|
|
|
|
|
@ -356,7 +390,7 @@ export default class NewEditProjectView extends Vue { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private async saveProject(issuerDid: string) { |
|
|
private async saveProject() { |
|
|
// Make a claim |
|
|
// Make a claim |
|
|
const vcClaim: PlanVerifiableCredential = this.fullClaim; |
|
|
const vcClaim: PlanVerifiableCredential = this.fullClaim; |
|
|
if (this.projectId) { |
|
|
if (this.projectId) { |
|
@ -407,13 +441,13 @@ export default class NewEditProjectView extends Vue { |
|
|
} else { |
|
|
} else { |
|
|
delete vcClaim.startTime; |
|
|
delete vcClaim.startTime; |
|
|
} |
|
|
} |
|
|
const vcJwt = await createEndorserJwtVcFromClaim(issuerDid, vcClaim); |
|
|
const vcJwt = await createEndorserJwtVcFromClaim(this.activeDid, vcClaim); |
|
|
|
|
|
|
|
|
// Make the xhr request payload |
|
|
// Make the xhr request payload |
|
|
|
|
|
|
|
|
const payload = JSON.stringify({ jwtEncoded: vcJwt }); |
|
|
const payload = JSON.stringify({ jwtEncoded: vcJwt }); |
|
|
const url = this.apiServer + "/api/v2/claim"; |
|
|
const url = this.apiServer + "/api/v2/claim"; |
|
|
const headers = await getHeaders(issuerDid); |
|
|
const headers = await getHeaders(this.activeDid); |
|
|
|
|
|
|
|
|
try { |
|
|
try { |
|
|
const resp = await this.axios.post(url, payload, { headers }); |
|
|
const resp = await this.axios.post(url, payload, { headers }); |
|
@ -421,6 +455,22 @@ export default class NewEditProjectView extends Vue { |
|
|
this.errorMessage = ""; |
|
|
this.errorMessage = ""; |
|
|
|
|
|
|
|
|
const projectPath = encodeURIComponent(resp.data.success.handleId); |
|
|
const projectPath = encodeURIComponent(resp.data.success.handleId); |
|
|
|
|
|
|
|
|
|
|
|
if (this.sendToTrustroots) { |
|
|
|
|
|
this.sendToNostrPartner( |
|
|
|
|
|
"NOSTR-EVENT-TRUSTROOTS", |
|
|
|
|
|
"Trustroots", |
|
|
|
|
|
resp.data.success.claimId, |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
if (this.sendToTripHopping) { |
|
|
|
|
|
this.sendToNostrPartner( |
|
|
|
|
|
"NOSTR-EVENT-TRIPHOPPING", |
|
|
|
|
|
"TripHopping", |
|
|
|
|
|
resp.data.success.claimId, |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
(this.$router as Router).push({ path: "/project/" + projectPath }); |
|
|
(this.$router as Router).push({ path: "/project/" + projectPath }); |
|
|
} else { |
|
|
} else { |
|
|
console.error( |
|
|
console.error( |
|
@ -485,6 +535,82 @@ export default class NewEditProjectView extends Vue { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private async sendToNostrPartner( |
|
|
|
|
|
linkCode: string, |
|
|
|
|
|
serviceName: string, |
|
|
|
|
|
jwtId: string, |
|
|
|
|
|
) { |
|
|
|
|
|
// first, get the public key for nostr |
|
|
|
|
|
const account = await getAccount(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; |
|
|
|
|
|
|
|
|
|
|
|
const trustrootsUrl = DEFAULT_PARTNER_API_SERVER + "/api/partner/link"; |
|
|
|
|
|
const timeSafariUrl = window.location.origin + "/claim/" + jwtId; |
|
|
|
|
|
const content = this.fullClaim.name + " - see " + timeSafariUrl; |
|
|
|
|
|
const trustrootsParams = { |
|
|
|
|
|
jwtId: jwtId, |
|
|
|
|
|
linkCode: linkCode, |
|
|
|
|
|
inputJson: JSON.stringify(content), |
|
|
|
|
|
nostrPubKeyHex: nostrPubKey, |
|
|
|
|
|
}; |
|
|
|
|
|
const fullTrustrootsUrl = trustrootsUrl; |
|
|
|
|
|
const headers = await getHeaders(this.activeDid); |
|
|
|
|
|
try { |
|
|
|
|
|
const linkResp = await this.axios.post( |
|
|
|
|
|
fullTrustrootsUrl, |
|
|
|
|
|
trustrootsParams, |
|
|
|
|
|
{ headers }, |
|
|
|
|
|
); |
|
|
|
|
|
if (linkResp.status === 201) { |
|
|
|
|
|
this.$notify( |
|
|
|
|
|
{ |
|
|
|
|
|
group: "alert", |
|
|
|
|
|
type: "success", |
|
|
|
|
|
title: `Sent to ${serviceName}`, |
|
|
|
|
|
text: `The project info was sent to ${serviceName}.`, |
|
|
|
|
|
}, |
|
|
|
|
|
5000, |
|
|
|
|
|
); |
|
|
|
|
|
} else { |
|
|
|
|
|
// axios never gets here because it throws an error, but just in case |
|
|
|
|
|
this.$notify( |
|
|
|
|
|
{ |
|
|
|
|
|
group: "alert", |
|
|
|
|
|
type: "danger", |
|
|
|
|
|
title: `Failed Sending to ${serviceName}`, |
|
|
|
|
|
text: JSON.stringify(linkResp.data), |
|
|
|
|
|
}, |
|
|
|
|
|
5000, |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
console.error(`Error sending to ${serviceName}`, error); |
|
|
|
|
|
let errorMessage = `There was an error sending to ${serviceName}.`; |
|
|
|
|
|
if (error.response?.data?.error?.message) { |
|
|
|
|
|
errorMessage = error.response.data.error.message; |
|
|
|
|
|
} |
|
|
|
|
|
this.$notify( |
|
|
|
|
|
{ |
|
|
|
|
|
group: "alert", |
|
|
|
|
|
type: "danger", |
|
|
|
|
|
title: `Error Sending to ${serviceName}`, |
|
|
|
|
|
text: errorMessage, |
|
|
|
|
|
}, |
|
|
|
|
|
5000, |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
public async onSaveProjectClick() { |
|
|
public async onSaveProjectClick() { |
|
|
this.isHiddenSave = true; |
|
|
this.isHiddenSave = true; |
|
|
this.isHiddenSpinner = false; |
|
|
this.isHiddenSpinner = false; |
|
@ -492,7 +618,7 @@ export default class NewEditProjectView extends Vue { |
|
|
if (this.numAccounts === 0) { |
|
|
if (this.numAccounts === 0) { |
|
|
console.error("Error: there is no account."); |
|
|
console.error("Error: there is no account."); |
|
|
} else { |
|
|
} else { |
|
|
this.saveProject(this.activeDid); |
|
|
this.saveProject(); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|