Browse Source

add recipient description to offers in user's list

Trent Larson 3 months ago
parent
commit
4244e6b279
  1. 9
      CONTRIBUTING.md
  2. 8
      src/libs/endorserServer.ts
  3. 62
      src/views/ProjectsView.vue
  4. 4
      test-playwright/50-record-offer.spec.ts

9
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.

8
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<string, PlanSummaryRecord> = 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<string, PlanSummaryRecord> = 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

62
src/views/ProjectsView.vue

@ -109,6 +109,19 @@
</div>
<div>
<div>
To
{{
offer.fulfillsPlanHandleId
? projectNameFromHandleId[offer.fulfillsPlanHandleId]
: didInfo(
offer.recipientDid,
activeDid,
allMyDids,
allContacts,
)
}}
</div>
<div>
{{ offer.objectDescription }}
</div>
@ -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<Contact> = [];
allMyDids: Array<string> = [];
apiServer = "";
projects: PlanData[] = [];
isLoading = false;
isRegistered = false;
numAccounts = 0;
offers: OfferSummaryRecord[] = [];
projectNameFromHandleId: Record<string, string> = {}; // 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:",

4
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');

Loading…
Cancel
Save