forked from trent_larson/crowd-funder-for-time-pwa
Compare commits
1 Commits
qr-reader-
...
friend-tec
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7f15fde25 |
22240
package-lock.json
generated
22240
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -52,7 +52,6 @@
|
||||
"vue": "^3.3.4",
|
||||
"vue-axios": "^3.5.2",
|
||||
"vue-facing-decorator": "^2.1.20",
|
||||
"vue-qrcode-reader": "^5.3.4",
|
||||
"vue-router": "^4.2.3",
|
||||
"web-did-resolver": "^2.0.27"
|
||||
},
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
|
||||
tasks:
|
||||
|
||||
- 08 Scan QR code to import into contacts assignee:matthew
|
||||
- SEE - https://github.com/gruhn/vue-qrcode-reader
|
||||
|
||||
- in endorser-push-server - mount folder for persistent sqlite DB outside of container
|
||||
- test alerts on all pages -- or refactor to new "notify" (since AlertMessage refactoring may require a change, et. ContactQRScanShowView)
|
||||
- .2 bug - on contacts view, click on "to" & "from" and nothing happens
|
||||
- 40 notifications :
|
||||
- push, where we trigger a ServiceWorker(?) in the app to reach out and check for new data assignee:matthew
|
||||
|
||||
- .2 Rename repo to crowd-sourcing-for-time (because Kickstarter is a corporation)
|
||||
- 01 Replace Gifted/Give in ContactsView with GiftedDialog assignee:matthew
|
||||
|
||||
- 01 fix the Discovery map display to not show on top of bottom icons (and any other UI tweaks on the map flow) assignee-group:ui
|
||||
@@ -24,7 +26,6 @@ tasks:
|
||||
|
||||
- 24 Move to Vite assignee:matthew
|
||||
|
||||
- .5 Allow edit of a contact name (but not the DID)
|
||||
- .2 Edit Plan does not have icons across the bottom assignee-group:ui
|
||||
- .5 include the hash of the latest commit, and maybe a version
|
||||
- .5 add link to further project / people when a project pays ahead
|
||||
@@ -36,7 +37,6 @@ tasks:
|
||||
- .2 fix rate limit verbiage (with the new one-per-day allowance) assignee:trent
|
||||
- .1 remove the logic to exclude beforeId in list of plans after server has commit 26b25af605e715600d4f12b6416ed9fd7142d164
|
||||
- .2 in SeedBackupView, don't load the mnemonic and keep it in memory; only load it when they click "show"
|
||||
- .1 change default server in app.ts
|
||||
|
||||
- Discuss whether the remaining tasks are worthwhile before MVP release.
|
||||
|
||||
|
||||
69
src/App.vue
69
src/App.vue
@@ -246,6 +246,75 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="notification.type === 'pwa-install-gate-ios'"
|
||||
class="absolute inset-0 h-screen flex flex-col items-center justify-center bg-slate-900/50"
|
||||
>
|
||||
<div
|
||||
class="flex w-11/12 max-w-sm mx-auto mb-3 overflow-hidden bg-white rounded-lg shadow-lg"
|
||||
>
|
||||
<div class="w-full px-6 py-6 text-slate-900 text-center">
|
||||
<fa
|
||||
icon="mobile-screen-button"
|
||||
class="inline-block text-7xl text-slate-400 mb-4"
|
||||
>
|
||||
</fa>
|
||||
<h3 class="text-2xl font-semibold mb-4">Add to Home Screen</h3>
|
||||
<p class="text-md mb-4">
|
||||
To install the app, you need to add this website to your home
|
||||
screen.
|
||||
</p>
|
||||
<p class="text-md">
|
||||
In your Safari browser menu, tap the
|
||||
<span class="whitespace-nowrap">
|
||||
<fa
|
||||
icon="arrow-up-from-bracket"
|
||||
class="fa-fw text-slate-500 bg-slate-200 py-1 -my-1 px-0.5 rounded"
|
||||
>
|
||||
</fa>
|
||||
Share
|
||||
</span>
|
||||
icon and choose
|
||||
<b>Add to Home Screen</b> in the options. Then, open the Time
|
||||
Safari app on your home screen.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="notification.type === 'pwa-install-gate-android'"
|
||||
class="absolute inset-0 h-screen flex flex-col items-center justify-center bg-slate-900/50"
|
||||
>
|
||||
<div
|
||||
class="flex w-11/12 max-w-sm mx-auto mb-3 overflow-hidden bg-white rounded-lg shadow-lg"
|
||||
>
|
||||
<div class="w-full px-6 py-6 text-slate-900 text-center">
|
||||
<fa
|
||||
icon="mobile-screen-button"
|
||||
class="inline-block text-7xl text-slate-400 mb-4"
|
||||
>
|
||||
</fa>
|
||||
<h3 class="text-2xl font-semibold mb-4">Add to Home Screen</h3>
|
||||
<p class="text-md mb-4">
|
||||
To install the app, you need to add this website to your home
|
||||
screen.
|
||||
</p>
|
||||
<p class="text-md">
|
||||
In your Chrome browser menu, tap the
|
||||
<span class="whitespace-nowrap">
|
||||
<fa
|
||||
icon="ellipsis-vertical"
|
||||
class="fa-fw text-slate-500 bg-slate-200 py-1 -my-1 px-0.5 rounded"
|
||||
>
|
||||
</fa>
|
||||
More
|
||||
</span>
|
||||
button and choose
|
||||
<b>Install App</b> in the options.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Notification>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
/**
|
||||
* Generic strings that could be used throughout the app.
|
||||
*
|
||||
* See also ../libs/veramo/setup.ts
|
||||
*/
|
||||
export enum AppString {
|
||||
APP_NAME = "Time Safari",
|
||||
APP_NAME = "Kick-Start with Time",
|
||||
|
||||
PROD_ENDORSER_API_SERVER = "https://api.endorser.ch",
|
||||
TEST_ENDORSER_API_SERVER = "https://test-api.endorser.ch",
|
||||
@@ -12,13 +10,3 @@ export enum AppString {
|
||||
|
||||
DEFAULT_ENDORSER_API_SERVER = TEST_ENDORSER_API_SERVER,
|
||||
}
|
||||
|
||||
/**
|
||||
* See notiwind package
|
||||
*/
|
||||
export interface NotificationIface {
|
||||
group: string;
|
||||
type: string; // "toast" | "info" | "success" | "warning" | "danger"
|
||||
title: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
@@ -7,5 +7,5 @@ export interface Contact {
|
||||
}
|
||||
|
||||
export const ContactsSchema = {
|
||||
contacts: "&did, name, publicKeyBase64, registered, seesMe",
|
||||
contacts: "++did, name, publicKeyBase64, registered, seesMe",
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { IIdentifier } from "@veramo/core";
|
||||
import { DEFAULT_DID_PROVIDER_NAME } from "../veramo/setup";
|
||||
import { getRandomBytesSync } from "ethereum-cryptography/random";
|
||||
import { entropyToMnemonic } from "ethereum-cryptography/bip39";
|
||||
import { wordlist } from "ethereum-cryptography/bip39/wordlists/english";
|
||||
@@ -6,9 +7,6 @@ import { HDNode } from "@ethersproject/hdnode";
|
||||
import * as didJwt from "did-jwt";
|
||||
import * as u8a from "uint8arrays";
|
||||
|
||||
import { ENDORSER_JWT_URL_LOCATION } from "@/libs/endorserServer";
|
||||
import { DEFAULT_DID_PROVIDER_NAME } from "../veramo/setup";
|
||||
|
||||
export const DEFAULT_ROOT_DERIVATION_PATH = "m/76798669'/0'/0'/0'";
|
||||
|
||||
/**
|
||||
@@ -152,24 +150,3 @@ export function fromJose(signature: string): {
|
||||
export function bytesToHex(b: Uint8Array): string {
|
||||
return u8a.toString(b, "base16");
|
||||
}
|
||||
|
||||
/**
|
||||
@return results of uportJwtPayload:
|
||||
{ iat: number, iss: string (DID), own: { name, publicEncKey (base64-encoded key) } }
|
||||
|
||||
Note that similar code is also contained in time-safari
|
||||
*/
|
||||
export const getContactPayloadFromJwtUrl = (jwtUrlText: string) => {
|
||||
let jwtText = jwtUrlText;
|
||||
const endorserContextLoc = jwtText.indexOf(ENDORSER_JWT_URL_LOCATION);
|
||||
if (endorserContextLoc > -1) {
|
||||
jwtText = jwtText.substring(
|
||||
endorserContextLoc + ENDORSER_JWT_URL_LOCATION.length,
|
||||
);
|
||||
}
|
||||
|
||||
// JWT format: { header, payload, signature, data }
|
||||
const jwt = didJwt.decodeJWT(jwtText);
|
||||
|
||||
return jwt.payload;
|
||||
};
|
||||
|
||||
@@ -6,12 +6,7 @@ import { Axios, AxiosResponse } from "axios";
|
||||
import { Contact } from "@/db/tables/contacts";
|
||||
|
||||
export const SCHEMA_ORG_CONTEXT = "https://schema.org";
|
||||
// the object in RegisterAction claims
|
||||
export const SERVICE_ID = "endorser.ch";
|
||||
// the prefix for the contact URL
|
||||
export const CONTACT_URL_PREFIX = "https://endorser.ch";
|
||||
// the suffix for the contact URL
|
||||
export const ENDORSER_JWT_URL_LOCATION = "/contact?jwt=";
|
||||
|
||||
export interface AgreeVerifiableCredential {
|
||||
"@context": string;
|
||||
|
||||
@@ -1,7 +1,151 @@
|
||||
// see also ../constants/app.ts and
|
||||
// Created from the setup in https://veramo.io/docs/guides/react_native
|
||||
|
||||
// Core interfaces
|
||||
/* import {
|
||||
createAgent,
|
||||
IDIDManager,
|
||||
IResolver,
|
||||
IDataStore,
|
||||
IKeyManager,
|
||||
} from "@veramo/core";
|
||||
*/
|
||||
// Core identity manager plugin
|
||||
//import { DIDManager } from "@veramo/did-manager";
|
||||
|
||||
// Ethr did identity provider
|
||||
//import { EthrDIDProvider } from "@veramo/did-provider-ethr";
|
||||
|
||||
// Core key manager plugin
|
||||
//import { KeyManager } from "@veramo/key-manager";
|
||||
|
||||
// Custom key management system for RN
|
||||
//import { KeyManagementSystem } from '@veramo/kms-local-react-native'
|
||||
|
||||
// Custom resolver
|
||||
// Custom resolvers
|
||||
//import { DIDResolverPlugin } from "@veramo/did-resolver";
|
||||
/* import { Resolver } from "did-resolver";
|
||||
import { getResolver as ethrDidResolver } from "ethr-did-resolver";
|
||||
import { getResolver as webDidResolver } from "web-did-resolver";
|
||||
*/
|
||||
// for VCs and VPs https://veramo.io/docs/api/credential-w3c
|
||||
//import { CredentialIssuer } from '@veramo/credential-w3c'
|
||||
|
||||
// Storage plugin using TypeOrm
|
||||
/* import {
|
||||
Entities,
|
||||
KeyStore,
|
||||
DIDStore,
|
||||
IDataStoreORM,
|
||||
} from "@veramo/data-store";
|
||||
*/
|
||||
// TypeORM is installed with @veramo/typeorm
|
||||
//import { createConnection } from 'typeorm'
|
||||
|
||||
//import * as R from "ramda";
|
||||
|
||||
/*
|
||||
import { Contact } from '../entity/contact'
|
||||
import { Settings } from '../entity/settings'
|
||||
import { PrivateData } from '../entity/privateData'
|
||||
|
||||
import { Initial1616938713828 } from '../migration/1616938713828-initial'
|
||||
import { SettingsContacts1616967972293 } from '../migration/1616967972293-settings-contacts'
|
||||
import { EncryptedSeed1637856484788 } from '../migration/1637856484788-EncryptedSeed'
|
||||
import { HomeScreenConfig1639947962124 } from '../migration/1639947962124-HomeScreenConfig'
|
||||
import { HandlePublicKeys1652142819353 } from '../migration/1652142819353-HandlePublicKeys'
|
||||
import { LastClaimsSeen1656811846836 } from '../migration/1656811846836-LastClaimsSeen'
|
||||
import { ContactRegistered1662256903367 }from '../migration/1662256903367-ContactRegistered'
|
||||
import { PrivateData1663080623479 } from '../migration/1663080623479-PrivateData'
|
||||
|
||||
const ALL_ENTITIES = Entities.concat([Contact, Settings, PrivateData])
|
||||
|
||||
// Create react native DB connection configured by ormconfig.js
|
||||
|
||||
export const dbConnection = createConnection({
|
||||
database: 'endorser-mobile.sqlite',
|
||||
entities: ALL_ENTITIES,
|
||||
location: 'default',
|
||||
logging: ['error', 'info', 'warn'],
|
||||
migrations: [ Initial1616938713828, SettingsContacts1616967972293, EncryptedSeed1637856484788, HomeScreenConfig1639947962124, HandlePublicKeys1652142819353, LastClaimsSeen1656811846836, ContactRegistered1662256903367, PrivateData1663080623479 ],
|
||||
migrationsRun: true,
|
||||
type: 'react-native',
|
||||
})
|
||||
*/
|
||||
function didProviderName(netName: string) {
|
||||
return "did:ethr" + (netName === "mainnet" ? "" : ":" + netName);
|
||||
}
|
||||
|
||||
export const DEFAULT_DID_PROVIDER_NAME = didProviderName("mainnet");
|
||||
//const NETWORK_NAMES = ["mainnet", "rinkeby"];
|
||||
|
||||
const DEFAULT_DID_PROVIDER_NETWORK_NAME = "mainnet";
|
||||
|
||||
export const DEFAULT_DID_PROVIDER_NAME = didProviderName(
|
||||
DEFAULT_DID_PROVIDER_NETWORK_NAME,
|
||||
);
|
||||
|
||||
export const HANDY_APP = false;
|
||||
|
||||
// this is used as the object in RegisterAction claims
|
||||
export const SERVICE_ID = "endorser.ch";
|
||||
|
||||
//const INFURA_PROJECT_ID = "INFURA_PROJECT_ID";
|
||||
/*
|
||||
const providers = {}
|
||||
NETWORK_NAMES.forEach((networkName) => {
|
||||
providers[didProviderName(networkName)] = new EthrDIDProvider({
|
||||
defaultKms: 'local',
|
||||
network: networkName,
|
||||
rpcUrl: 'https://' + networkName + '.infura.io/v3/' + INFURA_PROJECT_ID,
|
||||
gas: 1000001,
|
||||
ttl: 60 * 60 * 24 * 30 * 12 + 1,
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
const didManager = new DIDManager({
|
||||
store: new DIDStore(dbConnection),
|
||||
defaultProvider: DEFAULT_DID_PROVIDER_NAME,
|
||||
providers: providers,
|
||||
})
|
||||
*/
|
||||
|
||||
/* const basicDidResolvers = NETWORK_NAMES.map((networkName) => [
|
||||
networkName,
|
||||
new Resolver({
|
||||
ethr: ethrDidResolver({
|
||||
networks: [
|
||||
{
|
||||
name: networkName,
|
||||
rpcUrl:
|
||||
"https://" + networkName + ".infura.io/v3/" + INFURA_PROJECT_ID,
|
||||
},
|
||||
],
|
||||
}).ethr,
|
||||
web: webDidResolver().web,
|
||||
}),
|
||||
]);
|
||||
|
||||
const basicResolverMap = R.fromPairs(basicDidResolvers)
|
||||
|
||||
export const DEFAULT_BASIC_RESOLVER = basicResolverMap[DEFAULT_DID_PROVIDER_NETWORK_NAME]
|
||||
|
||||
const agentDidResolvers = NETWORK_NAMES.map((networkName) => {
|
||||
return new DIDResolverPlugin({
|
||||
resolver: basicResolverMap[networkName],
|
||||
})
|
||||
})
|
||||
|
||||
let allPlugins = [
|
||||
new CredentialIssuer(),
|
||||
new KeyManager({
|
||||
store: new KeyStore(dbConnection),
|
||||
kms: {
|
||||
local: new KeyManagementSystem(),
|
||||
},
|
||||
}),
|
||||
didManager,
|
||||
].concat(agentDidResolvers)
|
||||
*/
|
||||
|
||||
//export const agent = createAgent<IDIDManager & IKeyManager & IDataStore & IDataStoreORM & IResolver>({ plugins: allPlugins })
|
||||
|
||||
@@ -13,6 +13,7 @@ import { library } from "@fortawesome/fontawesome-svg-core";
|
||||
import {
|
||||
faArrowLeft,
|
||||
faArrowRight,
|
||||
faArrowUpFromBracket,
|
||||
faBurst,
|
||||
faCalendar,
|
||||
faChevronLeft,
|
||||
@@ -39,6 +40,7 @@ import {
|
||||
faLongArrowAltLeft,
|
||||
faLongArrowAltRight,
|
||||
faMagnifyingGlass,
|
||||
faMobileScreenButton,
|
||||
faPen,
|
||||
faPersonCircleCheck,
|
||||
faPersonCircleQuestion,
|
||||
@@ -59,6 +61,7 @@ import {
|
||||
library.add(
|
||||
faArrowLeft,
|
||||
faArrowRight,
|
||||
faArrowUpFromBracket,
|
||||
faBurst,
|
||||
faCalendar,
|
||||
faChevronLeft,
|
||||
@@ -85,6 +88,7 @@ library.add(
|
||||
faLongArrowAltLeft,
|
||||
faLongArrowAltRight,
|
||||
faMagnifyingGlass,
|
||||
faMobileScreenButton,
|
||||
faPen,
|
||||
faPersonCircleCheck,
|
||||
faPersonCircleQuestion,
|
||||
|
||||
@@ -2,7 +2,7 @@ import axios from "axios";
|
||||
import * as didJwt from "did-jwt";
|
||||
import { AppString } from "@/constants/app";
|
||||
import { db } from "../db";
|
||||
import { SERVICE_ID } from "../libs/endorserServer";
|
||||
import { SERVICE_ID } from "../libs/veramo/setup";
|
||||
import { deriveAddress, newIdentifier } from "../libs/crypto";
|
||||
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<!-- CONTENT -->
|
||||
<section id="Content" class="p-6 pb-24">
|
||||
<!-- Heading -->
|
||||
<h1 id="ViewHeading" class="text-4xl text-center font-light pt-4">
|
||||
<h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8">
|
||||
Your Contact Info
|
||||
</h1>
|
||||
|
||||
@@ -17,17 +17,12 @@
|
||||
:dotsOptions="{ type: 'square' }"
|
||||
class="flex justify-center"
|
||||
/>
|
||||
|
||||
<h1 class="text-4xl text-center font-light pt-4">Scan Contact Info</h1>
|
||||
<qrcode-stream @detect="onScanDetect" @error="onScanError" />
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import QRCodeVue3 from "qr-code-generator-vue3";
|
||||
import { Component, Vue } from "vue-facing-decorator";
|
||||
import { QrcodeStream } from "vue-qrcode-reader";
|
||||
|
||||
import { accountsDB, db } from "@/db/index";
|
||||
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
||||
import * as R from "ramda";
|
||||
@@ -35,10 +30,6 @@ import { SimpleSigner } from "@/libs/crypto";
|
||||
import * as didJwt from "did-jwt";
|
||||
import QuickNav from "@/components/QuickNav.vue";
|
||||
import { Account } from "@/db/tables/accounts";
|
||||
import {
|
||||
CONTACT_URL_PREFIX,
|
||||
ENDORSER_JWT_URL_LOCATION,
|
||||
} from "@/libs/endorserServer";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const Buffer = require("buffer/").Buffer;
|
||||
@@ -52,7 +43,6 @@ interface Notification {
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
QrcodeStream,
|
||||
QRCodeVue3,
|
||||
QuickNav,
|
||||
},
|
||||
@@ -122,47 +112,9 @@ export default class ContactQRScanShow extends Vue {
|
||||
issuer: identity.did,
|
||||
signer: signer,
|
||||
});
|
||||
const viewPrefix = CONTACT_URL_PREFIX + ENDORSER_JWT_URL_LOCATION;
|
||||
const viewPrefix = "https://endorser.ch/contact?jwt=";
|
||||
this.qrValue = viewPrefix + vcJwt;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param content is the result of a QR scan, an array with one item with a rawValue property
|
||||
*/
|
||||
// Unfortunately, there are not typescript definitions for the qrcode-stream component yet.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
onScanDetect(content: any) {
|
||||
if (content[0]?.rawValue) {
|
||||
console.log("onDetect", content[0].rawValue);
|
||||
localStorage.setItem("contactEndorserUrl", content[0].rawValue);
|
||||
this.$router.push({ name: "contacts" });
|
||||
} else {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "warning",
|
||||
title: "Invalid Contact QR Code",
|
||||
text: "No QR code detected with contact information.",
|
||||
},
|
||||
-1,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
onScanError(error: any) {
|
||||
console.log("Scan was invalid:", error);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "warning",
|
||||
title: "Invalid Scan",
|
||||
text: "The scan was invalid.",
|
||||
},
|
||||
-1,
|
||||
);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -20,11 +20,6 @@
|
||||
|
||||
<!-- New Contact -->
|
||||
<div class="mb-4 flex">
|
||||
<span class="self-center bg-slate-500 text-white px-1.5 py-1 rounded-md">
|
||||
<router-link :to="{ name: 'contact-qr' }">
|
||||
<fa icon="qrcode" class="fa-fw" />
|
||||
</router-link>
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="DID, Name, Public Key"
|
||||
@@ -216,17 +211,11 @@
|
||||
import { AxiosError } from "axios";
|
||||
import * as didJwt from "did-jwt";
|
||||
import * as R from "ramda";
|
||||
|
||||
import { NotificationIface } from "@/constants/app";
|
||||
import { IIdentifier } from "@veramo/core";
|
||||
import { accountsDB, db } from "@/db/index";
|
||||
import { Contact } from "@/db/tables/contacts";
|
||||
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
||||
import {
|
||||
accessToken,
|
||||
getContactPayloadFromJwtUrl,
|
||||
SimpleSigner,
|
||||
} from "@/libs/crypto";
|
||||
import { accessToken, SimpleSigner } from "@/libs/crypto";
|
||||
import {
|
||||
GiveServerRecord,
|
||||
GiveVerifiableCredential,
|
||||
@@ -240,16 +229,22 @@ import EntityIcon from "@/components/EntityIcon.vue";
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const Buffer = require("buffer/").Buffer;
|
||||
|
||||
interface Notification {
|
||||
group: string;
|
||||
type: string;
|
||||
title: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
components: { QuickNav, EntityIcon },
|
||||
})
|
||||
export default class ContactsView extends Vue {
|
||||
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
||||
$notify!: (notification: Notification, timeout?: number) => void;
|
||||
|
||||
activeDid = "";
|
||||
apiServer = "";
|
||||
contacts: Array<Contact> = [];
|
||||
contactEndorserUrl = localStorage.getItem("contactEndorserUrl") || "";
|
||||
contactInput = "";
|
||||
// { "did:...": concatenated-descriptions } entry for each contact
|
||||
givenByMeDescriptions: Record<string, string> = {};
|
||||
@@ -284,12 +279,6 @@ export default class ContactsView extends Vue {
|
||||
(a: Contact, b) => (a.name || "").localeCompare(b.name || ""),
|
||||
allContacts,
|
||||
);
|
||||
|
||||
if (this.contactEndorserUrl) {
|
||||
await this.newContactFromScan(this.contactEndorserUrl);
|
||||
localStorage.removeItem("contactEndorserUrl");
|
||||
this.contactEndorserUrl = "";
|
||||
}
|
||||
}
|
||||
|
||||
public async getIdentity(activeDid: string) {
|
||||
@@ -425,18 +414,6 @@ export default class ContactsView extends Vue {
|
||||
}
|
||||
|
||||
async onClickNewContact(): Promise<void> {
|
||||
if (!this.contactInput) {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "warning",
|
||||
title: "No Contact",
|
||||
text: "There was no contact info to add.",
|
||||
},
|
||||
-1,
|
||||
);
|
||||
return;
|
||||
}
|
||||
let did = this.contactInput;
|
||||
let name, publicKeyBase64;
|
||||
const commaPos1 = this.contactInput.indexOf(",");
|
||||
@@ -455,74 +432,12 @@ export default class ContactsView extends Vue {
|
||||
publicKeyBase64 = Buffer.from(publicKeyBase64, "hex").toString("base64");
|
||||
}
|
||||
const newContact = { did, name, publicKeyBase64 };
|
||||
return this.addContact(newContact);
|
||||
}
|
||||
|
||||
async newContactFromScan(url: string): Promise<void> {
|
||||
const payload = getContactPayloadFromJwtUrl(url);
|
||||
if (!payload) {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "No Contact Info",
|
||||
text: "The contact info could not be parsed.",
|
||||
},
|
||||
-1,
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
return this.addContact({
|
||||
did: payload.iss,
|
||||
name: payload.own.name,
|
||||
publicKeyBase64: payload.own.publicEncKey,
|
||||
} as Contact);
|
||||
}
|
||||
}
|
||||
|
||||
async addContact(newContact: Contact) {
|
||||
if (!newContact.did) {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Incomplete Contact",
|
||||
text: "Cannot add a contact without a DID.",
|
||||
},
|
||||
-1,
|
||||
);
|
||||
return;
|
||||
}
|
||||
return db.contacts
|
||||
.add(newContact)
|
||||
.then(() => {
|
||||
const allContacts = this.contacts.concat([newContact]);
|
||||
this.contacts = R.sort(
|
||||
(a: Contact, b) => (a.name || "").localeCompare(b.name || ""),
|
||||
allContacts,
|
||||
);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "success",
|
||||
title: "Contact added",
|
||||
text: newContact.name + " was added.",
|
||||
},
|
||||
-1,
|
||||
);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Error when adding contact to storage:", err);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Contact Not Added",
|
||||
text: "An error prevented importing.",
|
||||
},
|
||||
-1,
|
||||
);
|
||||
});
|
||||
await db.contacts.add(newContact);
|
||||
const allContacts = this.contacts.concat([newContact]);
|
||||
this.contacts = R.sort(
|
||||
(a: Contact, b) => (a.name || "").localeCompare(b.name || ""),
|
||||
allContacts,
|
||||
);
|
||||
}
|
||||
|
||||
async deleteContact(contact: Contact) {
|
||||
|
||||
Reference in New Issue
Block a user