diff --git a/package-lock.json b/package-lock.json
index 693dac6f..04d2b408 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -27,6 +27,7 @@
"@ethersproject/hdnode": "^5.7.0",
"@ethersproject/wallet": "^5.8.0",
"@fortawesome/fontawesome-svg-core": "^6.5.1",
+ "@fortawesome/free-regular-svg-icons": "^6.7.2",
"@fortawesome/free-solid-svg-icons": "^6.5.1",
"@fortawesome/vue-fontawesome": "^3.0.6",
"@jlongster/sql.js": "^1.6.7",
@@ -90,6 +91,7 @@
"vue": "3.5.13",
"vue-axios": "^3.5.2",
"vue-facing-decorator": "3.0.4",
+ "vue-markdown-render": "^2.2.1",
"vue-picture-cropper": "^0.7.0",
"vue-qrcode-reader": "^5.5.3",
"vue-router": "^4.5.0",
@@ -106,6 +108,7 @@
"@types/js-yaml": "^4.0.9",
"@types/leaflet": "^1.9.8",
"@types/luxon": "^3.4.2",
+ "@types/markdown-it": "^14.1.2",
"@types/node": "^20.14.11",
"@types/node-fetch": "^2.6.12",
"@types/ramda": "^0.29.11",
@@ -6786,6 +6789,17 @@
"node": ">=6"
}
},
+ "node_modules/@fortawesome/free-regular-svg-icons": {
+ "version": "6.7.2",
+ "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.7.2.tgz",
+ "integrity": "sha512-7Z/ur0gvCMW8G93dXIQOkQqHo2M5HLhYrRVC0//fakJXxcF1VmMPsxnG6Ee8qEylA8b8Q3peQXWMNZ62lYF28g==",
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "6.7.2"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/@fortawesome/free-solid-svg-icons": {
"version": "6.7.2",
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.7.2.tgz",
@@ -10147,6 +10161,12 @@
"@types/geojson": "*"
}
},
+ "node_modules/@types/linkify-it": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
+ "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
+ "dev": true
+ },
"node_modules/@types/luxon": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.7.1.tgz",
@@ -10154,6 +10174,22 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/markdown-it": {
+ "version": "14.1.2",
+ "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz",
+ "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==",
+ "dev": true,
+ "dependencies": {
+ "@types/linkify-it": "^5",
+ "@types/mdurl": "^2"
+ }
+ },
+ "node_modules/@types/mdurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
+ "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
+ "dev": true
+ },
"node_modules/@types/minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz",
@@ -32883,6 +32919,61 @@
"vue": "^3.0.0"
}
},
+ "node_modules/vue-markdown-render": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/vue-markdown-render/-/vue-markdown-render-2.2.1.tgz",
+ "integrity": "sha512-XkYnC0PMdbs6Vy6j/gZXSvCuOS0787Se5COwXlepRqiqPiunyCIeTPQAO2XnB4Yl04EOHXwLx5y6IuszMWSgyQ==",
+ "dependencies": {
+ "markdown-it": "^13.0.2"
+ },
+ "peerDependencies": {
+ "vue": "^3.3.4"
+ }
+ },
+ "node_modules/vue-markdown-render/node_modules/entities": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
+ "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/vue-markdown-render/node_modules/linkify-it": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz",
+ "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==",
+ "dependencies": {
+ "uc.micro": "^1.0.1"
+ }
+ },
+ "node_modules/vue-markdown-render/node_modules/markdown-it": {
+ "version": "13.0.2",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.2.tgz",
+ "integrity": "sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w==",
+ "dependencies": {
+ "argparse": "^2.0.1",
+ "entities": "~3.0.1",
+ "linkify-it": "^4.0.1",
+ "mdurl": "^1.0.1",
+ "uc.micro": "^1.0.5"
+ },
+ "bin": {
+ "markdown-it": "bin/markdown-it.js"
+ }
+ },
+ "node_modules/vue-markdown-render/node_modules/mdurl": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
+ "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g=="
+ },
+ "node_modules/vue-markdown-render/node_modules/uc.micro": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
+ "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
+ },
"node_modules/vue-picture-cropper": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/vue-picture-cropper/-/vue-picture-cropper-0.7.0.tgz",
diff --git a/package.json b/package.json
index 247a8044..a9587886 100644
--- a/package.json
+++ b/package.json
@@ -136,7 +136,6 @@
"*.{js,ts,vue,css,json,yml,yaml}": "eslint --fix || true",
"*.{md,markdown,mdc}": "markdownlint-cli2 --fix"
},
-
"dependencies": {
"@capacitor-community/electron": "^5.0.1",
"@capacitor-community/sqlite": "6.0.2",
@@ -157,6 +156,7 @@
"@ethersproject/hdnode": "^5.7.0",
"@ethersproject/wallet": "^5.8.0",
"@fortawesome/fontawesome-svg-core": "^6.5.1",
+ "@fortawesome/free-regular-svg-icons": "^6.7.2",
"@fortawesome/free-solid-svg-icons": "^6.5.1",
"@fortawesome/vue-fontawesome": "^3.0.6",
"@jlongster/sql.js": "^1.6.7",
@@ -220,6 +220,7 @@
"vue": "3.5.13",
"vue-axios": "^3.5.2",
"vue-facing-decorator": "3.0.4",
+ "vue-markdown-render": "^2.2.1",
"vue-picture-cropper": "^0.7.0",
"vue-qrcode-reader": "^5.5.3",
"vue-router": "^4.5.0",
@@ -236,6 +237,7 @@
"@types/js-yaml": "^4.0.9",
"@types/leaflet": "^1.9.8",
"@types/luxon": "^3.4.2",
+ "@types/markdown-it": "^14.1.2",
"@types/node": "^20.14.11",
"@types/node-fetch": "^2.6.12",
"@types/ramda": "^0.29.11",
diff --git a/src/assets/styles/tailwind.css b/src/assets/styles/tailwind.css
index f6457ff3..7785f095 100644
--- a/src/assets/styles/tailwind.css
+++ b/src/assets/styles/tailwind.css
@@ -22,4 +22,24 @@
.dialog {
@apply bg-white p-4 rounded-lg w-full max-w-lg;
}
+
+ /* Markdown content styling to restore list elements */
+ .markdown-content ul {
+ @apply list-disc list-inside ml-4;
+ }
+
+ .markdown-content ol {
+ @apply list-decimal list-inside ml-4;
+ }
+
+ .markdown-content li {
+ @apply mb-1;
+ }
+
+ .markdown-content ul ul,
+ .markdown-content ol ol,
+ .markdown-content ul ol,
+ .markdown-content ol ul {
+ @apply ml-4 mt-1;
+ }
}
\ No newline at end of file
diff --git a/src/components/ActivityListItem.vue b/src/components/ActivityListItem.vue
index 39dfcffa..33b50d02 100644
--- a/src/components/ActivityListItem.vue
+++ b/src/components/ActivityListItem.vue
@@ -80,7 +80,10 @@
- {{ description }}
+
@@ -258,11 +261,13 @@ import {
NOTIFY_UNKNOWN_PERSON,
} from "@/constants/notifications";
import { TIMEOUTS } from "@/utils/notify";
+import VueMarkdown from "vue-markdown-render";
@Component({
components: {
EntityIcon,
ProjectIcon,
+ VueMarkdown,
},
})
export default class ActivityListItem extends Vue {
@@ -303,6 +308,14 @@ export default class ActivityListItem extends Vue {
return `${claim?.description || ""}`;
}
+ get truncatedDescription(): string {
+ const desc = this.description;
+ if (desc.length <= 300) {
+ return desc;
+ }
+ return desc.substring(0, 300) + "...";
+ }
+
private displayAmount(code: string, amt: number) {
return `${amt} ${this.currencyShortWordForCode(code, amt === 1)}`;
}
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-sql/migration.ts b/src/db-sql/migration.ts
index f686d155..ca5dad14 100644
--- a/src/db-sql/migration.ts
+++ b/src/db-sql/migration.ts
@@ -192,6 +192,13 @@ const MIGRATIONS = [
name: "004_active_identity_management",
sql: MIG_004_SQL,
},
+ {
+ name: "005_add_starredPlanHandleIds_to_settings",
+ sql: `
+ ALTER TABLE settings ADD COLUMN starredPlanHandleIds TEXT DEFAULT '[]'; -- JSON string
+ ALTER TABLE settings ADD COLUMN lastAckedStarredPlanChangesJwtId TEXT;
+ `,
+ },
];
/**
diff --git a/src/db/databaseUtil.ts b/src/db/databaseUtil.ts
index 85b7192f..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) {
@@ -157,10 +130,11 @@ export async function retrieveSettingsForDefaultAccount(): Promise {
result.columns,
result.values,
)[0] as Settings;
- if (settings.searchBoxes) {
- // @ts-expect-error - the searchBoxes field is a string in the DB
- settings.searchBoxes = JSON.parse(settings.searchBoxes);
- }
+ settings.searchBoxes = parseJsonField(settings.searchBoxes, []);
+ settings.starredPlanHandleIds = parseJsonField(
+ settings.starredPlanHandleIds,
+ [],
+ );
return settings;
}
}
@@ -226,10 +200,11 @@ export async function retrieveSettingsForActiveAccount(): Promise {
);
}
- // Handle searchBoxes parsing
- if (settings.searchBoxes) {
- settings.searchBoxes = parseJsonField(settings.searchBoxes, []);
- }
+ settings.searchBoxes = parseJsonField(settings.searchBoxes, []);
+ settings.starredPlanHandleIds = parseJsonField(
+ settings.starredPlanHandleIds,
+ [],
+ );
return settings;
} catch (error) {
diff --git a/src/db/tables/settings.ts b/src/db/tables/settings.ts
index 4c00b46e..493e4596 100644
--- a/src/db/tables/settings.ts
+++ b/src/db/tables/settings.ts
@@ -43,6 +43,7 @@ export type Settings = {
lastAckedOfferToUserJwtId?: string; // the last JWT ID for offer-to-user that they've acknowledged seeing
lastAckedOfferToUserProjectsJwtId?: string; // the last JWT ID for offers-to-user's-projects that they've acknowledged seeing
+ lastAckedStarredPlanChangesJwtId?: string; // the last JWT ID for starred plan changes that they've acknowledged seeing
// The claim list has a most recent one used in notifications that's separate from the last viewed
lastNotifiedClaimId?: string;
@@ -67,15 +68,18 @@ export type Settings = {
showContactGivesInline?: boolean; // Display contact inline or not
showGeneralAdvanced?: boolean; // Show advanced features which don't have their own flag
showShortcutBvc?: boolean; // Show shortcut for Bountiful Voluntaryist Community actions
+
+ starredPlanHandleIds?: string[]; // Array of starred plan handle IDs
vapid?: string; // VAPID (Voluntary Application Server Identification) field for web push
warnIfProdServer?: boolean; // Warn if using a production server
warnIfTestServer?: boolean; // Warn if using a testing server
webPushServer?: string; // Web Push server URL
};
-// type of settings where the searchBoxes are JSON strings instead of objects
+// type of settings where the values are JSON strings instead of objects
export type SettingsWithJsonStrings = Settings & {
searchBoxes: string;
+ starredPlanHandleIds: string;
};
export function checkIsAnyFeedFilterOn(settings: Settings): boolean {
@@ -92,6 +96,11 @@ export const SettingsSchema = {
/**
* Constants.
*/
+
+/**
+ * This is deprecated.
+ * It only remains for those with a PWA who have not migrated, but we'll soon remove it.
+ */
export const MASTER_SETTINGS_KEY = "1";
export const DEFAULT_PASSKEY_EXPIRATION_MINUTES = 15;
diff --git a/src/interfaces/claims.ts b/src/interfaces/claims.ts
index c028858b..1fc03529 100644
--- a/src/interfaces/claims.ts
+++ b/src/interfaces/claims.ts
@@ -72,11 +72,15 @@ export interface PlanActionClaim extends ClaimObject {
name: string;
agent?: { identifier: string };
description?: string;
+ endTime?: string;
identifier?: string;
+ image?: string;
lastClaimId?: string;
location?: {
geo: { "@type": "GeoCoordinates"; latitude: number; longitude: number };
};
+ startTime?: string;
+ url?: string;
}
// AKA Registration & RegisterAction
diff --git a/src/interfaces/records.ts b/src/interfaces/records.ts
index 7a884f0c..ca82624c 100644
--- a/src/interfaces/records.ts
+++ b/src/interfaces/records.ts
@@ -1,4 +1,5 @@
-import { GiveActionClaim, OfferClaim } from "./claims";
+import { GiveActionClaim, OfferClaim, PlanActionClaim } from "./claims";
+import { GenericCredWrapper } from "./common";
// a summary record; the VC is found the fullClaim field
export interface GiveSummaryRecord {
@@ -61,6 +62,11 @@ export interface PlanSummaryRecord {
jwtId?: string;
}
+export interface PlanSummaryAndPreviousClaim {
+ plan: PlanSummaryRecord;
+ wrappedClaimBefore: GenericCredWrapper;
+}
+
/**
* Represents data about a project
*
@@ -87,7 +93,10 @@ export interface PlanData {
name: string;
/**
* The identifier of the project record -- different from jwtId
- * (Maybe we should use the jwtId to iterate through the records instead.)
+ *
+ * This has been used to iterate through plan records, because jwtId ordering doesn't match
+ * chronological create ordering, though it does match most recent edit order (in reverse order).
+ * (It may be worthwhile to order by jwtId instead. It is an indexed field.)
**/
rowId?: string;
}
diff --git a/src/libs/endorserServer.ts b/src/libs/endorserServer.ts
index 30bb7316..a0e2bf6c 100644
--- a/src/libs/endorserServer.ts
+++ b/src/libs/endorserServer.ts
@@ -56,7 +56,12 @@ import {
KeyMetaWithPrivate,
KeyMetaMaybeWithPrivate,
} from "../interfaces/common";
-import { PlanSummaryRecord } from "../interfaces/records";
+import {
+ OfferSummaryRecord,
+ OfferToPlanSummaryRecord,
+ PlanSummaryAndPreviousClaim,
+ PlanSummaryRecord,
+} from "../interfaces/records";
import { logger } from "../utils/logger";
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
import { APP_SERVER } from "@/constants/app";
@@ -362,6 +367,22 @@ export function didInfo(
return didInfoForContact(did, activeDid, contact, allMyDids).displayName;
}
+/**
+ * In some contexts (eg. agent), a blank really is nobody.
+ */
+export function didInfoOrNobody(
+ did: string | undefined,
+ activeDid: string | undefined,
+ allMyDids: string[],
+ contacts: Contact[],
+): string {
+ if (did == null) {
+ return "Nobody";
+ } else {
+ return didInfo(did, activeDid, allMyDids, contacts);
+ }
+}
+
/**
* return text description without any references to "you" as user
*/
@@ -730,7 +751,7 @@ export async function getNewOffersToUser(
activeDid: string,
afterOfferJwtId?: string,
beforeOfferJwtId?: string,
-) {
+): Promise<{ data: Array; hitLimit: boolean }> {
let url = `${apiServer}/api/v2/report/offers?recipientDid=${activeDid}`;
if (afterOfferJwtId) {
url += "&afterId=" + afterOfferJwtId;
@@ -752,7 +773,7 @@ export async function getNewOffersToUserProjects(
activeDid: string,
afterOfferJwtId?: string,
beforeOfferJwtId?: string,
-) {
+): Promise<{ data: Array; hitLimit: boolean }> {
let url = `${apiServer}/api/v2/report/offersToPlansOwnedByMe`;
if (afterOfferJwtId) {
url += "?afterId=" + afterOfferJwtId;
@@ -766,6 +787,46 @@ export async function getNewOffersToUserProjects(
return response.data;
}
+/**
+ * Get starred projects that have been updated since the last check
+ *
+ * @param axios - axios instance
+ * @param apiServer - endorser API server URL
+ * @param activeDid - user's DID for authentication
+ * @param starredPlanHandleIds - array of starred project handle IDs
+ * @param afterId - JWT ID to check for changes after (from lastAckedStarredPlanChangesJwtId)
+ * @returns { data: Array, hitLimit: boolean }
+ */
+export async function getStarredProjectsWithChanges(
+ axios: Axios,
+ apiServer: string,
+ activeDid: string,
+ starredPlanHandleIds: string[],
+ afterId?: string,
+): Promise<{ data: Array; hitLimit: boolean }> {
+ if (!starredPlanHandleIds || starredPlanHandleIds.length === 0) {
+ return { data: [], hitLimit: false };
+ }
+
+ 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 };
+ }
+
+ // Use POST method for larger lists of project IDs
+ const url = `${apiServer}/api/v2/report/plansLastUpdatedBetween`;
+ const headers = await getHeaders(activeDid);
+
+ const requestBody = {
+ planIds: starredPlanHandleIds,
+ afterId: afterId,
+ };
+
+ const response = await axios.post(url, requestBody, { headers });
+ return response.data;
+}
+
/**
* Construct GiveAction VC for submission to server
*
@@ -1697,49 +1758,19 @@ export async function fetchEndorserRateLimits(
timestamp: new Date().toISOString(),
});
- try {
- const response = await axios.get(url, { headers } as AxiosRequestConfig);
+ // not wrapped in a 'try' because the error returned is self-explanatory
+ 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(),
- });
-
- // 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;
}
/**
@@ -1788,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/libs/fontawesome.ts b/src/libs/fontawesome.ts
index 30c745c7..efd8ff03 100644
--- a/src/libs/fontawesome.ts
+++ b/src/libs/fontawesome.ts
@@ -86,6 +86,7 @@ import {
faSquareCaretDown,
faSquareCaretUp,
faSquarePlus,
+ faStar,
faThumbtack,
faTrashCan,
faTriangleExclamation,
@@ -94,6 +95,9 @@ import {
faXmark,
} from "@fortawesome/free-solid-svg-icons";
+// these are referenced differently, eg. ":icon='['far', 'star']'" as in ProjectViewView.vue
+import { faStar as faStarRegular } from "@fortawesome/free-regular-svg-icons";
+
// Initialize Font Awesome library with all required icons
library.add(
faArrowDown,
@@ -168,14 +172,16 @@ library.add(
faPlus,
faQrcode,
faQuestion,
- faRotate,
faRightFromBracket,
+ faRotate,
faShareNodes,
faSpinner,
faSquare,
faSquareCaretDown,
faSquareCaretUp,
faSquarePlus,
+ faStar,
+ faStarRegular,
faThumbtack,
faTrashCan,
faTriangleExclamation,
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/test/PlatformServiceMixinTest.vue b/src/test/PlatformServiceMixinTest.vue
index 219c72cf..98f5325c 100644
--- a/src/test/PlatformServiceMixinTest.vue
+++ b/src/test/PlatformServiceMixinTest.vue
@@ -85,7 +85,6 @@
diff --git a/src/views/ProjectViewView.vue b/src/views/ProjectViewView.vue
index 13aef08c..d92b05cc 100644
--- a/src/views/ProjectViewView.vue
+++ b/src/views/ProjectViewView.vue
@@ -27,10 +27,18 @@
>
-