From 852bd93f3f06d0a05ae3b683f113381c7073a7c6 Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Tue, 5 Sep 2023 21:25:04 +0800 Subject: [PATCH 1/3] New branch for cleanup and web push --- src/libs/endorserServer.ts | 117 +++++++++++-------------------------- 1 file changed, 34 insertions(+), 83 deletions(-) diff --git a/src/libs/endorserServer.ts b/src/libs/endorserServer.ts index 4aab30a..3b6704f 100644 --- a/src/libs/endorserServer.ts +++ b/src/libs/endorserServer.ts @@ -112,27 +112,15 @@ export function isHiddenDid(did: string) { /** always returns text, maybe UNNAMED_VISIBLE or UNKNOWN_ENTITY **/ -export function didInfo( - did: string, - activeDid: string, - allMyDids: Array, - contacts: Array, -): string { - const myId: string | undefined = R.find(R.equals(did), allMyDids); - if (myId) { - return "You" + (myId !== activeDid ? " (Alt ID)" : ""); - } else { - const contact: Contact | undefined = R.find((c) => c.did === did, contacts); - if (contact) { - return contact.name || "Someone Unnamed in Contacts"; - } else if (!did) { - return "Unspecified Person"; - } else if (isHiddenDid(did)) { - return "Someone Not In Network"; - } else { - return "Someone Not In Contacts"; - } - } +export function didInfo(did: string, activeDid: string, allMyDids: string[], contacts: Contact[]): string { + const myId = R.find(R.equals(did), allMyDids); + if (myId) return `You${myId !== activeDid ? " (Alt ID)" : ""}`; + + const contact = R.find(c => c.did === did, contacts); + return contact ? contact.name || "Someone Unnamed in Contacts" : + !did ? "Unspecified Person" : + isHiddenDid(did) ? "Someone Not In Network" : + "Someone Not In Contacts"; } export interface ResultWithType { @@ -171,30 +159,16 @@ export async function createAndSubmitGive( fulfillsProjectHandleId?: string, ): Promise { try { - // Make a claim const vcClaim: GiveVerifiableCredential = { "@context": "https://schema.org", "@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 = { vc: { "@context": ["https://www.w3.org/2018/credentials/v1"], @@ -205,14 +179,7 @@ export async function createAndSubmitGive( // Create a signature using private key of identity const firstKey = identity.keys[0]; - if (!firstKey || !firstKey.privateKeyHex) { - throw { - error: "No private key", - message: `Your identifier ${identity.did} is not configured correctly. Use a different identifier.`, - }; - } - - const privateKeyHex = firstKey.privateKeyHex; + const privateKeyHex = firstKey?.privateKeyHex; if (!privateKeyHex) { throw { @@ -222,56 +189,40 @@ export async function createAndSubmitGive( } const signer = await SimpleSigner(privateKeyHex); - const alg = undefined; // Create a JWT for the request - const vcJwt: string = await didJwt.createJWT(vcPayload, { - alg: alg, issuer: identity.did, - signer: signer, + signer, }); // Make the xhr request payload - const payload = JSON.stringify({ jwtEncoded: vcJwt }); - const url = apiServer + "/api/v2/claim"; + const url = `${apiServer}/api/v2/claim`; const token = await accessToken(identity); - const headers = { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }; - const response = await axios.post(url, payload, { headers }); - return { - type: "success", - response, - }; + const response = await axios.post(url, payload, { + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + }); + + return { type: "success", response }; + } catch (error: unknown) { - let errorMessage: string; - - if (error instanceof Error) { - // If it's a JavaScript Error object - errorMessage = error.message; - } else if ( - typeof error === "object" && - error !== null && - "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"; - } + const errorMessage: string = + error instanceof Error ? error.message : + (typeof error === "object" && error?.message) ? error.message : + "Unknown error"; return { type: "error", error: { error: errorMessage, - userMessage: "Failed to create and submit the claim.", - }, - }; + userMessage: "Failed to create and submit the claim." + } + }; } } From 31d13b91437f4ddd9264d2c36997e88fb9a53496 Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Wed, 6 Sep 2023 20:46:16 +0800 Subject: [PATCH 2/3] Refactoring for cleanup --- sample.txt | 70 ++++++ src/db/tables/accounts.ts | 43 +++- src/libs/endorserServer.ts | 41 ++-- src/views/AccountViewView.vue | 434 ++++++++++++++++++++++++---------- 4 files changed, 438 insertions(+), 150 deletions(-) create mode 100644 sample.txt diff --git a/sample.txt b/sample.txt new file mode 100644 index 0000000..903c3c3 --- /dev/null +++ b/sample.txt @@ -0,0 +1,70 @@ + +> 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. + + + ERROR Failed to compile with 6 errors8:44:37 PM + + error in ./src/views/AccountViewView.vue?vue&type=script&lang=ts + +Module not found: Error: Package path ./index is not exported from package /home/matthew/projects/kick-starter-for-time-pwa/node_modules/axios (see exports field in /home/matthew/projects/kick-starter-for-time-pwa/node_modules/axios/package.json) + + error in src/libs/endorserServer.ts:226:53 + +TS18047: 'error' is possibly 'null'. + 224 | error instanceof Error + 225 | ? error.message + > 226 | : typeof error === "object" && "message" in error + | ^^^^^ + 227 | ? (error as { message: string }).message + 228 | : "Unknown error"; + 229 | + + error in src/views/AccountViewView.vue:474:29 + +TS2304: Cannot find name 'IdentityType'. + 472 | * @param {IdentityType} identity - Object containing identity information. + 473 | */ + > 474 | processIdentity(identity: IdentityType) { + | ^^^^^^^^^^^^ + 475 | this.publicHex = identity.keys[0].publicKeyHex; + 476 | this.publicBase64 = Buffer.from(this.publicHex, "hex").toString("base64"); + 477 | this.derivationPath = identity.keys[0].meta.derivationPath; + + error in src/views/AccountViewView.vue:491:7 + +TS18046: 'err' is of type 'unknown'. + 489 | handleError(err: unknown) { + 490 | if ( + > 491 | err.message === + | ^^^ + 492 | "Attempted to load account records with no identity available." + 493 | ) { + 494 | this.limitsMessage = "No identity."; + + error in src/views/AccountViewView.vue:645:34 + +TS2345: Argument of type 'unknown' is not assignable to parameter of type 'Error | AxiosError'. + 643 | } + 644 | } catch (error) { + > 645 | this.handleRateLimitsError(error); + | ^^^^^ + 646 | } + 647 | + 648 | this.loadingLimits = false; + + error in src/views/AccountViewView.vue:726:40 + +TS2345: Argument of type 'Account' is not assignable to parameter of type 'IAccount'. + Property 'privateHex' is missing in type 'Account' but required in type 'IAccount'. + 724 | await db.settings.update(MASTER_SETTINGS_KEY, { activeDid: account.did }); + 725 | + > 726 | this.updateActiveAccountProperties(account); + | ^^^^^^^ + 727 | } + 728 | + 729 | /** + diff --git a/src/db/tables/accounts.ts b/src/db/tables/accounts.ts index d31f160..63f4c6a 100644 --- a/src/db/tables/accounts.ts +++ b/src/db/tables/accounts.ts @@ -1,17 +1,50 @@ +/** + * Represents an account stored in the database. + */ export type Account = { - id?: number; // auto-generated by Dexie + /** + * Auto-generated ID by Dexie. + */ + id?: number; + + /** + * The date the account was created. + */ dateCreated: string; + + /** + * The derivation path for the account. + */ derivationPath: string; + + /** + * Decentralized Identifier (DID) for the account. + */ 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; + + /** + * The public key in hexadecimal format. + */ publicKeyHex: string; + + /** + * The mnemonic passphrase for the account. + */ 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 = { accounts: "++id, dateCreated, derivationPath, did, $identity, $mnemonic, publicKeyHex", diff --git a/src/libs/endorserServer.ts b/src/libs/endorserServer.ts index 3b6704f..f651310 100644 --- a/src/libs/endorserServer.ts +++ b/src/libs/endorserServer.ts @@ -112,15 +112,23 @@ export function isHiddenDid(did: string) { /** always returns text, maybe UNNAMED_VISIBLE or UNKNOWN_ENTITY **/ -export function didInfo(did: string, activeDid: string, allMyDids: string[], contacts: Contact[]): string { +export function didInfo( + did: string, + activeDid: string, + allMyDids: string[], + contacts: Contact[], +): string { const myId = R.find(R.equals(did), allMyDids); if (myId) return `You${myId !== activeDid ? " (Alt ID)" : ""}`; - const contact = R.find(c => c.did === did, contacts); - return contact ? contact.name || "Someone Unnamed in Contacts" : - !did ? "Unspecified Person" : - isHiddenDid(did) ? "Someone Not In Network" : - "Someone Not In Contacts"; + const contact = R.find((c) => c.did === did, contacts); + return contact + ? contact.name || "Someone Unnamed in Contacts" + : !did + ? "Unspecified Person" + : isHiddenDid(did) + ? "Someone Not In Network" + : "Someone Not In Contacts"; } export interface ResultWithType { @@ -166,7 +174,9 @@ export async function createAndSubmitGive( agent: fromDid ? { identifier: fromDid } : undefined, description: description || undefined, object: hours ? { amountOfThisGood: hours, unitCode: "HUR" } : undefined, - fulfills: fulfillsProjectHandleId ? { "@type": "PlanAction", identifier: fulfillsProjectHandleId } : undefined, + fulfills: fulfillsProjectHandleId + ? { "@type": "PlanAction", identifier: fulfillsProjectHandleId } + : undefined, }; const vcPayload = { @@ -209,20 +219,21 @@ export async function createAndSubmitGive( }); return { type: "success", response }; - } catch (error: unknown) { - const errorMessage: string = - error instanceof Error ? error.message : - (typeof error === "object" && error?.message) ? error.message : - "Unknown error"; + const errorMessage: string = + error instanceof Error + ? error.message + : typeof error === "object" && "message" in error + ? (error as { message: string }).message + : "Unknown error"; return { type: "error", error: { error: errorMessage, - userMessage: "Failed to create and submit the claim." - } - }; + userMessage: "Failed to create and submit the claim.", + }, + }; } } diff --git a/src/views/AccountViewView.vue b/src/views/AccountViewView.vue index c17be54..4172eea 100644 --- a/src/views/AccountViewView.vue +++ b/src/views/AccountViewView.vue @@ -310,6 +310,21 @@ interface Notification { 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 } }) export default class AccountViewView extends Vue { $notify!: (notification: Notification, timeout?: number) => void; @@ -339,23 +354,56 @@ export default class AccountViewView extends Vue { alertMessage = ""; alertTitle = ""; - public async getIdentity(activeDid: string) { - await accountsDB.open(); - const account = await accountsDB.accounts - .where("did") - .equals(activeDid) - .first(); - const identity = JSON.parse(account?.identity || "null"); - return identity; + public async getIdentity(activeDid: string): Promise { + try { + // Open the accounts database + await accountsDB.open(); + } catch (error) { + console.error("Failed to open accounts database:", error); + return null; + } + + 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); - const headers = { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }; - return headers; + /** + * Asynchronously retrieves headers for HTTP requests. + * + * @param {IIdentifier} identity - The identity object for which to generate the headers. + * @returns {Promise>} A Promise that resolves to an object containing the headers. + * + * @throws Will throw an error if unable to generate an access token. + */ + public async getHeaders( + identity: IIdentifier, + ): Promise> { + try { + const token = await accessToken(identity); + + const headers: Record = { + "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 @@ -380,60 +428,82 @@ export default class AccountViewView extends Vue { 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() { - // 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 { await db.open(); + 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; - const identity = await this.getIdentity(this.activeDid); + // 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); if (identity) { - 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); - } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (err: any) { - if ( - err.message === - "Attempted to load account records with no identity available." - ) { - this.limitsMessage = "No identity."; - this.loadingLimits = false; - } else { - this.$notify( - { - group: "alert", - type: "danger", - title: "Error Creating Account", - text: "Clear your cache and start over (after data backup).", - }, - -1, - ); - console.error( - "Telling user to clear cache at page create because:", - err, - ); + this.processIdentity(identity); } + } catch (err: unknown) { + this.handleError(err); + } + } + + /** + * Initializes component state with values from the database or defaults. + * @param {SettingsType} settings - Object containing settings from the database. + */ + initializeState(settings: SettingsType | undefined) { + this.activeDid = settings?.activeDid || ""; + this.apiServer = settings?.apiServer || ""; + this.apiServerInput = settings?.apiServer || ""; + 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: IdentityType) { + 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); + } + + /** + * Handles errors and updates the component's state accordingly. + * @param {Error} err - The error object. + */ + handleError(err: unknown) { + if ( + err.message === + "Attempted to load account records with no identity available." + ) { + this.limitsMessage = "No identity."; + this.loadingLimits = false; + } else { + this.$notify( + { + group: "alert", + type: "danger", + title: "Error Creating Account", + text: "Clear your cache and start over (after data backup).", + }, + -1, + ); + console.error("Telling user to clear cache at page create because:", err); } } @@ -460,41 +530,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() { try { - const blob = await db.export({ prettyJson: true }); - const url = URL.createObjectURL(blob); + // Generate the blob from the database + const blob = await this.generateDatabaseBlob(); + + // Create a temporary URL for the blob + const url = this.createBlobURL(blob); - const downloadAnchor = this.$refs.downloadLink as HTMLAnchorElement; - downloadAnchor.href = url; - downloadAnchor.download = db.name + "-backup.json"; - downloadAnchor.click(); + // Trigger the download + this.downloadDatabaseBackup(url); + // Revoke the temporary URL URL.revokeObjectURL(url); - this.$notify( - { - group: "alert", - type: "toast", - title: "Download Started", - text: "See your downloads directory for the backup.", - }, - 5000, - ); + // Notify the user that the download has started + this.notifyDownloadStarted(); } catch (error) { - this.$notify( - { - group: "alert", - type: "danger", - title: "Export Error", - text: "See console logs for more info.", - }, - -1, - ); - console.error("Export Error:", error); + this.handleExportError(error); } } + /** + * Generates a blob object representing the database. + * + * @returns {Promise} The generated blob object. + */ + private async generateDatabaseBlob(): Promise { + 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() { const identity = await this.getIdentity(this.activeDid); if (identity) { @@ -502,66 +627,115 @@ 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.limitsMessage = ""; try { - const url = this.apiServer + "/api/report/rateLimits"; - const headers = await this.getHeaders(identity); - - const resp = await this.axios.get(url, { headers }); - // axios throws an exception on a 400 + const resp = await this.fetchRateLimits(identity); if (resp.status === 200) { this.limits = resp.data; } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (error: any) { - 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."; - } + } catch (error) { + this.handleRateLimitsError(error); } this.loadingLimits = false; } - async switchAccount(accountNum: number) { - // 0 means none + /** + * Fetches rate limits from the server. + * + * @param {IIdentifier} identity - The identity object to check rate limits for. + * @returns {Promise} The Axios response object. + */ + private async fetchRateLimits(identity: IIdentifier) { + const url = `${this.apiServer}/api/report/rateLimits`; + const headers = await this.getHeaders(identity); + return await this.axios.get(url, { headers }); + } + + /** + * Handles errors that occur while fetching rate limits. + * + * @param {AxiosError | Error} error - The error object. + */ + private handleRateLimitsError(error: AxiosError | Error) { + 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.message === + "Attempted to load Give records with no identity available." + ) { + this.limitsMessage = "No identity."; + } + } + + /** + * 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) { - await db.open(); - db.settings.update(MASTER_SETTINGS_KEY, { - activeDid: undefined, - }); - this.activeDid = ""; - this.derivationPath = ""; - this.publicHex = ""; - this.publicBase64 = ""; + this.switchToNoAccount(); } else { - await accountsDB.open(); - const accounts = await accountsDB.accounts.toArray(); - const account = accounts[accountNum - 1]; + await this.switchToAccountNumber(accountNum); + } + } - await db.open(); - db.settings.update(MASTER_SETTINGS_KEY, { - activeDid: account.did, - }); + /** + * Switches to no active account and clears relevant properties. + */ + private async switchToNoAccount() { + await db.settings.update(MASTER_SETTINGS_KEY, { activeDid: undefined }); + this.clearActiveAccountProperties(); + } - this.activeDid = account.did; - this.derivationPath = account.derivationPath; - this.publicHex = account.publicKeyHex; - this.publicBase64 = Buffer.from(this.publicHex, "hex").toString("base64"); - } + /** + * 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() { From 5b5c631001855a131fd70bcb84215ee4cd9b2881 Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Thu, 7 Sep 2023 18:07:53 +0800 Subject: [PATCH 3/3] Fix typing errors from the refactoring --- sample.txt | 231 +++++++++++++++++++++++++--------- src/libs/endorserServer.ts | 6 +- src/views/AccountViewView.vue | 39 ++++-- 3 files changed, 199 insertions(+), 77 deletions(-) diff --git a/sample.txt b/sample.txt index 903c3c3..efe20be 100644 --- a/sample.txt +++ b/sample.txt @@ -6,65 +6,172 @@ All browser targets in the browserslist configuration have supported ES module. Therefore we don't build two separate bundles for differential loading. - ERROR Failed to compile with 6 errors8:44:37 PM - - error in ./src/views/AccountViewView.vue?vue&type=script&lang=ts - -Module not found: Error: Package path ./index is not exported from package /home/matthew/projects/kick-starter-for-time-pwa/node_modules/axios (see exports field in /home/matthew/projects/kick-starter-for-time-pwa/node_modules/axios/package.json) - - error in src/libs/endorserServer.ts:226:53 - -TS18047: 'error' is possibly 'null'. - 224 | error instanceof Error - 225 | ? error.message - > 226 | : typeof error === "object" && "message" in error - | ^^^^^ - 227 | ? (error as { message: string }).message - 228 | : "Unknown error"; - 229 | - - error in src/views/AccountViewView.vue:474:29 - -TS2304: Cannot find name 'IdentityType'. - 472 | * @param {IdentityType} identity - Object containing identity information. - 473 | */ - > 474 | processIdentity(identity: IdentityType) { - | ^^^^^^^^^^^^ - 475 | this.publicHex = identity.keys[0].publicKeyHex; - 476 | this.publicBase64 = Buffer.from(this.publicHex, "hex").toString("base64"); - 477 | this.derivationPath = identity.keys[0].meta.derivationPath; - - error in src/views/AccountViewView.vue:491:7 - -TS18046: 'err' is of type 'unknown'. - 489 | handleError(err: unknown) { - 490 | if ( - > 491 | err.message === - | ^^^ - 492 | "Attempted to load account records with no identity available." - 493 | ) { - 494 | this.limitsMessage = "No identity."; - - error in src/views/AccountViewView.vue:645:34 - -TS2345: Argument of type 'unknown' is not assignable to parameter of type 'Error | AxiosError'. - 643 | } - 644 | } catch (error) { - > 645 | this.handleRateLimitsError(error); - | ^^^^^ - 646 | } - 647 | - 648 | this.loadingLimits = false; - - error in src/views/AccountViewView.vue:726:40 - -TS2345: Argument of type 'Account' is not assignable to parameter of type 'IAccount'. - Property 'privateHex' is missing in type 'Account' but required in type 'IAccount'. - 724 | await db.settings.update(MASTER_SETTINGS_KEY, { activeDid: account.did }); - 725 | - > 726 | this.updateActiveAccountProperties(account); - | ^^^^^^^ - 727 | } - 728 | - 729 | /** - + 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 + diff --git a/src/libs/endorserServer.ts b/src/libs/endorserServer.ts index f651310..ce7093a 100644 --- a/src/libs/endorserServer.ts +++ b/src/libs/endorserServer.ts @@ -221,9 +221,11 @@ export async function createAndSubmitGive( return { type: "success", response }; } catch (error: unknown) { const errorMessage: string = - error instanceof Error + error === null + ? "Null error" + : error instanceof Error ? error.message - : typeof error === "object" && "message" in error + : typeof error === "object" && error !== null && "message" in error ? (error as { message: string }).message : "Unknown error"; diff --git a/src/views/AccountViewView.vue b/src/views/AccountViewView.vue index 4172eea..55acf6c 100644 --- a/src/views/AccountViewView.vue +++ b/src/views/AccountViewView.vue @@ -287,7 +287,7 @@