New branch for cleanup and web push #65
177
sample.txt
Normal file
177
sample.txt
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
|
||||||
|
> kickstart-for-time-pwa@0.1.0 build
|
||||||
|
> vue-cli-service build
|
||||||
|
|
||||||
|
All browser targets in the browserslist configuration have supported ES module.
|
||||||
|
Therefore we don't build two separate bundles for differential loading.
|
||||||
|
|
||||||
|
|
||||||
|
WARNING Compiled with 5 warnings6:06:43 PM
|
||||||
|
|
||||||
|
[eslint]
|
||||||
|
/home/matthew/projects/kick-starter-for-time-pwa/src/components/World/components/objects/landmarks.js
|
||||||
|
98:11 warning Unexpected console statement no-console
|
||||||
|
133:7 warning Unexpected console statement no-console
|
||||||
|
144:5 warning Unexpected console statement no-console
|
||||||
|
|
||||||
|
/home/matthew/projects/kick-starter-for-time-pwa/src/router/index.ts
|
||||||
|
210:3 warning Unexpected console statement no-console
|
||||||
|
|
||||||
|
/home/matthew/projects/kick-starter-for-time-pwa/src/views/AccountViewView.vue
|
||||||
|
362:7 warning Unexpected console statement no-console
|
||||||
|
375:7 warning Unexpected console statement no-console
|
||||||
|
404:7 warning Unexpected console statement no-console
|
||||||
|
516:7 warning Unexpected console statement no-console
|
||||||
|
536:7 warning Unexpected console statement no-console
|
||||||
|
630:5 warning Unexpected console statement no-console
|
||||||
|
682:7 warning Unexpected console statement no-console
|
||||||
|
|
||||||
|
/home/matthew/projects/kick-starter-for-time-pwa/src/views/ContactAmountsView.vue
|
||||||
|
206:9 warning Unexpected console statement no-console
|
||||||
|
233:9 warning Unexpected console statement no-console
|
||||||
|
|
||||||
|
/home/matthew/projects/kick-starter-for-time-pwa/src/views/ContactGiftingView.vue
|
||||||
|
244:9 warning Unexpected console statement no-console
|
||||||
|
267:7 warning Unexpected console statement no-console
|
||||||
|
|
||||||
|
/home/matthew/projects/kick-starter-for-time-pwa/src/views/ContactsView.vue
|
||||||
|
340:9 warning Unexpected console statement no-console
|
||||||
|
577:9 warning Unexpected console statement no-console
|
||||||
|
|
||||||
|
/home/matthew/projects/kick-starter-for-time-pwa/src/views/DiscoverView.vue
|
||||||
|
315:9 warning Unexpected console statement no-console
|
||||||
|
343:7 warning Unexpected console statement no-console
|
||||||
|
390:9 warning Unexpected console statement no-console
|
||||||
|
423:7 warning Unexpected console statement no-console
|
||||||
|
532:9 warning Unexpected console statement no-console
|
||||||
|
575:7 warning Unexpected console statement no-console
|
||||||
|
|
||||||
|
/home/matthew/projects/kick-starter-for-time-pwa/src/views/HomeView.vue
|
||||||
|
349:9 warning Unexpected console statement no-console
|
||||||
|
498:9 warning Unexpected console statement no-console
|
||||||
|
521:7 warning Unexpected console statement no-console
|
||||||
|
|
||||||
|
/home/matthew/projects/kick-starter-for-time-pwa/src/views/IdentitySwitcherView.vue
|
||||||
|
142:7 warning Unexpected console statement no-console
|
||||||
|
|
||||||
|
/home/matthew/projects/kick-starter-for-time-pwa/src/views/ImportAccountView.vue
|
||||||
|
123:9 warning Unexpected console statement no-console
|
||||||
|
|
||||||
|
/home/matthew/projects/kick-starter-for-time-pwa/src/views/ImportDerivedAccountView.vue
|
||||||
|
159:7 warning Unexpected console statement no-console
|
||||||
|
|
||||||
|
/home/matthew/projects/kick-starter-for-time-pwa/src/views/NewEditProjectView.vue
|
||||||
|
183:9 warning Unexpected console statement no-console
|
||||||
|
215:7 warning Unexpected console statement no-console
|
||||||
|
297:13 warning Unexpected console statement no-console
|
||||||
|
320:11 warning Unexpected console statement no-console
|
||||||
|
345:7 warning Unexpected console statement no-console
|
||||||
|
|
||||||
|
/home/matthew/projects/kick-starter-for-time-pwa/src/views/ProjectViewView.vue
|
||||||
|
387:9 warning Unexpected console statement no-console
|
||||||
|
421:7 warning Unexpected console statement no-console
|
||||||
|
457:7 warning Unexpected console statement no-console
|
||||||
|
552:9 warning Unexpected console statement no-console
|
||||||
|
554:11 warning Unexpected console statement no-console
|
||||||
|
|
||||||
|
/home/matthew/projects/kick-starter-for-time-pwa/src/views/ProjectsView.vue
|
||||||
|
131:9 warning Unexpected console statement no-console
|
||||||
|
144:7 warning Unexpected console statement no-console
|
||||||
|
221:9 warning Unexpected console statement no-console
|
||||||
|
237:7 warning Unexpected console statement no-console
|
||||||
|
|
||||||
|
/home/matthew/projects/kick-starter-for-time-pwa/src/views/SeedBackupView.vue
|
||||||
|
94:7 warning Unexpected console statement no-console
|
||||||
|
|
||||||
|
✖ 44 problems (0 errors, 44 warnings)
|
||||||
|
|
||||||
|
|
||||||
|
You may use special comments to disable some warnings.
|
||||||
|
Use // eslint-disable-next-line to ignore the next line.
|
||||||
|
Use /* eslint-disable */ to ignore all warnings in a file.
|
||||||
|
warning
|
||||||
|
|
||||||
|
/models/lupine_plant/textures/lambert2SG_baseColor.png is 3.75 MB, and won't be precached. Configure maximumFileSizeToCacheInBytes to change this limit.
|
||||||
|
|
||||||
|
warning
|
||||||
|
|
||||||
|
/models/lupine_plant/textures/lambert2SG_normal.png is 4.91 MB, and won't be precached. Configure maximumFileSizeToCacheInBytes to change this limit.
|
||||||
|
|
||||||
|
warning
|
||||||
|
|
||||||
|
asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
|
||||||
|
This can impact web performance.
|
||||||
|
Assets:
|
||||||
|
js/project.44f30c9f.js (318 KiB)
|
||||||
|
js/statistics.8a97010a.js (586 KiB)
|
||||||
|
js/chunk-vendors.a4845bfb.js (411 KiB)
|
||||||
|
js/705.f6a6ce2a.js (252 KiB)
|
||||||
|
img/textures/leafy-autumn-forest-floor.jpg (705 KiB)
|
||||||
|
models/lupine_plant/textures/lambert2SG_baseColor.png (3.58 MiB)
|
||||||
|
models/lupine_plant/textures/lambert2SG_normal.png (4.69 MiB)
|
||||||
|
|
||||||
|
warning
|
||||||
|
|
||||||
|
entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance.
|
||||||
|
Entrypoints:
|
||||||
|
app (447 KiB)
|
||||||
|
js/chunk-vendors.a4845bfb.js
|
||||||
|
css/app.8f21529c.css
|
||||||
|
js/app.8833cebc.js
|
||||||
|
|
||||||
|
|
||||||
|
File Size Gzipped
|
||||||
|
|
||||||
|
dist/js/statistics.8a97010a.js 585.72 KiB 148.80 KiB
|
||||||
|
dist/js/chunk-vendors.a4845bfb.js 411.44 KiB 137.82 KiB
|
||||||
|
dist/js/project.44f30c9f.js 317.61 KiB 78.67 KiB
|
||||||
|
dist/js/705.f6a6ce2a.js 251.66 KiB 87.12 KiB
|
||||||
|
dist/js/891.33615e4f.js 147.32 KiB 42.09 KiB
|
||||||
|
dist/js/153.e2c8e249.js 146.26 KiB 42.21 KiB
|
||||||
|
dist/js/820.13565d16.js 66.10 KiB 18.33 KiB
|
||||||
|
dist/js/contact-qr.e170ec33.js 54.85 KiB 15.63 KiB
|
||||||
|
dist/js/772.7b4c53a7.js 30.29 KiB 7.21 KiB
|
||||||
|
dist/js/361.898a4525.js 27.40 KiB 8.19 KiB
|
||||||
|
dist/js/account.77d86130.js 17.51 KiB 5.93 KiB
|
||||||
|
dist/js/app.8833cebc.js 17.31 KiB 5.84 KiB
|
||||||
|
dist/js/contacts.3fc90ff8.js 16.94 KiB 5.52 KiB
|
||||||
|
dist/js/discover.24106939.js 15.30 KiB 5.22 KiB
|
||||||
|
dist/js/536.3bb13201.js 15.23 KiB 4.84 KiB
|
||||||
|
dist/workbox-5b385ed2.js 14.11 KiB 4.93 KiB
|
||||||
|
dist/js/home.218b99dd.js 13.89 KiB 4.97 KiB
|
||||||
|
dist/js/help.50d3117b.js 12.49 KiB 4.38 KiB
|
||||||
|
dist/js/projects.417a6cb7.js 8.71 KiB 3.00 KiB
|
||||||
|
dist/js/contact-amounts.a32b0ccd.js 8.44 KiB 3.25 KiB
|
||||||
|
dist/js/229.120e09bf.js 7.99 KiB 2.72 KiB
|
||||||
|
dist/js/identity-switcher.c7937333.js 7.44 KiB 2.52 KiB
|
||||||
|
dist/js/new-edit-project.0552181b.js 7.36 KiB 3.11 KiB
|
||||||
|
dist/js/300.dcaeb2a3.js 6.56 KiB 3.24 KiB
|
||||||
|
dist/js/seed-backup.76a0f7b3.js 3.99 KiB 1.97 KiB
|
||||||
|
dist/js/import-derive.c688d4b8.js 3.81 KiB 1.82 KiB
|
||||||
|
dist/js/import-account.c3fa35fd.js 3.54 KiB 1.66 KiB
|
||||||
|
dist/js/new-edit-account.bb763be2.js 3.39 KiB 1.51 KiB
|
||||||
|
dist/js/431.5a6d64e0.js 3.38 KiB 2.56 KiB
|
||||||
|
dist/service-worker.js 3.37 KiB 1.38 KiB
|
||||||
|
dist/js/scan-contact.46be989a.js 2.79 KiB 1.18 KiB
|
||||||
|
dist/js/start.091a7740.js 2.70 KiB 1.30 KiB
|
||||||
|
dist/js/new-identifier.bb379420.js 2.12 KiB 1.18 KiB
|
||||||
|
dist/js/93.b873dbbf.js 2.08 KiB 1.61 KiB
|
||||||
|
dist/js/new-edit-commitment.9248d367.j 1.96 KiB 1.05 KiB
|
||||||
|
s
|
||||||
|
dist/js/confirm-contact.02004d1d.js 1.89 KiB 1.04 KiB
|
||||||
|
dist/js/858.ae4c08ec.js 0.97 KiB 0.78 KiB
|
||||||
|
dist/css/app.8f21529c.css 18.41 KiB 4.39 KiB
|
||||||
|
dist/css/discover.73ee9bd3.css 14.77 KiB 6.25 KiB
|
||||||
|
dist/css/new-edit-project.73ee9bd3.css 14.77 KiB 6.25 KiB
|
||||||
|
dist/css/contacts.abb5e493.css 0.40 KiB 0.23 KiB
|
||||||
|
dist/css/contact-amounts.5b26ccd4.css 0.31 KiB 0.20 KiB
|
||||||
|
dist/css/home.828bc66e.css 0.25 KiB 0.19 KiB
|
||||||
|
dist/css/project.828bc66e.css 0.25 KiB 0.19 KiB
|
||||||
|
dist/css/statistics.828bc66e.css 0.25 KiB 0.19 KiB
|
||||||
|
|
||||||
|
Images and other types of assets omitted.
|
||||||
|
Build at: 2023-09-07T10:06:43.972Z - Hash: 2b39fcd4d0e78263 - Time: 32016ms
|
||||||
|
|
||||||
|
DONE Build complete. The dist directory is ready to be deployed.
|
||||||
|
INFO Check out deployment instructions at https://cli.vuejs.org/guide/deployment.html
|
||||||
|
|
||||||
@@ -1,17 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* Represents an account stored in the database.
|
||||||
|
*/
|
||||||
export type Account = {
|
export type Account = {
|
||||||
id?: number; // auto-generated by Dexie
|
/**
|
||||||
|
* Auto-generated ID by Dexie.
|
||||||
|
*/
|
||||||
|
id?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The date the account was created.
|
||||||
|
*/
|
||||||
dateCreated: string;
|
dateCreated: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The derivation path for the account.
|
||||||
|
*/
|
||||||
derivationPath: string;
|
derivationPath: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decentralized Identifier (DID) for the account.
|
||||||
|
*/
|
||||||
did: string;
|
did: string;
|
||||||
// stringified JSON containing underlying key material of type IIdentifier
|
|
||||||
// https://github.com/uport-project/veramo/blob/next/packages/core-types/src/types/IIdentifier.ts
|
/**
|
||||||
|
* Stringified JSON containing underlying key material.
|
||||||
|
* Based on the IIdentifier type from Veramo.
|
||||||
|
* @see {@link https://github.com/uport-project/veramo/blob/next/packages/core-types/src/types/IIdentifier.ts}
|
||||||
|
*/
|
||||||
identity: string;
|
identity: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The public key in hexadecimal format.
|
||||||
|
*/
|
||||||
publicKeyHex: string;
|
publicKeyHex: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The mnemonic passphrase for the account.
|
||||||
|
*/
|
||||||
mnemonic: string;
|
mnemonic: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
// mark encrypted field by starting with a $ character
|
/**
|
||||||
// see https://github.com/PVermeer/dexie-addon-suite-monorepo/tree/master/packages/dexie-encrypted-addon
|
* Schema for the accounts table in the database.
|
||||||
|
* Fields starting with a $ character are encrypted.
|
||||||
|
* @see {@link https://github.com/PVermeer/dexie-addon-suite-monorepo/tree/master/packages/dexie-encrypted-addon}
|
||||||
|
*/
|
||||||
export const AccountsSchema = {
|
export const AccountsSchema = {
|
||||||
accounts:
|
accounts:
|
||||||
"++id, dateCreated, derivationPath, did, $identity, $mnemonic, publicKeyHex",
|
"++id, dateCreated, derivationPath, did, $identity, $mnemonic, publicKeyHex",
|
||||||
|
|||||||
@@ -115,24 +115,20 @@ export function isHiddenDid(did: string) {
|
|||||||
export function didInfo(
|
export function didInfo(
|
||||||
did: string,
|
did: string,
|
||||||
activeDid: string,
|
activeDid: string,
|
||||||
allMyDids: Array<string>,
|
allMyDids: string[],
|
||||||
contacts: Array<Contact>,
|
contacts: Contact[],
|
||||||
): string {
|
): string {
|
||||||
const myId: string | undefined = R.find(R.equals(did), allMyDids);
|
const myId = R.find(R.equals(did), allMyDids);
|
||||||
if (myId) {
|
if (myId) return `You${myId !== activeDid ? " (Alt ID)" : ""}`;
|
||||||
return "You" + (myId !== activeDid ? " (Alt ID)" : "");
|
|
||||||
} else {
|
const contact = R.find((c) => c.did === did, contacts);
|
||||||
const contact: Contact | undefined = R.find((c) => c.did === did, contacts);
|
return contact
|
||||||
if (contact) {
|
? contact.name || "Someone Unnamed in Contacts"
|
||||||
return contact.name || "Someone Unnamed in Contacts";
|
: !did
|
||||||
} else if (!did) {
|
? "Unspecified Person"
|
||||||
return "Unspecified Person";
|
: isHiddenDid(did)
|
||||||
} else if (isHiddenDid(did)) {
|
? "Someone Not In Network"
|
||||||
return "Someone Not In Network";
|
: "Someone Not In Contacts";
|
||||||
} else {
|
|
||||||
return "Someone Not In Contacts";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ResultWithType {
|
export interface ResultWithType {
|
||||||
@@ -171,30 +167,18 @@ export async function createAndSubmitGive(
|
|||||||
fulfillsProjectHandleId?: string,
|
fulfillsProjectHandleId?: string,
|
||||||
): Promise<CreateAndSubmitGiveResult> {
|
): Promise<CreateAndSubmitGiveResult> {
|
||||||
try {
|
try {
|
||||||
// Make a claim
|
|
||||||
const vcClaim: GiveVerifiableCredential = {
|
const vcClaim: GiveVerifiableCredential = {
|
||||||
"@context": "https://schema.org",
|
"@context": "https://schema.org",
|
||||||
"@type": "GiveAction",
|
"@type": "GiveAction",
|
||||||
|
recipient: toDid ? { identifier: toDid } : undefined,
|
||||||
|
agent: fromDid ? { identifier: fromDid } : undefined,
|
||||||
|
description: description || undefined,
|
||||||
|
object: hours ? { amountOfThisGood: hours, unitCode: "HUR" } : undefined,
|
||||||
|
fulfills: fulfillsProjectHandleId
|
||||||
|
? { "@type": "PlanAction", identifier: fulfillsProjectHandleId }
|
||||||
|
: undefined,
|
||||||
};
|
};
|
||||||
if (toDid) {
|
|
||||||
vcClaim.recipient = { identifier: toDid };
|
|
||||||
}
|
|
||||||
if (fromDid) {
|
|
||||||
vcClaim.agent = { identifier: fromDid };
|
|
||||||
}
|
|
||||||
if (description) {
|
|
||||||
vcClaim.description = description;
|
|
||||||
}
|
|
||||||
if (hours) {
|
|
||||||
vcClaim.object = { amountOfThisGood: hours, unitCode: "HUR" };
|
|
||||||
}
|
|
||||||
if (fulfillsProjectHandleId) {
|
|
||||||
vcClaim.fulfills = {
|
|
||||||
"@type": "PlanAction",
|
|
||||||
identifier: fulfillsProjectHandleId,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// Make a payload for the claim
|
|
||||||
const vcPayload = {
|
const vcPayload = {
|
||||||
vc: {
|
vc: {
|
||||||
"@context": ["https://www.w3.org/2018/credentials/v1"],
|
"@context": ["https://www.w3.org/2018/credentials/v1"],
|
||||||
@@ -205,14 +189,7 @@ export async function createAndSubmitGive(
|
|||||||
|
|
||||||
// Create a signature using private key of identity
|
// Create a signature using private key of identity
|
||||||
const firstKey = identity.keys[0];
|
const firstKey = identity.keys[0];
|
||||||
if (!firstKey || !firstKey.privateKeyHex) {
|
const privateKeyHex = firstKey?.privateKeyHex;
|
||||||
throw {
|
|
||||||
error: "No private key",
|
|
||||||
message: `Your identifier ${identity.did} is not configured correctly. Use a different identifier.`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const privateKeyHex = firstKey.privateKeyHex;
|
|
||||||
|
|
||||||
if (!privateKeyHex) {
|
if (!privateKeyHex) {
|
||||||
throw {
|
throw {
|
||||||
@@ -222,48 +199,35 @@ export async function createAndSubmitGive(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const signer = await SimpleSigner(privateKeyHex);
|
const signer = await SimpleSigner(privateKeyHex);
|
||||||
const alg = undefined;
|
|
||||||
|
|
||||||
// Create a JWT for the request
|
// Create a JWT for the request
|
||||||
|
|
||||||
const vcJwt: string = await didJwt.createJWT(vcPayload, {
|
const vcJwt: string = await didJwt.createJWT(vcPayload, {
|
||||||
alg: alg,
|
|
||||||
issuer: identity.did,
|
issuer: identity.did,
|
||||||
signer: signer,
|
signer,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Make the xhr request payload
|
// Make the xhr request payload
|
||||||
|
|
||||||
const payload = JSON.stringify({ jwtEncoded: vcJwt });
|
const payload = JSON.stringify({ jwtEncoded: vcJwt });
|
||||||
const url = apiServer + "/api/v2/claim";
|
const url = `${apiServer}/api/v2/claim`;
|
||||||
const token = await accessToken(identity);
|
const token = await accessToken(identity);
|
||||||
const headers = {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
Authorization: "Bearer " + token,
|
|
||||||
};
|
|
||||||
|
|
||||||
const response = await axios.post(url, payload, { headers });
|
const response = await axios.post(url, payload, {
|
||||||
return {
|
headers: {
|
||||||
type: "success",
|
"Content-Type": "application/json",
|
||||||
response,
|
Authorization: `Bearer ${token}`,
|
||||||
};
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { type: "success", response };
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
let errorMessage: string;
|
const errorMessage: string =
|
||||||
|
error === null
|
||||||
if (error instanceof Error) {
|
? "Null error"
|
||||||
// If it's a JavaScript Error object
|
: error instanceof Error
|
||||||
errorMessage = error.message;
|
? error.message
|
||||||
} else if (
|
: typeof error === "object" && error !== null && "message" in error
|
||||||
typeof error === "object" &&
|
? (error as { message: string }).message
|
||||||
error !== null &&
|
: "Unknown error";
|
||||||
"message" in error
|
|
||||||
) {
|
|
||||||
// If it's an object that has a 'message' property
|
|
||||||
errorMessage = (error as { message: string }).message;
|
|
||||||
} else {
|
|
||||||
// Unknown error shape, default message
|
|
||||||
errorMessage = "Unknown error";
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: "error",
|
type: "error",
|
||||||
|
|||||||
@@ -334,7 +334,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { AxiosError } from "axios/index";
|
import { AxiosError } from "axios";
|
||||||
import "dexie-export-import";
|
import "dexie-export-import";
|
||||||
import { Component, Vue } from "vue-facing-decorator";
|
import { Component, Vue } from "vue-facing-decorator";
|
||||||
import { useClipboard } from "@vueuse/core";
|
import { useClipboard } from "@vueuse/core";
|
||||||
@@ -357,6 +357,21 @@ interface Notification {
|
|||||||
text: string;
|
text: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IAccount {
|
||||||
|
did: string;
|
||||||
|
publicKeyHex: string;
|
||||||
|
privateHex?: string;
|
||||||
|
derivationPath: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SettingsType {
|
||||||
|
activeDid?: string;
|
||||||
|
apiServer?: string;
|
||||||
|
firstName?: string;
|
||||||
|
lastName?: string;
|
||||||
|
showContactGivesInline?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
@Component({ components: { QuickNav } })
|
@Component({ components: { QuickNav } })
|
||||||
export default class AccountViewView extends Vue {
|
export default class AccountViewView extends Vue {
|
||||||
$notify!: (notification: Notification, timeout?: number) => void;
|
$notify!: (notification: Notification, timeout?: number) => void;
|
||||||
@@ -386,23 +401,56 @@ export default class AccountViewView extends Vue {
|
|||||||
alertMessage = "";
|
alertMessage = "";
|
||||||
alertTitle = "";
|
alertTitle = "";
|
||||||
|
|
||||||
public async getIdentity(activeDid: string) {
|
public async getIdentity(activeDid: string): Promise<IIdentifier | null> {
|
||||||
await accountsDB.open();
|
try {
|
||||||
const account = await accountsDB.accounts
|
// Open the accounts database
|
||||||
.where("did")
|
await accountsDB.open();
|
||||||
.equals(activeDid)
|
} catch (error) {
|
||||||
.first();
|
console.error("Failed to open accounts database:", error);
|
||||||
const identity = JSON.parse(account?.identity || "null");
|
return null;
|
||||||
return identity;
|
}
|
||||||
|
|
||||||
|
let account: { identity?: string } | undefined;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Search for the account with the matching DID (decentralized identifier)
|
||||||
|
account = await accountsDB.accounts
|
||||||
|
.where("did")
|
||||||
|
.equals(activeDid)
|
||||||
|
.first();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to find account:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return parsed identity or null if not found
|
||||||
|
return JSON.parse(account?.identity || "null");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getHeaders(identity: IIdentifier) {
|
/**
|
||||||
const token = await accessToken(identity);
|
* Asynchronously retrieves headers for HTTP requests.
|
||||||
const headers = {
|
*
|
||||||
"Content-Type": "application/json",
|
* @param {IIdentifier} identity - The identity object for which to generate the headers.
|
||||||
Authorization: "Bearer " + token,
|
* @returns {Promise<Record<string,string>>} A Promise that resolves to an object containing the headers.
|
||||||
};
|
*
|
||||||
return headers;
|
* @throws Will throw an error if unable to generate an access token.
|
||||||
|
*/
|
||||||
|
public async getHeaders(
|
||||||
|
identity: IIdentifier,
|
||||||
|
): Promise<Record<string, string>> {
|
||||||
|
try {
|
||||||
|
const token = await accessToken(identity);
|
||||||
|
|
||||||
|
const headers: Record<string, string> = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return headers;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to get headers:", error);
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// call fn, copy text to the clipboard, then redo fn after 2 seconds
|
// call fn, copy text to the clipboard, then redo fn after 2 seconds
|
||||||
@@ -427,60 +475,92 @@ export default class AccountViewView extends Vue {
|
|||||||
this.numAccounts = await accountsDB.accounts.count();
|
this.numAccounts = await accountsDB.accounts.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Async function executed when the component is created.
|
||||||
|
* Initializes the component's state with values from the database,
|
||||||
|
* handles identity-related tasks, and checks limitations.
|
||||||
|
*
|
||||||
|
* @throws Will display specific messages to the user based on different errors.
|
||||||
|
*/
|
||||||
async created() {
|
async created() {
|
||||||
// Uncomment this to register this user on the test server.
|
|
||||||
// To manage within the vue devtools browser extension https://devtools.vuejs.org/
|
|
||||||
// assign this to a class variable, eg. "registerThisUser = testServerRegisterUser",
|
|
||||||
// select a component in the extension, and enter in the console: $vm.ctx.registerThisUser()
|
|
||||||
//testServerRegisterUser();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await db.open();
|
await db.open();
|
||||||
|
|
||||||
const settings = await db.settings.get(MASTER_SETTINGS_KEY);
|
const settings = await db.settings.get(MASTER_SETTINGS_KEY);
|
||||||
this.activeDid = settings?.activeDid || "";
|
|
||||||
this.apiServer = settings?.apiServer || "";
|
|
||||||
this.apiServerInput = settings?.apiServer || "";
|
|
||||||
this.firstName = settings?.firstName || "";
|
|
||||||
this.lastName = settings?.lastName || "";
|
|
||||||
this.showContactGives = !!settings?.showContactGivesInline;
|
|
||||||
|
|
||||||
|
// Initialize component state with values from the database or defaults
|
||||||
|
this.initializeState(settings);
|
||||||
|
|
||||||
|
// Get and process the identity
|
||||||
const identity = await this.getIdentity(this.activeDid);
|
const identity = await this.getIdentity(this.activeDid);
|
||||||
|
|
||||||
if (identity) {
|
if (identity) {
|
||||||
this.publicHex = identity.keys[0].publicKeyHex;
|
this.processIdentity(identity);
|
||||||
this.publicBase64 = Buffer.from(this.publicHex, "hex").toString(
|
}
|
||||||
"base64",
|
} catch (err: unknown) {
|
||||||
);
|
this.handleError(err);
|
||||||
this.derivationPath = identity.keys[0].meta.derivationPath;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
db.settings.update(MASTER_SETTINGS_KEY, {
|
/**
|
||||||
activeDid: identity.did,
|
* Initializes component state with values from the database or defaults.
|
||||||
});
|
* @param {SettingsType} settings - Object containing settings from the database.
|
||||||
this.checkLimitsFor(identity);
|
*/
|
||||||
}
|
initializeState(settings: SettingsType | undefined) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
this.activeDid = settings?.activeDid || "";
|
||||||
} catch (err: any) {
|
this.apiServer = settings?.apiServer || "";
|
||||||
if (
|
this.apiServerInput = settings?.apiServer || "";
|
||||||
err.message ===
|
this.firstName = settings?.firstName || "";
|
||||||
|
this.lastName = settings?.lastName || "";
|
||||||
|
this.showContactGives = !!settings?.showContactGivesInline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes the identity and updates the component's state.
|
||||||
|
* @param {IdentityType} identity - Object containing identity information.
|
||||||
|
*/
|
||||||
|
processIdentity(identity: IIdentifier) {
|
||||||
|
if (
|
||||||
|
identity &&
|
||||||
|
identity.keys &&
|
||||||
|
identity.keys.length > 0 &&
|
||||||
|
identity.keys[0].meta
|
||||||
|
) {
|
||||||
|
this.publicHex = identity.keys[0].publicKeyHex;
|
||||||
|
this.publicBase64 = Buffer.from(this.publicHex, "hex").toString("base64");
|
||||||
|
this.derivationPath = identity.keys[0].meta.derivationPath;
|
||||||
|
|
||||||
|
db.settings.update(MASTER_SETTINGS_KEY, {
|
||||||
|
activeDid: identity.did,
|
||||||
|
});
|
||||||
|
this.checkLimitsFor(identity);
|
||||||
|
} else {
|
||||||
|
// Handle the case where any of these are null or undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles errors and updates the component's state accordingly.
|
||||||
|
* @param {Error} err - The error object.
|
||||||
|
*/
|
||||||
|
handleError(err: unknown) {
|
||||||
|
if (
|
||||||
|
err instanceof Error &&
|
||||||
|
err.message ===
|
||||||
"Attempted to load account records with no identity available."
|
"Attempted to load account records with no identity available."
|
||||||
) {
|
) {
|
||||||
this.limitsMessage = "No identity.";
|
this.limitsMessage = "No identity.";
|
||||||
this.loadingLimits = false;
|
this.loadingLimits = false;
|
||||||
} else {
|
} else {
|
||||||
this.$notify(
|
this.$notify(
|
||||||
{
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "danger",
|
type: "danger",
|
||||||
title: "Error Creating Account",
|
title: "Error Creating Account",
|
||||||
text: "Clear your cache and start over (after data backup).",
|
text: "Clear your cache and start over (after data backup).",
|
||||||
},
|
},
|
||||||
-1,
|
-1,
|
||||||
);
|
);
|
||||||
console.error(
|
console.error("Telling user to clear cache at page create because:", err);
|
||||||
"Telling user to clear cache at page create because:",
|
|
||||||
err,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -507,41 +587,96 @@ export default class AccountViewView extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asynchronously exports the database into a downloadable JSON file.
|
||||||
|
*
|
||||||
|
* @throws Will notify the user if there is an export error.
|
||||||
|
*/
|
||||||
public async exportDatabase() {
|
public async exportDatabase() {
|
||||||
try {
|
try {
|
||||||
const blob = await db.export({ prettyJson: true });
|
// Generate the blob from the database
|
||||||
const url = URL.createObjectURL(blob);
|
const blob = await this.generateDatabaseBlob();
|
||||||
|
|
||||||
const downloadAnchor = this.$refs.downloadLink as HTMLAnchorElement;
|
// Create a temporary URL for the blob
|
||||||
downloadAnchor.href = url;
|
const url = this.createBlobURL(blob);
|
||||||
downloadAnchor.download = db.name + "-backup.json";
|
|
||||||
downloadAnchor.click();
|
|
||||||
|
|
||||||
|
// Trigger the download
|
||||||
|
this.downloadDatabaseBackup(url);
|
||||||
|
|
||||||
|
// Revoke the temporary URL
|
||||||
URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url);
|
||||||
|
|
||||||
this.$notify(
|
// Notify the user that the download has started
|
||||||
{
|
this.notifyDownloadStarted();
|
||||||
group: "alert",
|
|
||||||
type: "toast",
|
|
||||||
title: "Download Started",
|
|
||||||
text: "See your downloads directory for the backup.",
|
|
||||||
},
|
|
||||||
5000,
|
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$notify(
|
this.handleExportError(error);
|
||||||
{
|
|
||||||
group: "alert",
|
|
||||||
type: "danger",
|
|
||||||
title: "Export Error",
|
|
||||||
text: "See console logs for more info.",
|
|
||||||
},
|
|
||||||
-1,
|
|
||||||
);
|
|
||||||
console.error("Export Error:", error);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a blob object representing the database.
|
||||||
|
*
|
||||||
|
* @returns {Promise<Blob>} The generated blob object.
|
||||||
|
*/
|
||||||
|
private async generateDatabaseBlob(): Promise<Blob> {
|
||||||
|
return await db.export({ prettyJson: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a temporary URL for a blob object.
|
||||||
|
*
|
||||||
|
* @param {Blob} blob - The blob object.
|
||||||
|
* @returns {string} The temporary URL for the blob.
|
||||||
|
*/
|
||||||
|
private createBlobURL(blob: Blob): string {
|
||||||
|
return URL.createObjectURL(blob);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers the download of the database backup.
|
||||||
|
*
|
||||||
|
* @param {string} url - The temporary URL for the blob.
|
||||||
|
*/
|
||||||
|
private downloadDatabaseBackup(url: string) {
|
||||||
|
const downloadAnchor = this.$refs.downloadLink as HTMLAnchorElement;
|
||||||
|
downloadAnchor.href = url;
|
||||||
|
downloadAnchor.download = `${db.name}-backup.json`;
|
||||||
|
downloadAnchor.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the user that the download has started.
|
||||||
|
*/
|
||||||
|
private notifyDownloadStarted() {
|
||||||
|
this.$notify(
|
||||||
|
{
|
||||||
|
group: "alert",
|
||||||
|
type: "toast",
|
||||||
|
title: "Download Started",
|
||||||
|
text: "See your downloads directory for the backup.",
|
||||||
|
},
|
||||||
|
5000,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles errors during the database export process.
|
||||||
|
*
|
||||||
|
* @param {Error} error - The error object.
|
||||||
|
*/
|
||||||
|
private handleExportError(error: unknown) {
|
||||||
|
this.$notify(
|
||||||
|
{
|
||||||
|
group: "alert",
|
||||||
|
type: "danger",
|
||||||
|
title: "Export Error",
|
||||||
|
text: "See console logs for more info.",
|
||||||
|
},
|
||||||
|
-1,
|
||||||
|
);
|
||||||
|
console.error("Export Error:", error);
|
||||||
|
}
|
||||||
|
|
||||||
async checkLimits() {
|
async checkLimits() {
|
||||||
const identity = await this.getIdentity(this.activeDid);
|
const identity = await this.getIdentity(this.activeDid);
|
||||||
if (identity) {
|
if (identity) {
|
||||||
@@ -549,68 +684,120 @@ export default class AccountViewView extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async checkLimitsFor(identity: IIdentifier) {
|
/**
|
||||||
|
* Asynchronously checks rate limits for the given identity.
|
||||||
|
*
|
||||||
|
* Updates component state variables `limits`, `limitsMessage`, and `loadingLimits`.
|
||||||
|
*/
|
||||||
|
public async checkLimitsFor(identity: IIdentifier) {
|
||||||
this.loadingLimits = true;
|
this.loadingLimits = true;
|
||||||
this.limitsMessage = "";
|
this.limitsMessage = "";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const url = this.apiServer + "/api/report/rateLimits";
|
const resp = await this.fetchRateLimits(identity);
|
||||||
const headers = await this.getHeaders(identity);
|
|
||||||
|
|
||||||
const resp = await this.axios.get(url, { headers });
|
|
||||||
// axios throws an exception on a 400
|
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
this.limits = resp.data;
|
this.limits = resp.data;
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
} catch (error) {
|
||||||
} catch (error: any) {
|
this.handleRateLimitsError(error);
|
||||||
if (
|
|
||||||
error.message ===
|
|
||||||
"Attempted to load Give records with no identity available."
|
|
||||||
) {
|
|
||||||
this.limitsMessage = "No identity.";
|
|
||||||
this.loadingLimits = false;
|
|
||||||
} else {
|
|
||||||
const serverError = error as AxiosError;
|
|
||||||
console.error("Bad response retrieving limits: ", serverError);
|
|
||||||
|
|
||||||
const data = (serverError.response &&
|
|
||||||
serverError.response.data) as ErrorResponse;
|
|
||||||
this.limitsMessage = data?.error?.message || "Bad server response.";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loadingLimits = false;
|
this.loadingLimits = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async switchAccount(accountNum: number) {
|
/**
|
||||||
// 0 means none
|
* Fetches rate limits from the server.
|
||||||
if (accountNum === 0) {
|
*
|
||||||
await db.open();
|
* @param {IIdentifier} identity - The identity object to check rate limits for.
|
||||||
db.settings.update(MASTER_SETTINGS_KEY, {
|
* @returns {Promise<AxiosResponse>} The Axios response object.
|
||||||
activeDid: undefined,
|
*/
|
||||||
});
|
private async fetchRateLimits(identity: IIdentifier) {
|
||||||
this.activeDid = "";
|
const url = `${this.apiServer}/api/report/rateLimits`;
|
||||||
this.derivationPath = "";
|
const headers = await this.getHeaders(identity);
|
||||||
this.publicHex = "";
|
return await this.axios.get(url, { headers });
|
||||||
this.publicBase64 = "";
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles errors that occur while fetching rate limits.
|
||||||
|
*
|
||||||
|
* @param {AxiosError | Error} error - The error object.
|
||||||
|
*/
|
||||||
|
private handleRateLimitsError(error: unknown) {
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
const data = error.response?.data as ErrorResponse;
|
||||||
|
this.limitsMessage = data?.error?.message || "Bad server response.";
|
||||||
|
console.error("Bad response retrieving limits:", error);
|
||||||
|
} else if (
|
||||||
|
error instanceof Error &&
|
||||||
|
error.message ===
|
||||||
|
"Attempted to load Give records with no identity available."
|
||||||
|
) {
|
||||||
|
this.limitsMessage = "No identity.";
|
||||||
} else {
|
} else {
|
||||||
await accountsDB.open();
|
// Handle other unknown errors
|
||||||
const accounts = await accountsDB.accounts.toArray();
|
|
||||||
const account = accounts[accountNum - 1];
|
|
||||||
|
|
||||||
await db.open();
|
|
||||||
db.settings.update(MASTER_SETTINGS_KEY, {
|
|
||||||
activeDid: account.did,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.activeDid = account.did;
|
|
||||||
this.derivationPath = account.derivationPath;
|
|
||||||
this.publicHex = account.publicKeyHex;
|
|
||||||
this.publicBase64 = Buffer.from(this.publicHex, "hex").toString("base64");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asynchronously switches the active account based on the provided account number.
|
||||||
|
*
|
||||||
|
* @param {number} accountNum - The account number to switch to. 0 means none.
|
||||||
|
*/
|
||||||
|
public async switchAccount(accountNum: number) {
|
||||||
|
await db.open(); // Assumes db needs to be open for both cases
|
||||||
|
|
||||||
|
if (accountNum === 0) {
|
||||||
|
this.switchToNoAccount();
|
||||||
|
} else {
|
||||||
|
await this.switchToAccountNumber(accountNum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switches to no active account and clears relevant properties.
|
||||||
|
*/
|
||||||
|
private async switchToNoAccount() {
|
||||||
|
await db.settings.update(MASTER_SETTINGS_KEY, { activeDid: undefined });
|
||||||
|
this.clearActiveAccountProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears properties related to the active account.
|
||||||
|
*/
|
||||||
|
private clearActiveAccountProperties() {
|
||||||
|
this.activeDid = "";
|
||||||
|
this.derivationPath = "";
|
||||||
|
this.publicHex = "";
|
||||||
|
this.publicBase64 = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switches to an account based on its number in the list.
|
||||||
|
*
|
||||||
|
* @param {number} accountNum - The account number to switch to.
|
||||||
|
*/
|
||||||
|
private async switchToAccountNumber(accountNum: number) {
|
||||||
|
await accountsDB.open();
|
||||||
|
const accounts = await accountsDB.accounts.toArray();
|
||||||
|
const account = accounts[accountNum - 1];
|
||||||
|
|
||||||
|
await db.settings.update(MASTER_SETTINGS_KEY, { activeDid: account.did });
|
||||||
|
|
||||||
|
this.updateActiveAccountProperties(account);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates properties related to the active account.
|
||||||
|
*
|
||||||
|
* @param {AccountType} account - The account object.
|
||||||
|
*/
|
||||||
|
private updateActiveAccountProperties(account: IAccount) {
|
||||||
|
this.activeDid = account.did;
|
||||||
|
this.derivationPath = account.derivationPath;
|
||||||
|
this.publicHex = account.publicKeyHex;
|
||||||
|
this.publicBase64 = Buffer.from(this.publicHex, "hex").toString("base64");
|
||||||
|
}
|
||||||
|
|
||||||
public showContactGivesClassNames() {
|
public showContactGivesClassNames() {
|
||||||
return {
|
return {
|
||||||
"bg-slate-900": !this.showContactGives,
|
"bg-slate-900": !this.showContactGives,
|
||||||
|
|||||||
Reference in New Issue
Block a user