add page for one-on-one invites (incomplete)
This commit is contained in:
118
src/components/InviteDialog.vue
Normal file
118
src/components/InviteDialog.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<div v-if="visible" class="dialog-overlay">
|
||||
<div class="dialog">
|
||||
<h1 class="text-xl font-bold text-center mb-4">{{ title }}</h1>
|
||||
|
||||
{{ message }}
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Name"
|
||||
class="block w-full rounded border border-slate-400 mb-4 px-3 py-2"
|
||||
v-model="text"
|
||||
/>
|
||||
|
||||
<!-- Add date selection element -->
|
||||
Expiration
|
||||
<input
|
||||
type="date"
|
||||
class="block rounded border border-slate-400 mb-4 px-3 py-2"
|
||||
v-model="expiresAt"
|
||||
/>
|
||||
|
||||
<div class="mt-8">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="block w-full text-center text-lg font-bold uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md mb-2"
|
||||
@click="onClickSaveChanges()"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
<!-- SHOW ME instead while processing saving changes -->
|
||||
<button
|
||||
type="button"
|
||||
class="block w-full text-center text-md uppercase 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-3 rounded-md mb-2"
|
||||
@click="onClickCancel()"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Vue, Component } from "vue-facing-decorator";
|
||||
|
||||
import { NotificationIface } from "@/constants/app";
|
||||
|
||||
@Component
|
||||
export default class InviteDialog extends Vue {
|
||||
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
||||
|
||||
callback: (text: string, expiresAt: string) => void = () => {};
|
||||
message = "";
|
||||
text = "";
|
||||
title = "";
|
||||
visible = false;
|
||||
expiresAt = new Date(Date.now() + 1000 * 60 * 60 * 24 * 30 * 3)
|
||||
.toISOString()
|
||||
.substring(0, 10);
|
||||
|
||||
async open(
|
||||
title: string,
|
||||
message: string,
|
||||
aCallback: (text: string, expiresAt: string) => void,
|
||||
) {
|
||||
this.callback = aCallback;
|
||||
this.title = title;
|
||||
this.message = message;
|
||||
this.visible = true;
|
||||
}
|
||||
|
||||
async onClickSaveChanges() {
|
||||
if (!this.expiresAt) {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "warning",
|
||||
title: "Needs Expiration",
|
||||
text: "You must select an expiration date.",
|
||||
},
|
||||
5000,
|
||||
);
|
||||
} else {
|
||||
this.callback(this.text, this.expiresAt);
|
||||
this.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
onClickCancel() {
|
||||
this.visible = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.dialog-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.dialog {
|
||||
background-color: white;
|
||||
padding: 1rem;
|
||||
border-radius: 0.5rem;
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
}
|
||||
</style>
|
||||
@@ -46,7 +46,7 @@ import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
||||
export default class UserNameDialog extends Vue {
|
||||
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
||||
|
||||
callback: (string?) => void = () => {};
|
||||
callback: (name?: string) => void = () => {};
|
||||
givenName = "";
|
||||
visible = false;
|
||||
|
||||
|
||||
@@ -54,16 +54,22 @@ export function isFromPasskey(keyMeta?: KeyMeta): boolean {
|
||||
export async function createEndorserJwtForKey(
|
||||
account: KeyMeta,
|
||||
payload: object,
|
||||
expiresIn?: number,
|
||||
) {
|
||||
if (account?.identity) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const identity: IIdentifier = JSON.parse(account.identity!);
|
||||
const privateKeyHex = identity.keys[0].privateKeyHex;
|
||||
const signer = await SimpleSigner(privateKeyHex as string);
|
||||
return didJwt.createJWT(payload, {
|
||||
const options = {
|
||||
issuer: account.did,
|
||||
signer: signer,
|
||||
});
|
||||
expiresIn: undefined as number | undefined,
|
||||
}
|
||||
if (expiresIn) {
|
||||
options.expiresIn = expiresIn;
|
||||
}
|
||||
return didJwt.createJWT(payload, options);
|
||||
} else if (account?.passkeyCredIdHex) {
|
||||
return createDidPeerJwt(account.did, account.passkeyCredIdHex, payload);
|
||||
} else {
|
||||
|
||||
@@ -239,8 +239,9 @@ export interface RegisterVerifiableCredential {
|
||||
"@context": string;
|
||||
"@type": string;
|
||||
agent: { identifier: string };
|
||||
identifier?: string;
|
||||
object: string;
|
||||
participant: { identifier: string };
|
||||
participant?: { identifier: string };
|
||||
}
|
||||
|
||||
// now for some of the error & other wrapper types
|
||||
@@ -993,9 +994,10 @@ export async function generateEndorserJwtForAccount(
|
||||
export async function createEndorserJwtForDid(
|
||||
issuerDid: string,
|
||||
payload: object,
|
||||
expiresIn?: number,
|
||||
) {
|
||||
const account = await getAccount(issuerDid);
|
||||
return createEndorserJwtForKey(account as KeyMeta, payload);
|
||||
return createEndorserJwtForKey(account as KeyMeta, payload, expiresIn);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1225,19 +1227,24 @@ export async function createEndorserJwtVcFromClaim(
|
||||
return createEndorserJwtForDid(issuerDid, vcPayload);
|
||||
}
|
||||
|
||||
export async function register(
|
||||
export async function createInviteJwt(
|
||||
activeDid: string,
|
||||
apiServer: string,
|
||||
axios: Axios,
|
||||
contact: Contact,
|
||||
) {
|
||||
contact?: Contact,
|
||||
inviteId?: string,
|
||||
expiresIn?: number,
|
||||
): Promise<string> {
|
||||
const vcClaim: RegisterVerifiableCredential = {
|
||||
"@context": SCHEMA_ORG_CONTEXT,
|
||||
"@type": "RegisterAction",
|
||||
agent: { identifier: activeDid },
|
||||
object: SERVICE_ID,
|
||||
participant: { identifier: contact.did },
|
||||
};
|
||||
if (contact) {
|
||||
vcClaim.participant = { identifier: contact.did };
|
||||
}
|
||||
if (inviteId) {
|
||||
vcClaim.identifier = inviteId;
|
||||
}
|
||||
// Make a payload for the claim
|
||||
const vcPayload = {
|
||||
vc: {
|
||||
@@ -1247,7 +1254,17 @@ export async function register(
|
||||
},
|
||||
};
|
||||
// Create a signature using private key of identity
|
||||
const vcJwt = await createEndorserJwtForDid(activeDid, vcPayload);
|
||||
const vcJwt = await createEndorserJwtForDid(activeDid, vcPayload, expiresIn);
|
||||
return vcJwt;
|
||||
}
|
||||
|
||||
export async function register(
|
||||
activeDid: string,
|
||||
apiServer: string,
|
||||
axios: Axios,
|
||||
contact: Contact,
|
||||
): Promise<{ success?: boolean; error?: string }> {
|
||||
const vcJwt = await createInviteJwt(activeDid, contact);
|
||||
|
||||
const url = apiServer + "/api/v2/claim";
|
||||
const resp = await axios.post(url, { jwtEncoded: vcJwt });
|
||||
|
||||
@@ -39,6 +39,7 @@ import {
|
||||
faDollar,
|
||||
faEllipsis,
|
||||
faEllipsisVertical,
|
||||
faEnvelopeOpenText,
|
||||
faEye,
|
||||
faEyeSlash,
|
||||
faFileLines,
|
||||
@@ -109,6 +110,7 @@ library.add(
|
||||
faDollar,
|
||||
faEllipsis,
|
||||
faEllipsisVertical,
|
||||
faEnvelopeOpenText,
|
||||
faEye,
|
||||
faEyeSlash,
|
||||
faFileLines,
|
||||
|
||||
@@ -128,6 +128,11 @@ const routes: Array<RouteRecordRaw> = [
|
||||
name: "import-derive",
|
||||
component: () => import("../views/ImportDerivedAccountView.vue"),
|
||||
},
|
||||
{
|
||||
path: "/invite-one",
|
||||
name: "invite-one",
|
||||
component: () => import("../views/InviteOneView.vue"),
|
||||
},
|
||||
{
|
||||
path: "/new-edit-account",
|
||||
name: "new-edit-account",
|
||||
|
||||
@@ -23,12 +23,20 @@
|
||||
|
||||
<!-- New Contact -->
|
||||
<div id="formAddNewContact" class="mt-4 mb-4 flex items-stretch">
|
||||
<router-link
|
||||
:to="{ name: 'invite-one' }"
|
||||
class="flex items-center bg-gradient-to-b from-green-400 to-green-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-1 mr-1 rounded-md"
|
||||
>
|
||||
<fa icon="envelope-open-text" class="fa-fw text-2xl" />
|
||||
</router-link>
|
||||
|
||||
<router-link
|
||||
:to="{ name: 'contact-qr' }"
|
||||
class="flex items-center 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-1.5 py-1 mr-1 rounded-md"
|
||||
class="flex items-center bg-gradient-to-b from-green-400 to-green-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-1 mr-1 rounded-md"
|
||||
>
|
||||
<fa icon="qrcode" class="fa-fw text-2xl" />
|
||||
</router-link>
|
||||
|
||||
<textarea
|
||||
type="text"
|
||||
placeholder="URL or DID, Name, Public Key, Next Public Key Hash"
|
||||
@@ -79,7 +87,9 @@
|
||||
class="text-md 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-1 py-1 rounded-md"
|
||||
@click="toggleShowContactAmounts()"
|
||||
>
|
||||
{{ showGiveNumbers ? "Hide Given Hours" : "Show Given Hours" }}
|
||||
{{
|
||||
showGiveNumbers ? "Hide Given Hours etc" : "Show Given Hours etc"
|
||||
}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -288,7 +298,7 @@ import { Buffer } from "buffer/";
|
||||
import { IndexableType } from "dexie";
|
||||
import * as R from "ramda";
|
||||
import { Component, Vue } from "vue-facing-decorator";
|
||||
import { Router } from "vue-router";
|
||||
import { RouteLocationNormalizedLoaded, Router } from "vue-router";
|
||||
import { useClipboard } from "@vueuse/core";
|
||||
|
||||
import { AppString, NotificationIface } from "@/constants/app";
|
||||
@@ -377,7 +387,7 @@ export default class ContactsView extends Vue {
|
||||
(a.name || "").localeCompare(b.name || ""),
|
||||
);
|
||||
|
||||
const importedContactJwt = (this.$route as Router).query[
|
||||
const importedContactJwt = (this.$route as RouteLocationNormalizedLoaded).query[
|
||||
"contactJwt"
|
||||
] as string;
|
||||
if (importedContactJwt) {
|
||||
@@ -736,7 +746,7 @@ export default class ContactsView extends Vue {
|
||||
type: "confirm",
|
||||
title: "Register",
|
||||
text: "Do you want to register them?",
|
||||
onCancel: async (stopAsking: boolean) => {
|
||||
onCancel: async (stopAsking?: boolean) => {
|
||||
if (stopAsking) {
|
||||
await updateDefaultSettings({
|
||||
hideRegisterPromptOnNewContact: stopAsking,
|
||||
@@ -744,7 +754,7 @@ export default class ContactsView extends Vue {
|
||||
this.hideRegisterPromptOnNewContact = stopAsking;
|
||||
}
|
||||
},
|
||||
onNo: async (stopAsking: boolean) => {
|
||||
onNo: async (stopAsking?: boolean) => {
|
||||
if (stopAsking) {
|
||||
await updateDefaultSettings({
|
||||
hideRegisterPromptOnNewContact: stopAsking,
|
||||
@@ -853,9 +863,14 @@ export default class ContactsView extends Vue {
|
||||
console.error("Error when registering:", error);
|
||||
let userMessage = "There was an error. See logs for more info.";
|
||||
const serverError = error as AxiosError;
|
||||
if (serverError) {
|
||||
if (serverError.response?.data?.error?.message) {
|
||||
userMessage = serverError.response.data.error.message;
|
||||
if (serverError.isAxiosError) {
|
||||
if (serverError.response?.data
|
||||
&& typeof serverError.response.data === 'object'
|
||||
&& 'error' in serverError.response.data
|
||||
&& typeof serverError.response.data.error === 'object'
|
||||
&& serverError.response.data.error !== null
|
||||
&& 'message' in serverError.response.data.error){
|
||||
userMessage = serverError.response.data.error.message as string;
|
||||
} else if (serverError.message) {
|
||||
userMessage = serverError.message; // Info for the user
|
||||
} else {
|
||||
@@ -971,8 +986,8 @@ export default class ContactsView extends Vue {
|
||||
}
|
||||
|
||||
private showGiftedDialog(giverDid: string, recipientDid: string) {
|
||||
let giver: libsUtil.GiverReceiverInputInfo;
|
||||
let receiver: libsUtil.GiverReceiverInputInfo;
|
||||
let giver: libsUtil.GiverReceiverInputInfo | undefined;
|
||||
let receiver: libsUtil.GiverReceiverInputInfo | undefined;
|
||||
if (giverDid) {
|
||||
giver = {
|
||||
did: giverDid,
|
||||
@@ -995,7 +1010,7 @@ export default class ContactsView extends Vue {
|
||||
newList[recipientDid] = (newList[recipientDid] || 0) + amount;
|
||||
this.givenByMeUnconfirmed = newList;
|
||||
};
|
||||
customTitle = "Given to " + receiver.name;
|
||||
customTitle = "Given to " + (receiver?.name || "Someone Unnamed");
|
||||
} else {
|
||||
// must be (recipientDid == this.activeDid)
|
||||
callback = (amount: number) => {
|
||||
@@ -1003,14 +1018,14 @@ export default class ContactsView extends Vue {
|
||||
newList[giverDid] = (newList[giverDid] || 0) + amount;
|
||||
this.givenToMeUnconfirmed = newList;
|
||||
};
|
||||
customTitle = "Received from " + giver.name;
|
||||
customTitle = "Received from " + (giver?.name || "Someone Unnamed");
|
||||
}
|
||||
(this.$refs.customGivenDialog as GiftedDialog).open(
|
||||
giver,
|
||||
receiver,
|
||||
undefined as string,
|
||||
undefined as unknown as string,
|
||||
customTitle,
|
||||
undefined as string,
|
||||
undefined as unknown as string,
|
||||
callback,
|
||||
);
|
||||
}
|
||||
|
||||
195
src/views/InviteOneView.vue
Normal file
195
src/views/InviteOneView.vue
Normal file
@@ -0,0 +1,195 @@
|
||||
<template>
|
||||
<QuickNav selected="Invite" />
|
||||
<TopMessage />
|
||||
|
||||
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
|
||||
<!-- Back -->
|
||||
<div class="text-lg text-center font-light relative px-7">
|
||||
<h1
|
||||
class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
|
||||
@click="$router.back()"
|
||||
>
|
||||
<fa icon="chevron-left" class="fa-fw"></fa>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<!-- Heading -->
|
||||
<h1 class="text-4xl text-center font-light">Invitations</h1>
|
||||
|
||||
<!-- New Project -->
|
||||
<button
|
||||
v-if="isRegistered"
|
||||
class="fixed right-6 top-12 text-center text-4xl leading-none bg-blue-600 text-white w-14 py-2.5 rounded-full"
|
||||
@click="createInvite()"
|
||||
>
|
||||
<fa icon="plus" class="fa-fw"></fa>
|
||||
</button>
|
||||
|
||||
<InviteDialog ref="inviteDialog" />
|
||||
|
||||
<!-- Invites Table -->
|
||||
<div v-if="invites.length" class="mt-6">
|
||||
<table class="min-w-full bg-white">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="py-2">ID</th>
|
||||
<th class="py-2">Notes</th>
|
||||
<th class="py-2">Expires At</th>
|
||||
<th class="py-2">Redeemed By</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="invite in invites"
|
||||
:key="invite.inviteIdentifier"
|
||||
class="border-t"
|
||||
>
|
||||
<td class="py-2 text-center">
|
||||
{{ getTruncatedInviteId(invite.inviteIdentifier) }}
|
||||
</td>
|
||||
<td class="py-2 text-left">{{ invite.notes }}</td>
|
||||
<td class="py-2 text-center">
|
||||
{{ invite.expiresAt.substring(0, 10) }}
|
||||
</td>
|
||||
<td class="py-2 text-center">
|
||||
{{ getTruncatedRedeemedBy(invite.redeemedBy) }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p v-else class="mt-6 text-center">No invites found.</p>
|
||||
</section>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from "vue-facing-decorator";
|
||||
import axios from "axios";
|
||||
|
||||
import { db, retrieveSettingsForActiveAccount } from "../db";
|
||||
import QuickNav from "@/components/QuickNav.vue";
|
||||
import TopMessage from "@/components/TopMessage.vue";
|
||||
import InviteDialog from "@/components/InviteDialog.vue";
|
||||
import { NotificationIface } from "@/constants/app";
|
||||
import { createInviteJwt, getHeaders } from "@/libs/endorserServer";
|
||||
|
||||
interface Invite {
|
||||
inviteIdentifier: string;
|
||||
expiresAt: string;
|
||||
notes: string;
|
||||
redeemedBy: string | null;
|
||||
}
|
||||
|
||||
@Component({
|
||||
components: { QuickNav, TopMessage, InviteDialog },
|
||||
})
|
||||
export default class InviteOneView extends Vue {
|
||||
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
||||
|
||||
invites: Invite[] = [];
|
||||
activeDid: string = "";
|
||||
apiServer: string = "";
|
||||
isRegistered: boolean = false;
|
||||
|
||||
async mounted() {
|
||||
try {
|
||||
await db.open();
|
||||
const settings = await retrieveSettingsForActiveAccount();
|
||||
this.activeDid = settings.activeDid || "";
|
||||
this.apiServer = settings.apiServer || "";
|
||||
this.isRegistered = !!settings.isRegistered;
|
||||
|
||||
const headers = await getHeaders(this.activeDid);
|
||||
const response = await axios.get(
|
||||
this.apiServer + "/api/userUtil/invite",
|
||||
{ headers },
|
||||
);
|
||||
this.invites = response.data.data;
|
||||
} catch (error) {
|
||||
console.error("Error fetching invites:", error);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Load Error",
|
||||
text: "Got an error loading your invites.",
|
||||
},
|
||||
5000,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
getTruncatedInviteId(inviteId: string): string {
|
||||
if (inviteId.length <= 9) return inviteId;
|
||||
return `${inviteId.slice(0, 3)}...${inviteId.slice(-3)}`;
|
||||
}
|
||||
|
||||
getTruncatedRedeemedBy(redeemedBy: string | null): string {
|
||||
if (!redeemedBy) return "Not yet redeemed";
|
||||
if (redeemedBy.length <= 19) return redeemedBy;
|
||||
return `${redeemedBy.slice(0, 13)}...${redeemedBy.slice(-3)}`;
|
||||
}
|
||||
|
||||
async createInvite() {
|
||||
(this.$refs.inviteDialog as InviteDialog).open(
|
||||
"Invitation Note",
|
||||
`These notes are only for your use, to make comments for a link to recall later if redeemed by someone.
|
||||
Note that this is sent to the server.`,
|
||||
async (notes, expiresAt) => {
|
||||
try {
|
||||
const inviteIdentifier =
|
||||
Math.random().toString(36).substring(2) +
|
||||
Math.random().toString(36).substring(2) +
|
||||
Math.random().toString(36).substring(2);
|
||||
const headers = await getHeaders(this.activeDid);
|
||||
if (!expiresAt) {
|
||||
throw {
|
||||
response: {
|
||||
data: { error: "You must select an expiration date." },
|
||||
},
|
||||
};
|
||||
}
|
||||
const expiresIn =
|
||||
(new Date(expiresAt).getTime() - new Date().getTime()) / 1000;
|
||||
const inviteJwt = await createInviteJwt(
|
||||
this.activeDid,
|
||||
undefined,
|
||||
inviteIdentifier,
|
||||
expiresIn,
|
||||
);
|
||||
await axios.post(
|
||||
this.apiServer + "/api/userUtil/invite",
|
||||
{ inviteJwt: inviteJwt, notes: notes },
|
||||
{ headers },
|
||||
);
|
||||
this.invites.push({
|
||||
inviteIdentifier: inviteIdentifier,
|
||||
expiresAt: expiresAt,
|
||||
notes: notes,
|
||||
redeemedBy: null,
|
||||
});
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (error: any) {
|
||||
console.error("Error creating invite:", error);
|
||||
let message = "Got an error creating your invite.";
|
||||
if (
|
||||
error.response &&
|
||||
error.response.data &&
|
||||
error.response.data.error
|
||||
) {
|
||||
message = error.response.data.error;
|
||||
}
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error Creating Invite",
|
||||
text: message,
|
||||
},
|
||||
5000,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -196,7 +196,7 @@ import { accountFromSeedWords } 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";
|
||||
import { Router } from "vue-router";
|
||||
import { RouteLocationNormalizedLoaded, Router } from "vue-router";
|
||||
|
||||
import ImageMethodDialog from "@/components/ImageMethodDialog.vue";
|
||||
import QuickNav from "@/components/QuickNav.vue";
|
||||
@@ -218,7 +218,7 @@ import { getAccount } from "@/libs/util";
|
||||
})
|
||||
export default class NewEditProjectView extends Vue {
|
||||
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
||||
errNote(message) {
|
||||
errNote(message: string) {
|
||||
this.$notify(
|
||||
{ group: "alert", type: "danger", title: "Error", text: message },
|
||||
5000,
|
||||
@@ -262,7 +262,7 @@ export default class NewEditProjectView extends Vue {
|
||||
this.apiServer = settings.apiServer || "";
|
||||
this.showGeneralAdvanced = !!settings.showGeneralAdvanced;
|
||||
|
||||
this.projectId = (this.$route as Router).query["projectId"] || "";
|
||||
this.projectId = (this.$route as RouteLocationNormalizedLoaded).query["projectId"] || "";
|
||||
|
||||
if (this.projectId) {
|
||||
if (this.numAccounts === 0) {
|
||||
@@ -447,7 +447,7 @@ export default class NewEditProjectView extends Vue {
|
||||
|
||||
const projectPath = encodeURIComponent(resp.data.success.handleId);
|
||||
|
||||
let signedPayload: VerifiedEvent; // sign something to prove ownership of pubkey
|
||||
let signedPayload: VerifiedEvent | undefined; // sign something to prove ownership of pubkey
|
||||
if (this.sendToTrustroots) {
|
||||
signedPayload = await this.signPayload();
|
||||
this.sendToNostrPartner(
|
||||
@@ -623,7 +623,7 @@ export default class NewEditProjectView extends Vue {
|
||||
5000,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
console.error(`Error sending to ${serviceName}`, error);
|
||||
let errorMessage = `There was an error sending to ${serviceName}.`;
|
||||
if (error.response?.data?.error?.message) {
|
||||
|
||||
Reference in New Issue
Block a user