From 4244e6b279dfda504f710ad3dcc07b7c99f7ba67 Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Mon, 12 Aug 2024 20:38:54 -0600 Subject: [PATCH] add recipient description to offers in user's list --- CONTRIBUTING.md | 9 +++- src/libs/endorserServer.ts | 8 ++-- src/views/ProjectsView.vue | 62 +++++++++++++++++++++---- test-playwright/50-record-offer.spec.ts | 4 +- 4 files changed, 66 insertions(+), 17 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 26c03883b..c7885c565 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,5 +2,10 @@ Welcome! We are happy to have your help with this project. -Note that all contributions will be under our -[license, modeled after SQLite](https://github.com/trentlarson/endorser-ch/blob/master/LICENSE). +We expect contributions to include automated tests and pass linting. Run the `test-all` task. +Note that some previous features don't have tests and adding more will make you friends quick. + +Note that all contributions will be under our [license, modeled after SQLite](https://github.com/trentlarson/endorser-ch/blob/master/LICENSE). + +If you want to see a code of conduct, we're probably not the people you want to hang with. +Basically, we'll work together as long as we both enjoy it, and we'll stop when that stops. diff --git a/src/libs/endorserServer.ts b/src/libs/endorserServer.ts index 97b943b79..d7475a8d7 100644 --- a/src/libs/endorserServer.ts +++ b/src/libs/endorserServer.ts @@ -267,10 +267,6 @@ export type CreateAndSubmitClaimResult = SuccessResult | ErrorResult; // See https://github.com/trentlarson/endorser-ch/blob/0cb626f803028e7d9c67f095858a9fc8542e3dbd/server/api/services/util.js#L6 const HIDDEN_DID = "did:none:HIDDEN"; -const planCache: LRUCache = new LRUCache({ - max: 500, -}); - export function isDid(did: string) { return did.startsWith("did:"); } @@ -507,6 +503,10 @@ export async function getHeaders(did?: string) { return headers; } +const planCache: LRUCache = new LRUCache({ + max: 500, +}); + /** * @param handleId nullable, in which case "undefined" will be returned * @param requesterDid optional, in which case no private info will be returned diff --git a/src/views/ProjectsView.vue b/src/views/ProjectsView.vue index 165a85e6a..d9394775c 100644 --- a/src/views/ProjectsView.vue +++ b/src/views/ProjectsView.vue @@ -109,6 +109,19 @@
+
+ To + {{ + offer.fulfillsPlanHandleId + ? projectNameFromHandleId[offer.fulfillsPlanHandleId] + : didInfo( + offer.recipientDid, + activeDid, + allMyDids, + allContacts, + ) + }} +
{{ offer.objectDescription }}
@@ -244,11 +257,14 @@ import QuickNav from "@/components/QuickNav.vue"; import ProjectIcon from "@/components/ProjectIcon.vue"; import TopMessage from "@/components/TopMessage.vue"; import { + didInfo, getHeaders, + getPlanFromCache, OfferSummaryRecord, PlanData, } from "@/libs/endorserServer"; import EntityIcon from "@/components/EntityIcon.vue"; +import { Contact } from "@/db/tables/contacts"; @Component({ components: { EntityIcon, InfiniteScroll, QuickNav, ProjectIcon, TopMessage }, @@ -263,16 +279,19 @@ export default class ProjectsView extends Vue { } activeDid = ""; + allContacts: Array = []; + allMyDids: Array = []; apiServer = ""; projects: PlanData[] = []; isLoading = false; isRegistered = false; - numAccounts = 0; offers: OfferSummaryRecord[] = []; + projectNameFromHandleId: Record = {}; // mapping from handleId to description showOffers = true; showProjects = false; libsUtil = libsUtil; + didInfo = didInfo; async mounted() { try { @@ -282,9 +301,13 @@ export default class ProjectsView extends Vue { this.apiServer = (settings?.apiServer as string) || ""; this.isRegistered = !!settings?.isRegistered; + this.allContacts = await db.contacts.toArray(); + await accountsDB.open(); - this.numAccounts = await accountsDB.accounts.count(); - if (this.numAccounts === 0) { + const allAccounts = await accountsDB.accounts.toArray(); + this.allMyDids = allAccounts.map((acc) => acc.did); + + if (allAccounts.length === 0) { console.error("No accounts found."); this.errNote("You need an identifier to load your projects."); } else { @@ -343,10 +366,7 @@ export default class ProjectsView extends Vue { async loadMoreProjectData(payload: boolean) { if (this.projects.length > 0 && payload) { const latestProject = this.projects[this.projects.length - 1]; - await this.loadProjects( - this.activeDid, - `beforeId=${latestProject.rowid}`, - ); + await this.loadProjects(`beforeId=${latestProject.rowid}`); } } @@ -355,7 +375,7 @@ export default class ProjectsView extends Vue { * @param issuerDid of the user * @param urlExtra additional url parameters in a string **/ - async loadProjects(activeDid?: string, urlExtra: string = "") { + async loadProjects(urlExtra: string = "") { const url = `${this.apiServer}/api/v2/report/plansByIssuer?${urlExtra}`; await this.projectDataLoader(url); } @@ -402,7 +422,31 @@ export default class ProjectsView extends Vue { this.isLoading = true; const resp = await this.axios.get(url, { headers } as AxiosRequestConfig); if (resp.status === 200 && resp.data.data) { - this.offers = this.offers.concat(resp.data.data); + // add one-by-one as they retrieve project names, potentially from the server + for (const offer of resp.data.data) { + if (offer.fulfillsPlanHandleId) { + const project = await getPlanFromCache( + offer.fulfillsPlanHandleId, + this.axios, + this.apiServer, + this.activeDid, + ); + const projectName = project?.name as string; + console.log( + "now have name for", + offer.fulfillsPlanHandleId, + projectName, + ); + this.projectNameFromHandleId[offer.fulfillsPlanHandleId] = + projectName; + console.log( + "now have a real name for", + offer.fulfillsPlanHandleId, + this.projectNameFromHandleId[offer.fulfillsPlanHandleId], + ); + } + this.offers = this.offers.concat([offer]); + } } else { console.error( "Bad server response & data for offers:", diff --git a/test-playwright/50-record-offer.spec.ts b/test-playwright/50-record-offer.spec.ts index 031c99ffe..c3d5aacd0 100644 --- a/test-playwright/50-record-offer.spec.ts +++ b/test-playwright/50-record-offer.spec.ts @@ -5,7 +5,7 @@ test('Record an offer', async ({ page }) => { // Generate a random string of 6 characters, skipping the "0." at the beginning const randomString = Math.random().toString(36).substring(2, 8); // Standard title prefix - const finalTitle = `Offer ${randomString}`; + const finalTitle = `Offering of ${randomString}`; const randomNonZeroNumber = Math.floor(Math.random() * 999) + 1; // Create new ID for default user @@ -24,7 +24,7 @@ test('Record an offer', async ({ page }) => { // Refresh home view and check gift await page.goto('./projects'); - await page.locator('li').filter({ hasText: `All ${randomNonZeroNumber} remaining` }).locator('a').first().click(); + await page.locator('li').filter({ hasText: finalTitle }).locator('a').first().click(); await expect(page.getByRole('heading', { name: 'Verifiable Claim Details' })).toBeVisible(); await expect(page.getByText(finalTitle, { exact: true })).toBeVisible(); const page1Promise = page.waitForEvent('popup');