forked from trent_larson/crowd-funder-for-time-pwa
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9bdd0743a3 |
22240
package-lock.json
generated
22240
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -52,6 +52,7 @@
|
|||||||
"vue": "^3.3.4",
|
"vue": "^3.3.4",
|
||||||
"vue-axios": "^3.5.2",
|
"vue-axios": "^3.5.2",
|
||||||
"vue-facing-decorator": "^2.1.20",
|
"vue-facing-decorator": "^2.1.20",
|
||||||
|
"vue-qrcode-reader": "^5.3.4",
|
||||||
"vue-router": "^4.2.3",
|
"vue-router": "^4.2.3",
|
||||||
"web-did-resolver": "^2.0.27"
|
"web-did-resolver": "^2.0.27"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ tasks:
|
|||||||
- 40 notifications :
|
- 40 notifications :
|
||||||
- push, where we trigger a ServiceWorker(?) in the app to reach out and check for new data assignee:matthew
|
- 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...
|
||||||
- 01 Replace Gifted/Give in ContactsView with GiftedDialog assignee:matthew
|
- 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
|
- 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
|
||||||
@@ -27,6 +28,7 @@ tasks:
|
|||||||
|
|
||||||
- 24 Move to Vite assignee:matthew
|
- 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
|
- .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 include the hash of the latest commit, and maybe a version
|
||||||
- .5 add link to further project / people when a project pays ahead
|
- .5 add link to further project / people when a project pays ahead
|
||||||
@@ -38,6 +40,7 @@ tasks:
|
|||||||
- .2 fix rate limit verbiage (with the new one-per-day allowance) assignee:trent
|
- .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
|
- .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"
|
- .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.
|
- Discuss whether the remaining tasks are worthwhile before MVP release.
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
/**
|
/**
|
||||||
* Generic strings that could be used throughout the app.
|
* Generic strings that could be used throughout the app.
|
||||||
|
*
|
||||||
|
* See also ../libs/veramo/setup.ts
|
||||||
*/
|
*/
|
||||||
export enum AppString {
|
export enum AppString {
|
||||||
APP_NAME = "Kick-Start with Time",
|
APP_NAME = "Time Safari",
|
||||||
|
|
||||||
PROD_ENDORSER_API_SERVER = "https://api.endorser.ch",
|
PROD_ENDORSER_API_SERVER = "https://api.endorser.ch",
|
||||||
TEST_ENDORSER_API_SERVER = "https://test-api.endorser.ch",
|
TEST_ENDORSER_API_SERVER = "https://test-api.endorser.ch",
|
||||||
@@ -10,3 +12,13 @@ export enum AppString {
|
|||||||
|
|
||||||
DEFAULT_ENDORSER_API_SERVER = TEST_ENDORSER_API_SERVER,
|
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 = {
|
export const ContactsSchema = {
|
||||||
contacts: "++did, name, publicKeyBase64, registered, seesMe",
|
contacts: "&did, name, publicKeyBase64, registered, seesMe",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { IIdentifier } from "@veramo/core";
|
import { IIdentifier } from "@veramo/core";
|
||||||
import { DEFAULT_DID_PROVIDER_NAME } from "../veramo/setup";
|
|
||||||
import { getRandomBytesSync } from "ethereum-cryptography/random";
|
import { getRandomBytesSync } from "ethereum-cryptography/random";
|
||||||
import { entropyToMnemonic } from "ethereum-cryptography/bip39";
|
import { entropyToMnemonic } from "ethereum-cryptography/bip39";
|
||||||
import { wordlist } from "ethereum-cryptography/bip39/wordlists/english";
|
import { wordlist } from "ethereum-cryptography/bip39/wordlists/english";
|
||||||
@@ -7,6 +6,9 @@ import { HDNode } from "@ethersproject/hdnode";
|
|||||||
import * as didJwt from "did-jwt";
|
import * as didJwt from "did-jwt";
|
||||||
import * as u8a from "uint8arrays";
|
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'";
|
export const DEFAULT_ROOT_DERIVATION_PATH = "m/76798669'/0'/0'/0'";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -150,3 +152,24 @@ export function fromJose(signature: string): {
|
|||||||
export function bytesToHex(b: Uint8Array): string {
|
export function bytesToHex(b: Uint8Array): string {
|
||||||
return u8a.toString(b, "base16");
|
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,7 +6,12 @@ import { Axios, AxiosResponse } from "axios";
|
|||||||
import { Contact } from "@/db/tables/contacts";
|
import { Contact } from "@/db/tables/contacts";
|
||||||
|
|
||||||
export const SCHEMA_ORG_CONTEXT = "https://schema.org";
|
export const SCHEMA_ORG_CONTEXT = "https://schema.org";
|
||||||
|
// the object in RegisterAction claims
|
||||||
export const SERVICE_ID = "endorser.ch";
|
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 {
|
export interface AgreeVerifiableCredential {
|
||||||
"@context": string;
|
"@context": string;
|
||||||
|
|||||||
@@ -1,151 +1,7 @@
|
|||||||
// Created from the setup in https://veramo.io/docs/guides/react_native
|
// see also ../constants/app.ts and
|
||||||
|
|
||||||
// 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) {
|
function didProviderName(netName: string) {
|
||||||
return "did:ethr" + (netName === "mainnet" ? "" : ":" + netName);
|
return "did:ethr" + (netName === "mainnet" ? "" : ":" + netName);
|
||||||
}
|
}
|
||||||
|
|
||||||
//const NETWORK_NAMES = ["mainnet", "rinkeby"];
|
export const DEFAULT_DID_PROVIDER_NAME = didProviderName("mainnet");
|
||||||
|
|
||||||
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 })
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import axios from "axios";
|
|||||||
import * as didJwt from "did-jwt";
|
import * as didJwt from "did-jwt";
|
||||||
import { AppString } from "@/constants/app";
|
import { AppString } from "@/constants/app";
|
||||||
import { db } from "../db";
|
import { db } from "../db";
|
||||||
import { SERVICE_ID } from "../libs/veramo/setup";
|
import { SERVICE_ID } from "../libs/endorserServer";
|
||||||
import { deriveAddress, newIdentifier } from "../libs/crypto";
|
import { deriveAddress, newIdentifier } from "../libs/crypto";
|
||||||
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<!-- CONTENT -->
|
<!-- CONTENT -->
|
||||||
<section id="Content" class="p-6 pb-24">
|
<section id="Content" class="p-6 pb-24">
|
||||||
<!-- 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 pt-4">
|
||||||
Your Contact Info
|
Your Contact Info
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
@@ -17,12 +17,17 @@
|
|||||||
:dotsOptions="{ type: 'square' }"
|
:dotsOptions="{ type: 'square' }"
|
||||||
class="flex justify-center"
|
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>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import QRCodeVue3 from "qr-code-generator-vue3";
|
import QRCodeVue3 from "qr-code-generator-vue3";
|
||||||
import { Component, Vue } from "vue-facing-decorator";
|
import { Component, Vue } from "vue-facing-decorator";
|
||||||
|
import { QrcodeStream } from "vue-qrcode-reader";
|
||||||
|
|
||||||
import { accountsDB, db } from "@/db/index";
|
import { accountsDB, db } from "@/db/index";
|
||||||
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
||||||
import * as R from "ramda";
|
import * as R from "ramda";
|
||||||
@@ -30,6 +35,10 @@ import { SimpleSigner } from "@/libs/crypto";
|
|||||||
import * as didJwt from "did-jwt";
|
import * as didJwt from "did-jwt";
|
||||||
import QuickNav from "@/components/QuickNav.vue";
|
import QuickNav from "@/components/QuickNav.vue";
|
||||||
import { Account } from "@/db/tables/accounts";
|
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
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const Buffer = require("buffer/").Buffer;
|
const Buffer = require("buffer/").Buffer;
|
||||||
@@ -43,6 +52,7 @@ interface Notification {
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: {
|
components: {
|
||||||
|
QrcodeStream,
|
||||||
QRCodeVue3,
|
QRCodeVue3,
|
||||||
QuickNav,
|
QuickNav,
|
||||||
},
|
},
|
||||||
@@ -112,9 +122,47 @@ export default class ContactQRScanShow extends Vue {
|
|||||||
issuer: identity.did,
|
issuer: identity.did,
|
||||||
signer: signer,
|
signer: signer,
|
||||||
});
|
});
|
||||||
const viewPrefix = "https://endorser.ch/contact?jwt=";
|
const viewPrefix = CONTACT_URL_PREFIX + ENDORSER_JWT_URL_LOCATION;
|
||||||
this.qrValue = viewPrefix + vcJwt;
|
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>
|
</script>
|
||||||
|
|||||||
@@ -20,6 +20,11 @@
|
|||||||
|
|
||||||
<!-- New Contact -->
|
<!-- New Contact -->
|
||||||
<div class="mb-4 flex">
|
<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
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="DID, Name, Public Key"
|
placeholder="DID, Name, Public Key"
|
||||||
@@ -211,11 +216,17 @@
|
|||||||
import { AxiosError } from "axios";
|
import { AxiosError } from "axios";
|
||||||
import * as didJwt from "did-jwt";
|
import * as didJwt from "did-jwt";
|
||||||
import * as R from "ramda";
|
import * as R from "ramda";
|
||||||
|
|
||||||
|
import { NotificationIface } from "@/constants/app";
|
||||||
import { IIdentifier } from "@veramo/core";
|
import { IIdentifier } from "@veramo/core";
|
||||||
import { accountsDB, db } from "@/db/index";
|
import { accountsDB, db } from "@/db/index";
|
||||||
import { Contact } from "@/db/tables/contacts";
|
import { Contact } from "@/db/tables/contacts";
|
||||||
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
||||||
import { accessToken, SimpleSigner } from "@/libs/crypto";
|
import {
|
||||||
|
accessToken,
|
||||||
|
getContactPayloadFromJwtUrl,
|
||||||
|
SimpleSigner,
|
||||||
|
} from "@/libs/crypto";
|
||||||
import {
|
import {
|
||||||
GiveServerRecord,
|
GiveServerRecord,
|
||||||
GiveVerifiableCredential,
|
GiveVerifiableCredential,
|
||||||
@@ -229,22 +240,16 @@ import EntityIcon from "@/components/EntityIcon.vue";
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const Buffer = require("buffer/").Buffer;
|
const Buffer = require("buffer/").Buffer;
|
||||||
|
|
||||||
interface Notification {
|
|
||||||
group: string;
|
|
||||||
type: string;
|
|
||||||
title: string;
|
|
||||||
text: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: { QuickNav, EntityIcon },
|
components: { QuickNav, EntityIcon },
|
||||||
})
|
})
|
||||||
export default class ContactsView extends Vue {
|
export default class ContactsView extends Vue {
|
||||||
$notify!: (notification: Notification, timeout?: number) => void;
|
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
||||||
|
|
||||||
activeDid = "";
|
activeDid = "";
|
||||||
apiServer = "";
|
apiServer = "";
|
||||||
contacts: Array<Contact> = [];
|
contacts: Array<Contact> = [];
|
||||||
|
contactEndorserUrl = localStorage.getItem("contactEndorserUrl") || "";
|
||||||
contactInput = "";
|
contactInput = "";
|
||||||
// { "did:...": concatenated-descriptions } entry for each contact
|
// { "did:...": concatenated-descriptions } entry for each contact
|
||||||
givenByMeDescriptions: Record<string, string> = {};
|
givenByMeDescriptions: Record<string, string> = {};
|
||||||
@@ -279,6 +284,12 @@ export default class ContactsView extends Vue {
|
|||||||
(a: Contact, b) => (a.name || "").localeCompare(b.name || ""),
|
(a: Contact, b) => (a.name || "").localeCompare(b.name || ""),
|
||||||
allContacts,
|
allContacts,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (this.contactEndorserUrl) {
|
||||||
|
await this.newContactFromScan(this.contactEndorserUrl);
|
||||||
|
localStorage.removeItem("contactEndorserUrl");
|
||||||
|
this.contactEndorserUrl = "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getIdentity(activeDid: string) {
|
public async getIdentity(activeDid: string) {
|
||||||
@@ -414,6 +425,18 @@ export default class ContactsView extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async onClickNewContact(): Promise<void> {
|
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 did = this.contactInput;
|
||||||
let name, publicKeyBase64;
|
let name, publicKeyBase64;
|
||||||
const commaPos1 = this.contactInput.indexOf(",");
|
const commaPos1 = this.contactInput.indexOf(",");
|
||||||
@@ -432,12 +455,74 @@ export default class ContactsView extends Vue {
|
|||||||
publicKeyBase64 = Buffer.from(publicKeyBase64, "hex").toString("base64");
|
publicKeyBase64 = Buffer.from(publicKeyBase64, "hex").toString("base64");
|
||||||
}
|
}
|
||||||
const newContact = { did, name, publicKeyBase64 };
|
const newContact = { did, name, publicKeyBase64 };
|
||||||
await db.contacts.add(newContact);
|
return this.addContact(newContact);
|
||||||
const allContacts = this.contacts.concat([newContact]);
|
}
|
||||||
this.contacts = R.sort(
|
|
||||||
(a: Contact, b) => (a.name || "").localeCompare(b.name || ""),
|
async newContactFromScan(url: string): Promise<void> {
|
||||||
allContacts,
|
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,
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteContact(contact: Contact) {
|
async deleteContact(contact: Contact) {
|
||||||
|
|||||||
Reference in New Issue
Block a user