Browse Source

prompt for name when showing info, and provide a "copy" page when remote

pull/121/head^2
Trent Larson 4 weeks ago
parent
commit
767d33c0a0
  1. 5
      src/App.vue
  2. 2
      src/components/ImageMethodDialog.vue
  3. 96
      src/components/UserNameDialog.vue
  4. 4
      src/constants/app.ts
  5. 52
      src/libs/endorserServer.ts
  6. 5
      src/router/index.ts
  7. 4
      src/views/AccountViewView.vue
  8. 18
      src/views/ContactQRScanShowView.vue
  9. 4
      src/views/ContactsView.vue
  10. 10
      src/views/DiscoverView.vue
  11. 62
      src/views/HomeView.vue
  12. 8
      src/views/ProjectsView.vue
  13. 106
      src/views/ShareMyContactInfoView.vue

5
src/App.vue

@ -180,8 +180,9 @@
" "
class="block w-full text-center text-md font-bold uppercase bg-blue-600 text-white px-2 py-2 rounded-md mb-2" class="block w-full text-center text-md font-bold uppercase bg-blue-600 text-white px-2 py-2 rounded-md mb-2"
> >
Yes Yes{{
{{ notification.yesText ? ", " + notification.yesText : "" }} notification.yesText ? ", " + notification.yesText : ""
}}
</button> </button>
<button <button

2
src/components/ImageMethodDialog.vue

@ -6,7 +6,7 @@
id="ViewHeading" id="ViewHeading"
class="text-center font-bold absolute top-0 left-0 right-0 px-4 py-0.5 bg-black/50 text-white leading-none" class="text-center font-bold absolute top-0 left-0 right-0 px-4 py-0.5 bg-black/50 text-white leading-none"
> >
Camera or Other? Add Photo
</div> </div>
<div <div
class="text-lg text-center px-2 py-0.5 leading-none absolute right-0 top-0 text-white" class="text-lg text-center px-2 py-0.5 leading-none absolute right-0 top-0 text-white"

96
src/components/UserNameDialog.vue

@ -0,0 +1,96 @@
<template>
<div v-if="visible" class="dialog-overlay">
<div class="dialog">
<h1 class="text-xl font-bold text-center mb-4">Set Your Name</h1>
Note that this is not sent to servers. It is only shared with people when
you choose to send it to them.
<input
type="text"
placeholder="Name"
class="block w-full rounded border border-slate-400 mb-4 px-3 py-2"
v-model="givenName"
/>
<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";
import { db } from "@/db/index";
import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings";
@Component
export default class UserNameDialog extends Vue {
$notify!: (notification: NotificationIface, timeout?: number) => void;
callback: (string?) => void = () => {};
givenName = "";
visible = false;
async open(aCallback?: (name?: string) => void) {
this.callback = aCallback || this.callback;
await db.open();
const settings = (await db.settings.get(MASTER_SETTINGS_KEY)) as Settings;
this.givenName = settings?.firstName || "";
this.visible = true;
}
async onClickSaveChanges() {
await db.settings.update(MASTER_SETTINGS_KEY, {
firstName: this.givenName,
});
this.visible = false;
this.callback(this.givenName);
}
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>

4
src/constants/app.ts

@ -49,8 +49,8 @@ export interface NotificationIface {
title: string; title: string;
text?: string; text?: string;
noText?: string; noText?: string;
onCancel?: (stopAsking: boolean) => Promise<void>; onCancel?: (stopAsking?: boolean) => Promise<void>;
onNo?: (stopAsking: boolean) => Promise<void>; onNo?: (stopAsking?: boolean) => Promise<void>;
onYes?: () => Promise<void>; onYes?: () => Promise<void>;
promptToStopAsking?: boolean; promptToStopAsking?: boolean;
yesText?: string; yesText?: string;

52
src/libs/endorserServer.ts

@ -1,13 +1,16 @@
import { Axios, AxiosRequestConfig, AxiosResponse } from "axios"; import { Axios, AxiosRequestConfig, AxiosResponse } from "axios";
import { Buffer } from "buffer";
import { sha256 } from "ethereum-cryptography/sha256";
import { LRUCache } from "lru-cache"; import { LRUCache } from "lru-cache";
import * as R from "ramda"; import * as R from "ramda";
import { DEFAULT_IMAGE_API_SERVER } from "@/constants/app"; import { DEFAULT_IMAGE_API_SERVER } from "@/constants/app";
import { Contact } from "@/db/tables/contacts"; import { Contact } from "@/db/tables/contacts";
import { accessToken } from "@/libs/crypto"; import { accessToken, deriveAddress, nextDerivationPath } from "@/libs/crypto";
import { NonsensitiveDexie } from "@/db/index"; import { NonsensitiveDexie } from "@/db/index";
import { getAccount, getPasskeyExpirationSeconds } from "@/libs/util"; import { getAccount, getPasskeyExpirationSeconds } from "@/libs/util";
import { createEndorserJwtForKey, KeyMeta } from "@/libs/crypto/vc"; import { createEndorserJwtForKey, KeyMeta } from "@/libs/crypto/vc";
import { Account } from "@/db/tables/accounts";
export const SCHEMA_ORG_CONTEXT = "https://schema.org"; export const SCHEMA_ORG_CONTEXT = "https://schema.org";
// the object in RegisterAction claims // the object in RegisterAction claims
@ -925,6 +928,53 @@ export async function createAndSubmitClaim(
} }
} }
export async function generateEndorserJwtForAccount(
account: Account,
isRegistered?: boolean,
name?: string,
profileImageUrl?: string,
) {
const publicKeyHex = account.publicKeyHex;
const publicEncKey = Buffer.from(publicKeyHex, "hex").toString("base64");
interface UserInfo {
name: string;
publicEncKey: string;
registered: boolean;
profileImageUrl?: string;
nextPublicEncKeyHash?: string;
}
const contactInfo = {
iat: Date.now(),
iss: account.did,
own: {
name: name ?? "",
publicEncKey,
registered: !!isRegistered,
} as UserInfo,
};
if (profileImageUrl) {
contactInfo.own.profileImageUrl = profileImageUrl;
}
if (account?.mnemonic && account?.derivationPath) {
const newDerivPath = nextDerivationPath(account.derivationPath as string);
const nextPublicHex = deriveAddress(
account.mnemonic as string,
newDerivPath,
)[2];
const nextPublicEncKey = Buffer.from(nextPublicHex, "hex");
const nextPublicEncKeyHash = sha256(nextPublicEncKey);
const nextPublicEncKeyHashBase64 =
Buffer.from(nextPublicEncKeyHash).toString("base64");
contactInfo.own.nextPublicEncKeyHash = nextPublicEncKeyHashBase64;
}
const vcJwt = await createEndorserJwtForDid(account.did, contactInfo);
const viewPrefix = CONTACT_URL_PREFIX + ENDORSER_JWT_URL_LOCATION;
return viewPrefix + vcJwt;
}
export async function createEndorserJwtForDid( export async function createEndorserJwtForDid(
issuerDid: string, issuerDid: string,
payload: object, payload: object,

5
src/router/index.ts

@ -189,6 +189,11 @@ const routes: Array<RouteRecordRaw> = [
name: "seed-backup", name: "seed-backup",
component: () => import("../views/SeedBackupView.vue"), component: () => import("../views/SeedBackupView.vue"),
}, },
{
path: "/share-my-contact-info",
name: "share-my-contact-info",
component: () => import("@/views/ShareMyContactInfoView.vue"),
},
{ {
path: "/shared-photo", path: "/shared-photo",
name: "shared-photo", name: "shared-photo",

4
src/views/AccountViewView.vue

@ -5,11 +5,11 @@
<!-- CONTENT --> <!-- CONTENT -->
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto"> <section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
<!-- Heading --> <!-- Heading -->
<h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-4"> <h1 id="ViewHeading" class="text-4xl text-center font-light">
Your Identity Your Identity
</h1> </h1>
<div class="flex justify-between"> <div class="flex justify-between mb-2 mt-4">
<span /> <span />
<span class="whitespace-nowrap"> <span class="whitespace-nowrap">
<router-link <router-link

18
src/views/ContactQRScanShowView.vue

@ -1,5 +1,5 @@
<template> <template>
<QuickNav selected="Profile"></QuickNav> <QuickNav selected="Profile" />
<!-- CONTENT --> <!-- CONTENT -->
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto"> <section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
<!-- Breadcrumb --> <!-- Breadcrumb -->
@ -109,6 +109,7 @@ import {
CONTACT_URL_PREFIX, CONTACT_URL_PREFIX,
createEndorserJwtForDid, createEndorserJwtForDid,
ENDORSER_JWT_URL_LOCATION, ENDORSER_JWT_URL_LOCATION,
generateEndorserJwtForAccount,
isDid, isDid,
register, register,
setVisibilityUtil, setVisibilityUtil,
@ -157,7 +158,7 @@ export default class ContactQRScanShow extends Vue {
own: { own: {
name: name:
(settings?.firstName || "") + (settings?.firstName || "") +
(settings?.lastName ? ` ${settings.lastName}` : ""), // deprecated, pre v 0.1.3 (settings?.lastName ? ` ${settings.lastName}` : ""), // lastName is deprecated, pre v 0.1.3
publicEncKey, publicEncKey,
profileImageUrl: settings?.profileImageUrl, profileImageUrl: settings?.profileImageUrl,
registered: settings?.isRegistered, registered: settings?.isRegistered,
@ -182,7 +183,18 @@ export default class ContactQRScanShow extends Vue {
const vcJwt = await createEndorserJwtForDid(this.activeDid, contactInfo); const vcJwt = await createEndorserJwtForDid(this.activeDid, contactInfo);
const viewPrefix = CONTACT_URL_PREFIX + ENDORSER_JWT_URL_LOCATION; const viewPrefix = CONTACT_URL_PREFIX + ENDORSER_JWT_URL_LOCATION;
this.qrValue = viewPrefix + vcJwt; viewPrefix + vcJwt;
const name =
(settings?.firstName || "") +
(settings?.lastName ? ` ${settings.lastName}` : ""); // lastName is deprecated, pre v 0.1.3
this.qrValue = await generateEndorserJwtForAccount(
account,
!!settings?.isRegistered,
name,
settings?.profileImageUrl as string,
);
} }
} }

4
src/views/ContactsView.vue

@ -4,11 +4,11 @@
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto"> <section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
<!-- Heading --> <!-- Heading -->
<h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8"> <h1 id="ViewHeading" class="text-4xl text-center font-light">
Your Contacts Your Contacts
</h1> </h1>
<div class="flex justify-between py-2"> <div class="flex justify-between py-2 mt-8">
<span /> <span />
<span> <span>
<a <a

10
src/views/DiscoverView.vue

@ -1,16 +1,20 @@
<template> <template>
<QuickNav selected="Discover"></QuickNav> <QuickNav selected="Discover" />
<TopMessage /> <TopMessage />
<!-- CONTENT --> <!-- CONTENT -->
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto"> <section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
<!-- Heading --> <!-- Heading -->
<h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8"> <h1 id="ViewHeading" class="text-4xl text-center font-light">
Discover Projects Discover Projects
</h1> </h1>
<!-- Quick Search --> <!-- Quick Search -->
<div id="QuickSearch" class="mb-4 flex" v-on:keyup.enter="searchSelected()"> <div
id="QuickSearch"
class="mt-8 mb-4 flex"
v-on:keyup.enter="searchSelected()"
>
<input <input
type="text" type="text"
v-model="searchTerms" v-model="searchTerms"

62
src/views/HomeView.vue

@ -4,12 +4,12 @@
<!-- CONTENT --> <!-- CONTENT -->
<section id="Content" class="p-2 pb-24 max-w-3xl mx-auto"> <section id="Content" class="p-2 pb-24 max-w-3xl mx-auto">
<h1 id="ViewHeading" class="text-4xl text-center font-light px-4 mb-8"> <h1 id="ViewHeading" class="text-4xl text-center font-light mb-8">
{{ AppString.APP_NAME }} {{ AppString.APP_NAME }}
</h1> </h1>
<!-- prompt to install notifications, which we're making an advanced feature --> <!-- prompt to install notifications with notificationsSupported, which we're making an advanced feature -->
<div class="mb-8"> <div class="mb-8 mt-8">
<div <div
v-if="false" v-if="false"
class="bg-amber-200 rounded-md overflow-hidden text-center px-4 py-3 mb-4" class="bg-amber-200 rounded-md overflow-hidden text-center px-4 py-3 mb-4"
@ -86,13 +86,14 @@
> >
<!-- activeDid && !isRegistered --> <!-- activeDid && !isRegistered -->
To share, someone must register you. To share, someone must register you.
<router-link <div
:to="{ name: 'contact-qr' }" @click="showNameDialog()"
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" 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 Show Them {{ PASSKEYS_ENABLED ? "Default" : "Your" }} Identifier
Info Info
</router-link> </div>
<UserNameDialog ref="userNameDialog" />
<div v-if="PASSKEYS_ENABLED" class="flex justify-end w-full"> <div v-if="PASSKEYS_ENABLED" class="flex justify-end w-full">
<router-link <router-link
:to="{ name: 'start' }" :to="{ name: 'start' }"
@ -107,7 +108,7 @@
<!-- activeDid && isRegistered --> <!-- activeDid && isRegistered -->
<!-- show the actions for recognizing a give --> <!-- show the actions for recognizing a give -->
<div class="flex justify-between mb-4"> <div class="flex justify-between">
<h2 class="text-xl font-bold">Record Something Given By:</h2> <h2 class="text-xl font-bold">Record Something Given By:</h2>
<div class="flex justify-end"> <div class="flex justify-end">
<button <button
@ -120,7 +121,7 @@
</div> </div>
<ul <ul
class="grid grid-cols-4 sm:grid-cols-5 md:grid-cols-6 gap-x-3 gap-y-5 text-center mb-5" class="grid grid-cols-4 sm:grid-cols-5 md:grid-cols-6 gap-x-3 gap-y-5 text-center mt-4"
> >
<li @click="openDialog()"> <li @click="openDialog()">
<img <img
@ -169,7 +170,7 @@
<FeedFilters ref="feedFilters" /> <FeedFilters ref="feedFilters" />
<!-- Results List --> <!-- Results List -->
<div class="bg-slate-100 rounded-md overflow-hidden px-4 py-3 mb-4"> <div class="bg-slate-100 rounded-md overflow-hidden px-4 py-3 mt-4 mb-4">
<div class="flex items-center mb-4"> <div class="flex items-center mb-4">
<h2 class="text-xl font-bold">Latest Activity</h2> <h2 class="text-xl font-bold">Latest Activity</h2>
<button @click="openFeedFilters()" class="block text-center ml-auto"> <button @click="openFeedFilters()" class="block text-center ml-auto">
@ -313,6 +314,7 @@ import FeedFilters from "@/components/FeedFilters.vue";
import InfiniteScroll from "@/components/InfiniteScroll.vue"; import InfiniteScroll from "@/components/InfiniteScroll.vue";
import QuickNav from "@/components/QuickNav.vue"; import QuickNav from "@/components/QuickNav.vue";
import TopMessage from "@/components/TopMessage.vue"; import TopMessage from "@/components/TopMessage.vue";
import UserNameDialog from "@/components/UserNameDialog.vue";
import { import {
AppString, AppString,
NotificationIface, NotificationIface,
@ -370,6 +372,7 @@ interface GiveRecordWithContactInfo extends GiveSummaryRecord {
EntityIcon, EntityIcon,
InfiniteScroll, InfiniteScroll,
TopMessage, TopMessage,
UserNameDialog,
}, },
}) })
export default class HomeView extends Vue { export default class HomeView extends Vue {
@ -426,6 +429,7 @@ export default class HomeView extends Vue {
this.showShortcutBvc = !!settings?.showShortcutBvc; this.showShortcutBvc = !!settings?.showShortcutBvc;
this.isAnyFeedFilterOn = isAnyFeedFilterOn(settings); this.isAnyFeedFilterOn = isAnyFeedFilterOn(settings);
console.log("getting through mounted");
// someone may have have registered after sharing contact info, so recheck // someone may have have registered after sharing contact info, so recheck
if (!this.isRegistered && this.activeDid) { if (!this.isRegistered && this.activeDid) {
@ -449,7 +453,7 @@ export default class HomeView extends Vue {
} }
// this returns a Promise but we don't need to wait for it // this returns a Promise but we don't need to wait for it
await this.updateAllFeed(); this.updateAllFeed();
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (err: any) { } catch (err: any) {
@ -495,7 +499,7 @@ export default class HomeView extends Vue {
this.feedData = []; this.feedData = [];
this.feedPreviousOldestId = undefined; this.feedPreviousOldestId = undefined;
this.updateAllFeed(); await this.updateAllFeed();
} }
/** /**
@ -507,7 +511,7 @@ export default class HomeView extends Vue {
// and the InfiniteScroll component triggers a load before finished. // and the InfiniteScroll component triggers a load before finished.
// One alternative is to totally separate the project link loading. // One alternative is to totally separate the project link loading.
if (payload && !this.isFeedLoading) { if (payload && !this.isFeedLoading) {
this.updateAllFeed(); await this.updateAllFeed();
} }
} }
@ -527,6 +531,7 @@ export default class HomeView extends Vue {
async updateAllFeed() { async updateAllFeed() {
this.isFeedLoading = true; this.isFeedLoading = true;
let endOfResults = true; let endOfResults = true;
console.log("about to retrieveGives");
await this.retrieveGives(this.apiServer, this.feedPreviousOldestId) await this.retrieveGives(this.apiServer, this.feedPreviousOldestId)
.then(async (results) => { .then(async (results) => {
if (results.data.length > 0) { if (results.data.length > 0) {
@ -620,7 +625,7 @@ export default class HomeView extends Vue {
}); });
if (this.feedData.length === 0 && !endOfResults) { if (this.feedData.length === 0 && !endOfResults) {
// repeat until there's at least some data // repeat until there's at least some data
this.updateAllFeed(); await this.updateAllFeed();
} }
this.isFeedLoading = false; this.isFeedLoading = false;
} }
@ -770,5 +775,36 @@ export default class HomeView extends Vue {
computeKnownPersonIconStyleClassNames(known: boolean) { computeKnownPersonIconStyleClassNames(known: boolean) {
return known ? "text-slate-500" : "text-slate-100"; return known ? "text-slate-500" : "text-slate-100";
} }
showNameDialog() {
if (!this.givenName) {
(this.$refs.userNameDialog as UserNameDialog).open(() => {
this.promptForShareMethod();
});
} else {
this.promptForShareMethod();
}
}
promptForShareMethod() {
this.$notify(
{
group: "modal",
type: "confirm",
title: "Are you nearby with cameras?",
text: "If so, we'll use those with QR codes to share.",
onCancel: async () => {},
onNo: async () => {
(this.$router as Router).push({ name: "share-my-contact-info" });
},
onYes: async () => {
(this.$router as Router).push({ name: "contact-qr" });
},
noText: "we will share another way",
yesText: "we are nearby with cameras",
},
-1,
);
}
} }
</script> </script>

8
src/views/ProjectsView.vue

@ -1,15 +1,13 @@
<template> <template>
<QuickNav selected="Projects"></QuickNav> <QuickNav selected="Projects" />
<TopMessage /> <TopMessage />
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto"> <section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
<!-- Heading --> <!-- Heading -->
<h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8"> <h1 id="ViewHeading" class="text-4xl text-center font-light">Your Ideas</h1>
Your Ideas
</h1>
<!-- Result Tabs --> <!-- Result Tabs -->
<div class="text-center text-slate-500 border-b border-slate-300"> <div class="text-center text-slate-500 border-b border-slate-300 mt-8">
<ul class="flex flex-wrap justify-center gap-4 -mb-px"> <ul class="flex flex-wrap justify-center gap-4 -mb-px">
<li> <li>
<a <a

106
src/views/ShareMyContactInfoView.vue

@ -0,0 +1,106 @@
<template>
<QuickNav />
<TopMessage />
<!-- CONTENT -->
<section id="Content" class="p-2 pb-24 max-w-3xl mx-auto">
<!-- Breadcrumb -->
<div>
<!-- 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" />
</h1>
</div>
<!-- Heading -->
<h1 id="ViewHeading" class="text-4xl text-center font-light">
Share Your Contact Info
</h1>
</div>
<div class="flex justify-center mt-8">
<button
class="block w-fit text-center text-lg 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 px-2 py-3 rounded-md"
@click="onClickShare()"
>
Copy to Clipboard
</button>
</div>
<div class="mt-8">Click to copy your info, then send it to them.</div>
<div>
They will paste it in the input box on the Contacts
<fa icon="users" /> screen.
</div>
</section>
</template>
<script lang="ts">
import * as R from "ramda";
import { Component, Vue } from "vue-facing-decorator";
import { Router } from "vue-router";
import { useClipboard } from "@vueuse/core";
import QuickNav from "@/components/QuickNav.vue";
import TopMessage from "@/components/TopMessage.vue";
import { NotificationIface } from "@/constants/app";
import { accountsDB, db } from "@/db/index";
import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings";
import { generateEndorserJwtForAccount } from "@/libs/endorserServer";
@Component({
components: { QuickNav, TopMessage },
})
export default class ShareMyContactInfoView extends Vue {
$notify!: (notification: NotificationIface, timeout?: number) => void;
async onClickShare() {
await db.open();
const settings = (await db.settings.get(MASTER_SETTINGS_KEY)) as Settings;
const activeDid = settings?.activeDid || "";
const givenName = settings?.firstName || "";
const isRegistered = !!settings?.isRegistered;
const profileImageUrl = settings?.profileImageUrl || "";
await accountsDB.open();
const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === activeDid, accounts);
if (account) {
const message = await generateEndorserJwtForAccount(
account,
isRegistered,
givenName,
profileImageUrl,
);
useClipboard()
.copy(message)
.then(() => {
this.$notify(
{
group: "alert",
type: "info",
title: "Copied",
text: "Those contacts were copied to the clipboard. Have them paste it in the box on their 'Contacts' screen.",
},
5000,
);
});
(this.$router as Router).push({ name: "contacts" });
} else {
this.$notify(
{
group: "alert",
type: "error",
title: "Error",
text: "No account was found for the active DID.",
},
5000,
);
}
}
}
</script>
Loading…
Cancel
Save