prompt for name when showing info, and provide a "copy" page when remote
This commit is contained in:
@@ -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"
|
||||
>
|
||||
Yes
|
||||
{{ notification.yesText ? ", " + notification.yesText : "" }}
|
||||
Yes{{
|
||||
notification.yesText ? ", " + notification.yesText : ""
|
||||
}}
|
||||
</button>
|
||||
|
||||
<button
|
||||
@@ -193,7 +194,7 @@
|
||||
"
|
||||
class="block w-full text-center text-md font-bold uppercase bg-yellow-600 text-white px-2 py-2 rounded-md mb-2"
|
||||
>
|
||||
No {{ notification.noText ? ", " + notification.noText : "" }}
|
||||
No{{ notification.noText ? ", " + notification.noText : "" }}
|
||||
</button>
|
||||
|
||||
<label
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
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"
|
||||
>
|
||||
Camera or Other?
|
||||
Add Photo
|
||||
</div>
|
||||
<div
|
||||
class="text-lg text-center px-2 py-0.5 leading-none absolute right-0 top-0 text-white"
|
||||
|
||||
96
src/components/UserNameDialog.vue
Normal file
96
src/components/UserNameDialog.vue
Normal file
@@ -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>
|
||||
@@ -49,8 +49,8 @@ export interface NotificationIface {
|
||||
title: string;
|
||||
text?: string;
|
||||
noText?: string;
|
||||
onCancel?: (stopAsking: boolean) => Promise<void>;
|
||||
onNo?: (stopAsking: boolean) => Promise<void>;
|
||||
onCancel?: (stopAsking?: boolean) => Promise<void>;
|
||||
onNo?: (stopAsking?: boolean) => Promise<void>;
|
||||
onYes?: () => Promise<void>;
|
||||
promptToStopAsking?: boolean;
|
||||
yesText?: string;
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import { Axios, AxiosRequestConfig, AxiosResponse } from "axios";
|
||||
import { Buffer } from "buffer";
|
||||
import { sha256 } from "ethereum-cryptography/sha256";
|
||||
import { LRUCache } from "lru-cache";
|
||||
import * as R from "ramda";
|
||||
|
||||
import { DEFAULT_IMAGE_API_SERVER } from "@/constants/app";
|
||||
import { Contact } from "@/db/tables/contacts";
|
||||
import { accessToken } from "@/libs/crypto";
|
||||
import { accessToken, deriveAddress, nextDerivationPath } from "@/libs/crypto";
|
||||
import { NonsensitiveDexie } from "@/db/index";
|
||||
import { getAccount, getPasskeyExpirationSeconds } from "@/libs/util";
|
||||
import { createEndorserJwtForKey, KeyMeta } from "@/libs/crypto/vc";
|
||||
import { Account } from "@/db/tables/accounts";
|
||||
|
||||
export const SCHEMA_ORG_CONTEXT = "https://schema.org";
|
||||
// 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(
|
||||
issuerDid: string,
|
||||
payload: object,
|
||||
|
||||
@@ -189,6 +189,11 @@ const routes: Array<RouteRecordRaw> = [
|
||||
name: "seed-backup",
|
||||
component: () => import("../views/SeedBackupView.vue"),
|
||||
},
|
||||
{
|
||||
path: "/share-my-contact-info",
|
||||
name: "share-my-contact-info",
|
||||
component: () => import("@/views/ShareMyContactInfoView.vue"),
|
||||
},
|
||||
{
|
||||
path: "/shared-photo",
|
||||
name: "shared-photo",
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
<!-- CONTENT -->
|
||||
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
|
||||
<!-- 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
|
||||
</h1>
|
||||
|
||||
<div class="flex justify-between">
|
||||
<div class="flex justify-between mb-2 mt-4">
|
||||
<span />
|
||||
<span class="whitespace-nowrap">
|
||||
<router-link
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<QuickNav selected="Profile"></QuickNav>
|
||||
<QuickNav selected="Profile" />
|
||||
<!-- CONTENT -->
|
||||
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
|
||||
<!-- Breadcrumb -->
|
||||
@@ -109,6 +109,7 @@ import {
|
||||
CONTACT_URL_PREFIX,
|
||||
createEndorserJwtForDid,
|
||||
ENDORSER_JWT_URL_LOCATION,
|
||||
generateEndorserJwtForAccount,
|
||||
isDid,
|
||||
register,
|
||||
setVisibilityUtil,
|
||||
@@ -157,7 +158,7 @@ export default class ContactQRScanShow extends Vue {
|
||||
own: {
|
||||
name:
|
||||
(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,
|
||||
profileImageUrl: settings?.profileImageUrl,
|
||||
registered: settings?.isRegistered,
|
||||
@@ -182,7 +183,18 @@ export default class ContactQRScanShow extends Vue {
|
||||
const vcJwt = await createEndorserJwtForDid(this.activeDid, contactInfo);
|
||||
|
||||
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,11 +4,11 @@
|
||||
|
||||
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
|
||||
<!-- 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
|
||||
</h1>
|
||||
|
||||
<div class="flex justify-between py-2">
|
||||
<div class="flex justify-between py-2 mt-8">
|
||||
<span />
|
||||
<span>
|
||||
<a
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
<template>
|
||||
<QuickNav selected="Discover"></QuickNav>
|
||||
<QuickNav selected="Discover" />
|
||||
<TopMessage />
|
||||
|
||||
<!-- CONTENT -->
|
||||
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
|
||||
<!-- 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
|
||||
</h1>
|
||||
|
||||
<!-- 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
|
||||
type="text"
|
||||
v-model="searchTerms"
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
|
||||
<!-- CONTENT -->
|
||||
<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 }}
|
||||
</h1>
|
||||
|
||||
<!-- prompt to install notifications, which we're making an advanced feature -->
|
||||
<div class="mb-8">
|
||||
<!-- prompt to install notifications with notificationsSupported, which we're making an advanced feature -->
|
||||
<div class="mb-8 mt-8">
|
||||
<div
|
||||
v-if="false"
|
||||
class="bg-amber-200 rounded-md overflow-hidden text-center px-4 py-3 mb-4"
|
||||
@@ -86,13 +86,14 @@
|
||||
>
|
||||
<!-- activeDid && !isRegistered -->
|
||||
To share, someone must register you.
|
||||
<router-link
|
||||
:to="{ name: 'contact-qr' }"
|
||||
<div
|
||||
@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"
|
||||
>
|
||||
Show Them {{ PASSKEYS_ENABLED ? "Default" : "Your" }} Identifier
|
||||
Info
|
||||
</router-link>
|
||||
</div>
|
||||
<UserNameDialog ref="userNameDialog" />
|
||||
<div v-if="PASSKEYS_ENABLED" class="flex justify-end w-full">
|
||||
<router-link
|
||||
:to="{ name: 'start' }"
|
||||
@@ -107,7 +108,7 @@
|
||||
<!-- activeDid && isRegistered -->
|
||||
|
||||
<!-- 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>
|
||||
<div class="flex justify-end">
|
||||
<button
|
||||
@@ -120,7 +121,7 @@
|
||||
</div>
|
||||
|
||||
<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()">
|
||||
<img
|
||||
@@ -169,7 +170,7 @@
|
||||
<FeedFilters ref="feedFilters" />
|
||||
|
||||
<!-- 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">
|
||||
<h2 class="text-xl font-bold">Latest Activity</h2>
|
||||
<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 QuickNav from "@/components/QuickNav.vue";
|
||||
import TopMessage from "@/components/TopMessage.vue";
|
||||
import UserNameDialog from "@/components/UserNameDialog.vue";
|
||||
import {
|
||||
AppString,
|
||||
NotificationIface,
|
||||
@@ -370,6 +372,7 @@ interface GiveRecordWithContactInfo extends GiveSummaryRecord {
|
||||
EntityIcon,
|
||||
InfiniteScroll,
|
||||
TopMessage,
|
||||
UserNameDialog,
|
||||
},
|
||||
})
|
||||
export default class HomeView extends Vue {
|
||||
@@ -426,6 +429,7 @@ export default class HomeView extends Vue {
|
||||
this.showShortcutBvc = !!settings?.showShortcutBvc;
|
||||
|
||||
this.isAnyFeedFilterOn = isAnyFeedFilterOn(settings);
|
||||
console.log("getting through mounted");
|
||||
|
||||
// someone may have have registered after sharing contact info, so recheck
|
||||
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
|
||||
await this.updateAllFeed();
|
||||
this.updateAllFeed();
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (err: any) {
|
||||
@@ -495,7 +499,7 @@ export default class HomeView extends Vue {
|
||||
|
||||
this.feedData = [];
|
||||
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.
|
||||
// One alternative is to totally separate the project link loading.
|
||||
if (payload && !this.isFeedLoading) {
|
||||
this.updateAllFeed();
|
||||
await this.updateAllFeed();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -527,6 +531,7 @@ export default class HomeView extends Vue {
|
||||
async updateAllFeed() {
|
||||
this.isFeedLoading = true;
|
||||
let endOfResults = true;
|
||||
console.log("about to retrieveGives");
|
||||
await this.retrieveGives(this.apiServer, this.feedPreviousOldestId)
|
||||
.then(async (results) => {
|
||||
if (results.data.length > 0) {
|
||||
@@ -620,7 +625,7 @@ export default class HomeView extends Vue {
|
||||
});
|
||||
if (this.feedData.length === 0 && !endOfResults) {
|
||||
// repeat until there's at least some data
|
||||
this.updateAllFeed();
|
||||
await this.updateAllFeed();
|
||||
}
|
||||
this.isFeedLoading = false;
|
||||
}
|
||||
@@ -770,5 +775,36 @@ export default class HomeView extends Vue {
|
||||
computeKnownPersonIconStyleClassNames(known: boolean) {
|
||||
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>
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
<template>
|
||||
<QuickNav selected="Projects"></QuickNav>
|
||||
<QuickNav selected="Projects" />
|
||||
<TopMessage />
|
||||
|
||||
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
|
||||
<!-- Heading -->
|
||||
<h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8">
|
||||
Your Ideas
|
||||
</h1>
|
||||
<h1 id="ViewHeading" class="text-4xl text-center font-light">Your Ideas</h1>
|
||||
|
||||
<!-- 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">
|
||||
<li>
|
||||
<a
|
||||
|
||||
106
src/views/ShareMyContactInfoView.vue
Normal file
106
src/views/ShareMyContactInfoView.vue
Normal file
@@ -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>
|
||||
Reference in New Issue
Block a user