diff --git a/src/components/DataExportSection.vue b/src/components/DataExportSection.vue
index 17524e79..737f8f2e 100644
--- a/src/components/DataExportSection.vue
+++ b/src/components/DataExportSection.vue
@@ -18,7 +18,7 @@ messages * - Conditional UI based on platform capabilities * * @component *
>
@@ -108,7 +108,7 @@ export default class DataExportSection extends Vue {
* Flag indicating if the user has backed up their seed phrase
* Used to control the visibility of the notification dot
*/
- hasBackedUpSeed = false;
+ showRedNotificationDot = false;
/**
* Notification helper for consistent notification patterns
@@ -240,11 +240,12 @@ export default class DataExportSection extends Vue {
private async loadSeedBackupStatus(): Promise {
try {
const settings = await this.$accountSettings();
- this.hasBackedUpSeed = !!settings.hasBackedUpSeed;
+ this.showRedNotificationDot =
+ !!settings.isRegistered && !settings.hasBackedUpSeed;
} catch (err: unknown) {
logger.error("Failed to load seed backup status:", err);
// Default to false (show notification dot) if we can't load the setting
- this.hasBackedUpSeed = false;
+ this.showRedNotificationDot = false;
}
}
}
diff --git a/src/components/TopMessage.vue b/src/components/TopMessage.vue
index 9975101e..96f1428e 100644
--- a/src/components/TopMessage.vue
+++ b/src/components/TopMessage.vue
@@ -27,7 +27,7 @@ import { logger } from "../utils/logger";
})
export default class TopMessage extends Vue {
// Enhanced PlatformServiceMixin v4.0 provides:
- // - Cached database operations: this.$contacts(), this.$settings(), this.$accountSettings()
+ // - Cached database operations: this.$contacts(), this.$accountSettings()
// - Settings shortcuts: this.$saveSettings()
// - Cache management: this.$refreshSettings(), this.$clearAllCaches()
// - Ultra-concise database methods: this.$db(), this.$exec(), this.$query()
diff --git a/src/constants/accountView.ts b/src/constants/accountView.ts
index d74c9404..3953b264 100644
--- a/src/constants/accountView.ts
+++ b/src/constants/accountView.ts
@@ -86,7 +86,7 @@ export const ACCOUNT_VIEW_CONSTANTS = {
CANNOT_UPLOAD_IMAGES: "You cannot upload images.",
BAD_SERVER_RESPONSE: "Bad server response.",
ERROR_RETRIEVING_LIMITS:
- "No limits were found, so no actions are allowed. You will need to get registered.",
+ "No limits were found, so no actions are allowed. You need to get registered.",
},
// Project assignment errors
diff --git a/src/db/databaseUtil.ts b/src/db/databaseUtil.ts
index 18e7952b..487742c9 100644
--- a/src/db/databaseUtil.ts
+++ b/src/db/databaseUtil.ts
@@ -9,34 +9,6 @@ import { logger } from "@/utils/logger";
import { DEFAULT_ENDORSER_API_SERVER } from "@/constants/app";
import { QueryExecResult } from "@/interfaces/database";
-export async function updateDefaultSettings(
- settingsChanges: Settings,
-): Promise {
- delete settingsChanges.accountDid; // just in case
- // ensure there is no "id" that would override the key
- delete settingsChanges.id;
- try {
- const platformService = PlatformServiceFactory.getInstance();
- const { sql, params } = generateUpdateStatement(
- settingsChanges,
- "settings",
- "id = ?",
- [MASTER_SETTINGS_KEY],
- );
- const result = await platformService.dbExec(sql, params);
- return result.changes === 1;
- } catch (error) {
- logger.error("Error updating default settings:", error);
- if (error instanceof Error) {
- throw error; // Re-throw if it's already an Error with a message
- } else {
- throw new Error(
- `Failed to update settings. We recommend you try again or restart the app.`,
- );
- }
- }
-}
-
export async function insertDidSpecificSettings(
did: string,
settings: Partial = {},
@@ -91,6 +63,7 @@ export async function updateDidSpecificSettings(
? mapColumnsToValues(postUpdateResult.columns, postUpdateResult.values)[0]
: null;
+ // Note that we want to eliminate this check (and fix the above if it doesn't work).
// Check if any of the target fields were actually changed
let actuallyUpdated = false;
if (currentRecord && updatedRecord) {
diff --git a/src/libs/endorserServer.ts b/src/libs/endorserServer.ts
index 320a6363..a0e2bf6c 100644
--- a/src/libs/endorserServer.ts
+++ b/src/libs/endorserServer.ts
@@ -809,6 +809,8 @@ export async function getStarredProjectsWithChanges(
}
if (!afterId) {
+ // This doesn't make sense: there should always be some previous one they've seen.
+ // We'll just return blank.
return { data: [], hitLimit: false };
}
@@ -1756,49 +1758,19 @@ export async function fetchEndorserRateLimits(
timestamp: new Date().toISOString(),
});
- try {
- const response = await axios.get(url, { headers } as AxiosRequestConfig);
-
- // Log successful registration check
- logger.debug("[User Registration] User registration check successful:", {
- did: issuerDid,
- server: apiServer,
- status: response.status,
- isRegistered: true,
- timestamp: new Date().toISOString(),
- });
-
- return response;
- } catch (error) {
- // Enhanced error logging with user registration context
- const axiosError = error as {
- response?: {
- data?: { error?: { code?: string; message?: string } };
- status?: number;
- };
- };
- const errorCode = axiosError.response?.data?.error?.code;
- const errorMessage = axiosError.response?.data?.error?.message;
- const httpStatus = axiosError.response?.status;
-
- logger.warn("[User Registration] User not registered on server:", {
- did: issuerDid,
- server: apiServer,
- errorCode: errorCode,
- errorMessage: errorMessage,
- httpStatus: httpStatus,
- needsRegistration: true,
- timestamp: new Date().toISOString(),
- });
+ // not wrapped in a 'try' because the error returned is self-explanatory
+ const response = await axios.get(url, { headers } as AxiosRequestConfig);
- // Log the original error for debugging
- logger.error(
- `[fetchEndorserRateLimits] Error for DID ${issuerDid}:`,
- errorStringForLog(error),
- );
+ // Log successful registration check
+ logger.debug("[User Registration] User registration check successful:", {
+ did: issuerDid,
+ server: apiServer,
+ status: response.status,
+ isRegistered: true,
+ timestamp: new Date().toISOString(),
+ });
- throw error;
- }
+ return response;
}
/**
@@ -1847,14 +1819,17 @@ export async function fetchImageRateLimits(
};
};
- logger.error("[Image Server] Image rate limits check failed:", {
- did: issuerDid,
- server: server,
- errorCode: axiosError.response?.data?.error?.code,
- errorMessage: axiosError.response?.data?.error?.message,
- httpStatus: axiosError.response?.status,
- timestamp: new Date().toISOString(),
- });
+ logger.warn(
+ "[Image Server] Image rate limits check failed, which is expected for users not registered on test server (eg. when only registered on local server).",
+ {
+ did: issuerDid,
+ server: server,
+ errorCode: axiosError.response?.data?.error?.code,
+ errorMessage: axiosError.response?.data?.error?.message,
+ httpStatus: axiosError.response?.status,
+ timestamp: new Date().toISOString(),
+ },
+ );
return null;
}
}
diff --git a/src/services/api.ts b/src/services/api.ts
index d7b67beb..e983f2a1 100644
--- a/src/services/api.ts
+++ b/src/services/api.ts
@@ -19,7 +19,6 @@ import { logger, safeStringify } from "../utils/logger";
* @remarks
* Special handling includes:
* - Enhanced logging for Capacitor platform
- * - Rate limit detection and handling
* - Detailed error information logging including:
* - Error message
* - HTTP status
@@ -50,11 +49,5 @@ export const handleApiError = (error: AxiosError, endpoint: string) => {
});
}
- // Specific handling for rate limits
- if (error.response?.status === 400) {
- logger.warn(`[Rate Limit] ${endpoint}`);
- return null;
- }
-
throw error;
};
diff --git a/src/utils/PlatformServiceMixin.ts b/src/utils/PlatformServiceMixin.ts
index 05dc08dd..ad0b249f 100644
--- a/src/utils/PlatformServiceMixin.ts
+++ b/src/utils/PlatformServiceMixin.ts
@@ -353,6 +353,14 @@ export const PlatformServiceMixin = {
? JSON.stringify(settings.searchBoxes)
: String(settings.searchBoxes);
}
+ if (settings.starredPlanHandleIds !== undefined) {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ (converted as any).starredPlanHandleIds = Array.isArray(
+ settings.starredPlanHandleIds,
+ )
+ ? JSON.stringify(settings.starredPlanHandleIds)
+ : String(settings.starredPlanHandleIds);
+ }
return converted;
},
@@ -555,6 +563,12 @@ export const PlatformServiceMixin = {
if (settings.searchBoxes) {
settings.searchBoxes = this._parseJsonField(settings.searchBoxes, []);
}
+ if (settings.starredPlanHandleIds) {
+ settings.starredPlanHandleIds = this._parseJsonField(
+ settings.starredPlanHandleIds,
+ [],
+ );
+ }
return settings;
} catch (error) {
@@ -621,6 +635,12 @@ export const PlatformServiceMixin = {
[],
);
}
+ if (mergedSettings.starredPlanHandleIds) {
+ mergedSettings.starredPlanHandleIds = this._parseJsonField(
+ mergedSettings.starredPlanHandleIds,
+ [],
+ );
+ }
return mergedSettings;
} catch (error) {
diff --git a/src/utils/logger.ts b/src/utils/logger.ts
index cf90daac..07e1566b 100644
--- a/src/utils/logger.ts
+++ b/src/utils/logger.ts
@@ -24,10 +24,28 @@ export function getMemoryLogs(): string[] {
return [..._memoryLogs];
}
+/**
+ * Stringify an object with proper handling of circular references and functions
+ *
+ * Don't use for arrays; map with this over the array.
+ *
+ * @param obj - The object to stringify
+ * @returns The stringified object, plus 'message' and 'stack' for Error objects
+ */
export function safeStringify(obj: unknown) {
const seen = new WeakSet();
- return JSON.stringify(obj, (_key, value) => {
+ // since 'message' & 'stack' are not enumerable for errors, let's add those
+ let objToStringify = obj;
+ if (obj instanceof Error) {
+ objToStringify = {
+ ...obj,
+ message: obj.message,
+ stack: obj.stack,
+ };
+ }
+
+ return JSON.stringify(objToStringify, (_key, value) => {
if (typeof value === "object" && value !== null) {
if (seen.has(value)) {
return "[Circular]";
@@ -178,7 +196,8 @@ export const logger = {
}
// Database logging
- const argsString = args.length > 0 ? " - " + safeStringify(args) : "";
+ const argsString =
+ args.length > 0 ? " - " + args.map(safeStringify).join(", ") : "";
logToDatabase(message + argsString, "info");
},
@@ -189,7 +208,8 @@ export const logger = {
}
// Database logging
- const argsString = args.length > 0 ? " - " + safeStringify(args) : "";
+ const argsString =
+ args.length > 0 ? " - " + args.map(safeStringify).join(", ") : "";
logToDatabase(message + argsString, "info");
},
@@ -200,7 +220,8 @@ export const logger = {
}
// Database logging
- const argsString = args.length > 0 ? " - " + safeStringify(args) : "";
+ const argsString =
+ args.length > 0 ? " - " + args.map(safeStringify).join(", ") : "";
logToDatabase(message + argsString, "warn");
},
@@ -211,9 +232,9 @@ export const logger = {
}
// Database logging
- const messageString = safeStringify(message);
- const argsString = args.length > 0 ? safeStringify(args) : "";
- logToDatabase(messageString + argsString, "error");
+ const argsString =
+ args.length > 0 ? " - " + args.map(safeStringify).join(", ") : "";
+ logToDatabase(message + argsString, "error");
},
// New database-focused methods (self-contained)
diff --git a/src/views/AccountViewView.vue b/src/views/AccountViewView.vue
index aba86705..d30f1798 100644
--- a/src/views/AccountViewView.vue
+++ b/src/views/AccountViewView.vue
@@ -1453,9 +1453,6 @@ export default class AccountViewView extends Vue {
if (imageResp && imageResp.status === 200) {
this.imageLimits = imageResp.data;
- } else {
- this.limitsMessage = ACCOUNT_VIEW_CONSTANTS.LIMITS.NO_IMAGE_ACCESS;
- this.notify.warning(ACCOUNT_VIEW_CONSTANTS.LIMITS.CANNOT_UPLOAD_IMAGES);
}
const endorserResp = await fetchEndorserRateLimits(
@@ -1466,9 +1463,6 @@ export default class AccountViewView extends Vue {
if (endorserResp.status === 200) {
this.endorserLimits = endorserResp.data;
- } else {
- this.limitsMessage = ACCOUNT_VIEW_CONSTANTS.LIMITS.NO_LIMITS_FOUND;
- this.notify.warning(ACCOUNT_VIEW_CONSTANTS.LIMITS.BAD_SERVER_RESPONSE);
}
} catch (error) {
this.limitsMessage =
diff --git a/src/views/ContactAmountsView.vue b/src/views/ContactAmountsView.vue
index b34b3d11..18f600c7 100644
--- a/src/views/ContactAmountsView.vue
+++ b/src/views/ContactAmountsView.vue
@@ -223,7 +223,7 @@ export default class ContactAmountssView extends Vue {
const contact = await this.$getContact(contactDid);
this.contact = contact;
- const settings = await this.$settings();
+ const settings = await this.$accountSettings();
// Get activeDid from active_identity table (single source of truth)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue
index d7e23fc1..8fac7b7b 100644
--- a/src/views/HomeView.vue
+++ b/src/views/HomeView.vue
@@ -421,6 +421,18 @@ export default class HomeView extends Vue {
numNewOffersToUser: number = 0; // number of new offers-to-user
numNewOffersToUserProjects: number = 0; // number of new offers-to-user's-projects
+ numNewStarredProjectChanges: number = 0; // number of new starred project changes
+ starredPlanHandleIds: Array = []; // list of starred project IDs
+ searchBoxes: Array<{
+ name: string;
+ bbox: BoundingBox;
+ }> = [];
+ showShortcutBvc = false;
+ userAgentInfo = new UAParser(); // see https://docs.uaparser.js.org/v2/api/ua-parser-js/get-os.html
+ selectedImage = "";
+ isImageViewerOpen = false;
+ showProjectsDialog = false;
+
/**
* CRITICAL VUE REACTIVITY BUG WORKAROUND
*
@@ -458,18 +470,6 @@ export default class HomeView extends Vue {
// return shouldShow;
// }
- numNewStarredProjectChanges: number = 0; // number of new starred project changes
- starredPlanHandleIds: Array = []; // list of starred project IDs
- searchBoxes: Array<{
- name: string;
- bbox: BoundingBox;
- }> = [];
- showShortcutBvc = false;
- userAgentInfo = new UAParser(); // see https://docs.uaparser.js.org/v2/api/ua-parser-js/get-os.html
- selectedImage = "";
- isImageViewerOpen = false;
- showProjectsDialog = false;
-
/**
* Initializes notification helpers
*/
@@ -734,7 +734,7 @@ export default class HomeView extends Vue {
* Used for displaying contact info in feed and actions
*
* @internal
- * Called by mounted() and initializeIdentity()
+ * Called by initializeIdentity()
*/
private async loadContacts() {
this.allContacts = await this.$contacts();
@@ -748,7 +748,6 @@ export default class HomeView extends Vue {
* Triggers updateAllFeed() to populate activity feed
*
* @internal
- * Called by mounted()
*/
private async loadFeedData() {
await this.updateAllFeed();
@@ -762,7 +761,6 @@ export default class HomeView extends Vue {
* - Rate limit status for both
*
* @internal
- * Called by mounted() and initializeIdentity()
* @requires Active DID
*/
private async loadNewOffers() {
@@ -873,7 +871,6 @@ export default class HomeView extends Vue {
* - Rate limit status for starred project changes
*
* @internal
- * Called by mounted() and initializeIdentity()
* @requires Active DID
*/
private async loadNewStarredProjectChanges() {
diff --git a/src/views/ProjectViewView.vue b/src/views/ProjectViewView.vue
index c54067be..d92b05cc 100644
--- a/src/views/ProjectViewView.vue
+++ b/src/views/ProjectViewView.vue
@@ -170,7 +170,7 @@
-
+
@@ -303,10 +303,7 @@
{{ offer.objectDescription }}
-
+
= [];
+ /** Project JWT ID */
+ jwtId = "";
/** Project location data */
latitude = 0;
loadingTotals = false;
@@ -910,6 +908,7 @@ export default class ProjectViewView extends Vue {
this.allContacts,
);
this.issuerVisibleToDids = resp.data.issuerVisibleToDids || [];
+ this.jwtId = resp.data.id;
this.name = resp.data.claim?.name || "(no name)";
this.description = resp.data.claim?.description || "";
this.truncatedDesc = this.description.slice(0, this.truncateLength);
@@ -1509,11 +1508,10 @@ export default class ProjectViewView extends Vue {
if (!this.projectId) return;
try {
+ const settings = await this.$accountSettings();
+ const starredIds = settings.starredPlanHandleIds || [];
if (!this.isStarred) {
// Add to starred projects
- const settings = await databaseUtil.retrieveSettingsForActiveAccount();
- const starredIds = settings.starredPlanHandleIds || [];
-
if (!starredIds.includes(this.projectId)) {
const newStarredIds = [...starredIds, this.projectId];
const newIdsParam = JSON.stringify(newStarredIds);
@@ -1526,20 +1524,16 @@ export default class ProjectViewView extends Vue {
this.isStarred = true;
} else {
// eslint-disable-next-line no-console
- console.log(
- "Still getting a bad result from SQL update to star a project.",
- );
+ logger.error("Got a bad result from SQL update to star a project.");
}
}
if (!settings.lastAckedStarredPlanChangesJwtId) {
await databaseUtil.updateDidSpecificSettings(this.activeDid, {
- lastAckedStarredPlanChangesJwtId: settings.lastViewedClaimId,
+ lastAckedStarredPlanChangesJwtId: this.jwtId,
});
}
} else {
// Remove from starred projects
- const settings = await databaseUtil.retrieveSettingsForActiveAccount();
- const starredIds = settings.starredPlanHandleIds || [];
const updatedIds = starredIds.filter((id) => id !== this.projectId);
const newIdsParam = JSON.stringify(updatedIds);