add display of my own offers
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
|
||||
tasks:
|
||||
|
||||
- change server plan & project endpoints to use jwtId as identifier rather than rowid
|
||||
- on mobile, don't capitalize first word & don't add spaces of website entry, and don't add spaces in numeric entry
|
||||
- edit offers & gives, or revoke allowing recreation
|
||||
- bug (that is hard to reproduce) - offer gave USD (by default?)
|
||||
- .1 When available in the server, give message for 'nonAmountGiven' on offers on ProjectsView page.
|
||||
- .5 fix timeSafari.org cert renewals
|
||||
- .2 anchor hash into BTC
|
||||
- 04 allow backup of localStorage key
|
||||
|
||||
@@ -80,9 +80,13 @@ export interface GiveServerRecord {
|
||||
export interface OfferServerRecord {
|
||||
amount: number;
|
||||
amountGiven: number;
|
||||
amountGivenConfirmed: number;
|
||||
fullClaim: OfferVerifiableCredential;
|
||||
fulfillsPlanHandleId: string;
|
||||
handleId: string;
|
||||
jwtId: string;
|
||||
nonAmountGivenConfirmed: number;
|
||||
objectDescription: string;
|
||||
offeredByDid: string;
|
||||
recipientDid: string;
|
||||
requirementsMet: boolean;
|
||||
@@ -145,6 +149,57 @@ export interface PlanVerifiableCredential {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents data about a project
|
||||
*
|
||||
* @deprecated
|
||||
* We should use PlanServerRecord instead.
|
||||
**/
|
||||
export interface PlanData {
|
||||
/**
|
||||
* Name of the project
|
||||
**/
|
||||
name: string;
|
||||
/**
|
||||
* Description of the project
|
||||
**/
|
||||
description: string;
|
||||
/**
|
||||
* URL referencing information about the project
|
||||
**/
|
||||
handleId: string;
|
||||
/**
|
||||
* The DID of the issuer
|
||||
*/
|
||||
issuerDid: string;
|
||||
/**
|
||||
* The Identier of the project -- different from jwtId, needs to be fixed
|
||||
**/
|
||||
rowid?: string;
|
||||
}
|
||||
|
||||
export interface RateLimits {
|
||||
doneClaimsThisWeek: string;
|
||||
doneRegistrationsThisMonth: string;
|
||||
maxClaimsPerWeek: string;
|
||||
maxRegistrationsPerMonth: string;
|
||||
nextMonthBeginDateTime: string;
|
||||
nextWeekBeginDateTime: string;
|
||||
}
|
||||
|
||||
export interface VerifiableCredential {
|
||||
"@context": string;
|
||||
"@type": string;
|
||||
name: string;
|
||||
description: string;
|
||||
identifier?: string;
|
||||
}
|
||||
|
||||
export interface WorldProperties {
|
||||
startTime?: string;
|
||||
endTime?: string;
|
||||
}
|
||||
|
||||
export interface RegisterVerifiableCredential {
|
||||
"@context": string;
|
||||
"@type": string;
|
||||
@@ -153,11 +208,35 @@ export interface RegisterVerifiableCredential {
|
||||
participant: { identifier: string };
|
||||
}
|
||||
|
||||
// now for some of the error & other wrapper types
|
||||
|
||||
export interface ResultWithType {
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface SuccessResult extends ResultWithType {
|
||||
type: "success";
|
||||
response: AxiosResponse<ClaimResult>;
|
||||
}
|
||||
|
||||
export interface ErrorResponse {
|
||||
error?: {
|
||||
message?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ErrorResult {
|
||||
type: "error";
|
||||
error: InternalError;
|
||||
}
|
||||
|
||||
export interface InternalError {
|
||||
error: string; // for system logging
|
||||
userMessage?: string; // for user display
|
||||
}
|
||||
|
||||
export type CreateAndSubmitClaimResult = SuccessResult | ErrorResult;
|
||||
|
||||
// This is used to check for hidden info.
|
||||
// See https://github.com/trentlarson/endorser-ch/blob/0cb626f803028e7d9c67f095858a9fc8542e3dbd/server/api/services/util.js#L6
|
||||
const HIDDEN_DID = "did:none:HIDDEN";
|
||||
@@ -294,22 +373,6 @@ export function didInfo(
|
||||
}
|
||||
}
|
||||
|
||||
export interface ResultWithType {
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface SuccessResult extends ResultWithType {
|
||||
type: "success";
|
||||
response: AxiosResponse<ClaimResult>;
|
||||
}
|
||||
|
||||
export interface ErrorResult {
|
||||
type: "error";
|
||||
error: InternalError;
|
||||
}
|
||||
|
||||
export type CreateAndSubmitClaimResult = SuccessResult | ErrorResult;
|
||||
|
||||
/**
|
||||
* For result, see https://api.endorser.ch/api-docs/#/claims/post_api_v2_claim
|
||||
*
|
||||
@@ -487,57 +550,3 @@ export function isNumeric(str: string): boolean {
|
||||
export function numberOrZero(str: string): number {
|
||||
return isNumeric(str) ? +str : 0;
|
||||
}
|
||||
|
||||
export interface ErrorResponse {
|
||||
error?: {
|
||||
message?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface RateLimits {
|
||||
doneClaimsThisWeek: string;
|
||||
doneRegistrationsThisMonth: string;
|
||||
maxClaimsPerWeek: string;
|
||||
maxRegistrationsPerMonth: string;
|
||||
nextMonthBeginDateTime: string;
|
||||
nextWeekBeginDateTime: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents data about a project
|
||||
**/
|
||||
export interface ProjectData {
|
||||
/**
|
||||
* Name of the project
|
||||
**/
|
||||
name: string;
|
||||
/**
|
||||
* Description of the project
|
||||
**/
|
||||
description: string;
|
||||
/**
|
||||
* URL referencing information about the project
|
||||
**/
|
||||
handleId: string;
|
||||
/**
|
||||
* The DID of the issuer
|
||||
*/
|
||||
issuerDid: string;
|
||||
/**
|
||||
* The Identier of the project
|
||||
**/
|
||||
rowid: string;
|
||||
}
|
||||
|
||||
export interface VerifiableCredential {
|
||||
"@context": string;
|
||||
"@type": string;
|
||||
name: string;
|
||||
description: string;
|
||||
identifier?: string;
|
||||
}
|
||||
|
||||
export interface WorldProperties {
|
||||
startTime?: string;
|
||||
endTime?: string;
|
||||
}
|
||||
|
||||
@@ -36,6 +36,25 @@ export const UNIT_LONG: Record<string, string> = {
|
||||
};
|
||||
/* eslint-enable prettier/prettier */
|
||||
|
||||
const UNIT_CODES: Record<string, Record<string, string>> = {
|
||||
BTC: {
|
||||
name: "Bitcoin",
|
||||
faIcon: "bitcoin-sign",
|
||||
},
|
||||
HUR: {
|
||||
name: "hours",
|
||||
faIcon: "clock",
|
||||
},
|
||||
USD: {
|
||||
name: "US Dollars",
|
||||
faIcon: "dollar",
|
||||
},
|
||||
};
|
||||
|
||||
export function iconForUnitCode(unitCode: string) {
|
||||
return UNIT_CODES[unitCode]?.faIcon || "question";
|
||||
}
|
||||
|
||||
export const isGlobalUri = (uri: string) => {
|
||||
return uri && uri.match(new RegExp(/^[A-Za-z][A-Za-z0-9+.-]+:/));
|
||||
};
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
<br />
|
||||
(Only most recent hours included. To see more, click
|
||||
<span
|
||||
class="text-sm uppercase bg-slate-500 text-white px-2 py-1.5 rounded-md"
|
||||
class="text-sm uppercase bg-slate-500 text-white px-1 py-1 rounded-md"
|
||||
>
|
||||
<fa icon="file-lines" class="fa-fw" />
|
||||
</span>
|
||||
|
||||
@@ -51,13 +51,13 @@
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
v-bind:class="computedRemoteTabClassNames()"
|
||||
@click="
|
||||
projects = [];
|
||||
isRemoteActive = true;
|
||||
isLocalActive = false;
|
||||
searchAll();
|
||||
"
|
||||
v-bind:class="computedRemoteTabClassNames()"
|
||||
>
|
||||
Anywhere
|
||||
<span
|
||||
@@ -133,7 +133,7 @@ import { accountsDB, db } from "@/db/index";
|
||||
import { Contact } from "@/db/tables/contacts";
|
||||
import { BoundingBox, MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
||||
import { accessToken } from "@/libs/crypto";
|
||||
import { didInfo, ProjectData } from "@/libs/endorserServer";
|
||||
import { didInfo, PlanData } from "@/libs/endorserServer";
|
||||
import QuickNav from "@/components/QuickNav.vue";
|
||||
import InfiniteScroll from "@/components/InfiniteScroll.vue";
|
||||
import EntityIcon from "@/components/EntityIcon.vue";
|
||||
@@ -164,7 +164,7 @@ export default class DiscoverView extends Vue {
|
||||
allMyDids: Array<string> = [];
|
||||
apiServer = "";
|
||||
searchTerms = "";
|
||||
projects: ProjectData[] = [];
|
||||
projects: PlanData[] = [];
|
||||
isLoading = false;
|
||||
isLocalActive = true;
|
||||
isRemoteActive = false;
|
||||
@@ -178,8 +178,8 @@ export default class DiscoverView extends Vue {
|
||||
async mounted() {
|
||||
await db.open();
|
||||
const settings = await db.settings.get(MASTER_SETTINGS_KEY);
|
||||
this.activeDid = settings?.activeDid || "";
|
||||
this.apiServer = settings?.apiServer || "";
|
||||
this.activeDid = (settings?.activeDid as string) || "";
|
||||
this.apiServer = (settings?.apiServer as string) || "";
|
||||
this.searchBox = settings?.searchBoxes?.[0] || null;
|
||||
|
||||
this.allContacts = await db.contacts.toArray();
|
||||
@@ -276,7 +276,7 @@ export default class DiscoverView extends Vue {
|
||||
|
||||
const results = await response.json();
|
||||
|
||||
const plans: ProjectData[] = results.data;
|
||||
const plans: PlanData[] = results.data;
|
||||
if (plans) {
|
||||
for (const plan of plans) {
|
||||
const { name, description, handleId, issuerDid, rowid } = plan;
|
||||
@@ -360,7 +360,7 @@ export default class DiscoverView extends Vue {
|
||||
|
||||
if (results.data) {
|
||||
if (beforeId) {
|
||||
const plans: ProjectData[] = results.data;
|
||||
const plans: PlanData[] = results.data;
|
||||
for (const plan of plans) {
|
||||
const { name, description, handleId, issuerDid, rowid } = plan;
|
||||
this.projects.push({
|
||||
@@ -428,13 +428,15 @@ export default class DiscoverView extends Vue {
|
||||
"py-3": true,
|
||||
"rounded-t-lg": true,
|
||||
"border-b-2": true,
|
||||
|
||||
active: this.isLocalActive,
|
||||
"text-blue-600": this.isLocalActive,
|
||||
"border-blue-600": this.isLocalActive,
|
||||
"text-black": this.isLocalActive,
|
||||
"border-black": this.isLocalActive,
|
||||
"font-semibold": this.isLocalActive,
|
||||
|
||||
"text-blue-600": !this.isLocalActive,
|
||||
"border-transparent": !this.isLocalActive,
|
||||
"hover:text-slate-600": !this.isLocalActive,
|
||||
"hover:border-slate-300": !this.isLocalActive,
|
||||
"hover:border-slate-400": !this.isLocalActive,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -444,13 +446,15 @@ export default class DiscoverView extends Vue {
|
||||
"py-3": true,
|
||||
"rounded-t-lg": true,
|
||||
"border-b-2": true,
|
||||
|
||||
active: this.isRemoteActive,
|
||||
"text-blue-600": this.isRemoteActive,
|
||||
"border-blue-600": this.isRemoteActive,
|
||||
"text-black": this.isRemoteActive,
|
||||
"border-black": this.isRemoteActive,
|
||||
"font-semibold": this.isRemoteActive,
|
||||
|
||||
"text-blue-600": !this.isRemoteActive,
|
||||
"border-transparent": !this.isRemoteActive,
|
||||
"hover:text-slate-600": !this.isRemoteActive,
|
||||
"hover:border-slate-300": !this.isRemoteActive,
|
||||
"hover:border-slate-400": !this.isRemoteActive,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@
|
||||
</span>
|
||||
<span v-if="offer.amount" class="whitespace-nowrap">
|
||||
<fa
|
||||
:icon="iconForUnitCode(offer.unit)"
|
||||
:icon="libsUtil.iconForUnitCode(offer.unit)"
|
||||
class="fa-fw text-slate-400"
|
||||
/>{{ offer.amount }}
|
||||
</span>
|
||||
@@ -274,7 +274,7 @@
|
||||
</span>
|
||||
<span v-if="give.amount" class="whitespace-nowrap">
|
||||
<fa
|
||||
:icon="iconForUnitCode(give.unit)"
|
||||
:icon="libsUtil.iconForUnitCode(give.unit)"
|
||||
class="fa-fw text-slate-400"
|
||||
/>{{ give.amount }}
|
||||
</span>
|
||||
@@ -755,25 +755,6 @@ export default class ProjectViewView extends Vue {
|
||||
(this.$refs.customGiveDialog as GiftedDialog).open(giver, offer.handleId);
|
||||
}
|
||||
|
||||
UNIT_CODES: Record<string, Record<string, string>> = {
|
||||
BTC: {
|
||||
name: "Bitcoin",
|
||||
faIcon: "bitcoin-sign",
|
||||
},
|
||||
HUR: {
|
||||
name: "hours",
|
||||
faIcon: "clock",
|
||||
},
|
||||
USD: {
|
||||
name: "US Dollars",
|
||||
faIcon: "dollar",
|
||||
},
|
||||
};
|
||||
|
||||
iconForUnitCode(unitCode: string) {
|
||||
return this.UNIT_CODES[unitCode]?.faIcon || "question";
|
||||
}
|
||||
|
||||
// return an HTTPS URL if it's not a global URL
|
||||
addScheme(url: string) {
|
||||
if (!libsUtil.isGlobalUri(url)) {
|
||||
|
||||
@@ -8,8 +8,44 @@
|
||||
Your Ideas
|
||||
</h1>
|
||||
|
||||
<!-- Quick Search -->
|
||||
<!-- Result Tabs -->
|
||||
<div class="text-center text-slate-500 border-b border-slate-300">
|
||||
<ul class="flex flex-wrap justify-center gap-4 -mb-px">
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
@click="
|
||||
offers = [];
|
||||
projects = [];
|
||||
showOffers = true;
|
||||
showProjects = false;
|
||||
loadOffers();
|
||||
"
|
||||
v-bind:class="computedOfferTabClassNames()"
|
||||
>
|
||||
Offers
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
@click="
|
||||
offers = [];
|
||||
projects = [];
|
||||
showOffers = false;
|
||||
showProjects = true;
|
||||
loadProjects();
|
||||
"
|
||||
v-bind:class="computedProjectTabClassNames()"
|
||||
>
|
||||
Projects
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Quick Search -->
|
||||
<!--
|
||||
<div id="QuickSearch" class="mb-4 flex">
|
||||
<input
|
||||
type="text"
|
||||
@@ -22,9 +58,11 @@
|
||||
<fa icon="magnifying-glass" class="fa-fw"></fa>
|
||||
</button>
|
||||
</div>
|
||||
-->
|
||||
|
||||
<!-- New Project -->
|
||||
<button
|
||||
v-if="showProjects"
|
||||
class="fixed right-6 bottom-24 text-center text-4xl leading-none bg-blue-600 text-white w-14 py-2.5 rounded-full"
|
||||
@click="onClickNewProject()"
|
||||
>
|
||||
@@ -39,8 +77,108 @@
|
||||
<fa icon="spinner" class="fa-spin-pulse"></fa>
|
||||
</div>
|
||||
|
||||
<!-- Results List -->
|
||||
<InfiniteScroll @reached-bottom="loadMoreData">
|
||||
<!-- Offer Results List -->
|
||||
<InfiniteScroll v-if="showOffers" @reached-bottom="loadMoreOfferData">
|
||||
<ul class="border-t border-slate-300">
|
||||
<li
|
||||
class="border-b border-slate-300"
|
||||
v-for="offer in offers"
|
||||
:key="offer.handleId"
|
||||
>
|
||||
<div class="block py-4 flex gap-4">
|
||||
<div v-if="offer.fulfillsPlanHandleId" class="flex-none w-12">
|
||||
<ProjectIcon
|
||||
:entityId="offer.fulfillsPlanHandleId"
|
||||
:iconSize="48"
|
||||
class="inline-block align-middle border border-slate-300 rounded-md"
|
||||
></ProjectIcon>
|
||||
</div>
|
||||
<div v-if="offer.recipientDid" class="flex-none w-12">
|
||||
<EntityIcon
|
||||
:entityId="offer.recipientDid"
|
||||
:iconSize="48"
|
||||
class="inline-block align-middle border border-slate-300 rounded-md"
|
||||
></EntityIcon>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
{{ offer.objectDescription }}
|
||||
</div>
|
||||
|
||||
<span class="text-sm">
|
||||
<span v-if="offer.amount">
|
||||
<fa
|
||||
:icon="libsUtil.iconForUnitCode(offer.unit)"
|
||||
class="fa-fw text-slate-400"
|
||||
/>
|
||||
|
||||
<span v-if="offer.amountGiven >= offer.amount">
|
||||
<fa icon="check-circle" class="fa-fw text-green-500" />
|
||||
All {{ offer.amount }} given
|
||||
</span>
|
||||
<span v-else>
|
||||
<fa
|
||||
icon="triangle-exclamation"
|
||||
class="fa-fw text-yellow-500"
|
||||
/>
|
||||
{{ offer.amountGiven ? "" : "All" }}
|
||||
{{ offer.amount - (offer.amountGiven || 0) }} remaining
|
||||
</span>
|
||||
|
||||
<span v-if="offer.amountGiven > 0">
|
||||
<span class="text-sm text-slate-400">
|
||||
({{ offer.amountGiven }} given,
|
||||
<span
|
||||
v-if="offer.amountGivenConfirmed >= offer.amountGiven"
|
||||
>
|
||||
<!-- no need for green icon; unnecessary if there's already a green, confusing if there's a yellow -->
|
||||
all
|
||||
</span>
|
||||
<span v-else>
|
||||
<!-- only show icon if there's not already a warning -->
|
||||
<fa
|
||||
v-if="offer.amountGiven >= offer.amount"
|
||||
icon="triangle-exclamation"
|
||||
class="fa-fw text-yellow-300"
|
||||
/>
|
||||
{{ offer.amountGivenConfirmed || 0 }}
|
||||
</span>
|
||||
of that is confirmed)
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span v-else>
|
||||
<!-- Non-amount offer -->
|
||||
<span v-if="offer.nonAmountGivenConfirmed">
|
||||
<fa icon="check-circle" class="fa-fw text-green-500" />
|
||||
{{ offer.nonAmountGivenConfirmed }}
|
||||
{{ offer.nonAmountGivenConfirmed == 1 ? "give" : "gives" }}
|
||||
are confirmed.
|
||||
</span>
|
||||
<span v-else>
|
||||
<fa
|
||||
icon="triangle-exclamation"
|
||||
class="fa-fw text-yellow-500"
|
||||
/>
|
||||
<span class="text-sm">Not confirmed by anyone</span>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<a @click="onClickLoadClaim(offer.jwtId)">
|
||||
<fa
|
||||
icon="circle-info"
|
||||
class="pl-2 text-blue-500 cursor-pointer"
|
||||
></fa>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</InfiniteScroll>
|
||||
<!-- Project Results List -->
|
||||
<InfiniteScroll v-if="showProjects" @reached-bottom="loadMoreProjectData">
|
||||
<ul class="border-t border-slate-300">
|
||||
<li
|
||||
class="border-b border-slate-300"
|
||||
@@ -74,15 +212,18 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from "vue-facing-decorator";
|
||||
|
||||
import { accountsDB, db } from "@/db/index";
|
||||
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
||||
import { accessToken } from "@/libs/crypto";
|
||||
import * as libsUtil from "@/libs/util";
|
||||
import { IIdentifier } from "@veramo/core";
|
||||
import InfiniteScroll from "@/components/InfiniteScroll.vue";
|
||||
import QuickNav from "@/components/QuickNav.vue";
|
||||
import ProjectIcon from "@/components/ProjectIcon.vue";
|
||||
import TopMessage from "@/components/TopMessage.vue";
|
||||
import { ProjectData } from "@/libs/endorserServer";
|
||||
import { OfferServerRecord, PlanData } from "@/libs/endorserServer";
|
||||
import EntityIcon from "@/components/EntityIcon.vue";
|
||||
|
||||
interface Notification {
|
||||
group: string;
|
||||
@@ -92,16 +233,21 @@ interface Notification {
|
||||
}
|
||||
|
||||
@Component({
|
||||
components: { InfiniteScroll, QuickNav, ProjectIcon, TopMessage },
|
||||
components: { EntityIcon, InfiniteScroll, QuickNav, ProjectIcon, TopMessage },
|
||||
})
|
||||
export default class ProjectsView extends Vue {
|
||||
$notify!: (notification: Notification, timeout?: number) => void;
|
||||
|
||||
apiServer = "";
|
||||
projects: ProjectData[] = [];
|
||||
current: IIdentifier;
|
||||
projects: PlanData[] = [];
|
||||
currentIid: IIdentifier;
|
||||
isLoading = false;
|
||||
numAccounts = 0;
|
||||
offers: OfferServerRecord[] = [];
|
||||
showOffers = true;
|
||||
showProjects = false;
|
||||
|
||||
libsUtil = libsUtil;
|
||||
|
||||
/**
|
||||
* 'created' hook runs when the Vue instance is first created
|
||||
@@ -110,8 +256,8 @@ export default class ProjectsView extends Vue {
|
||||
try {
|
||||
await db.open();
|
||||
const settings = await db.settings.get(MASTER_SETTINGS_KEY);
|
||||
const activeDid = settings?.activeDid || "";
|
||||
this.apiServer = settings?.apiServer || "";
|
||||
const activeDid: string = (settings?.activeDid as string) || "";
|
||||
this.apiServer = (settings?.apiServer as string) || "";
|
||||
|
||||
await accountsDB.open();
|
||||
this.numAccounts = await accountsDB.accounts.count();
|
||||
@@ -127,12 +273,11 @@ export default class ProjectsView extends Vue {
|
||||
-1,
|
||||
);
|
||||
} else {
|
||||
const identity = await this.getIdentity(activeDid);
|
||||
this.current = identity;
|
||||
this.loadProjects(identity);
|
||||
this.currentIid = await this.getIdentity(activeDid);
|
||||
await this.loadOffers();
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("Error initializing:", err);
|
||||
console.error("Error initializing:", err);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
@@ -150,7 +295,7 @@ export default class ProjectsView extends Vue {
|
||||
* @param url the url used to fetch the data
|
||||
* @param token Authorization token
|
||||
**/
|
||||
async dataLoader(url: string, token: string) {
|
||||
async projectDataLoader(url: string, token: string) {
|
||||
const headers: { [key: string]: string } = {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
@@ -160,13 +305,17 @@ export default class ProjectsView extends Vue {
|
||||
this.isLoading = true;
|
||||
const resp = await this.axios.get(url, { headers });
|
||||
if (resp.status === 200 || !resp.data.data) {
|
||||
const plans: ProjectData[] = resp.data.data;
|
||||
const plans: PlanData[] = resp.data.data;
|
||||
for (const plan of plans) {
|
||||
const { name, description, handleId, issuerDid, rowid } = plan;
|
||||
this.projects.push({ name, description, handleId, issuerDid, rowid });
|
||||
}
|
||||
} else {
|
||||
console.log("Bad server response & data:", resp.status, resp.data);
|
||||
console.error(
|
||||
"Bad server response & data for plans:",
|
||||
resp.status,
|
||||
resp.data,
|
||||
);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
@@ -179,7 +328,7 @@ export default class ProjectsView extends Vue {
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (error: any) {
|
||||
console.error("Got error loading projects:", error.message || error);
|
||||
console.error("Got error loading plans:", error.message || error);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
@@ -198,15 +347,44 @@ export default class ProjectsView extends Vue {
|
||||
* Data loader used by infinite scroller
|
||||
* @param payload is the flag from the InfiniteScroll indicating if it should load
|
||||
**/
|
||||
async loadMoreData(payload: boolean) {
|
||||
async loadMoreProjectData(payload: boolean) {
|
||||
if (this.projects.length > 0 && payload) {
|
||||
const latestProject = this.projects[this.projects.length - 1];
|
||||
const url = `${this.apiServer}/api/v2/report/plansByIssuer?beforeId=${latestProject.rowid}`;
|
||||
const token = await accessToken(this.current);
|
||||
await this.dataLoader(url, token);
|
||||
await this.loadProjects(
|
||||
this.currentIid,
|
||||
`beforeId=${latestProject.rowid}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load projects initially
|
||||
* @param identifier of the user
|
||||
* @param urlExtra additional url parameters in a string
|
||||
**/
|
||||
async loadProjects(identifier?: IIdentifier, urlExtra: string = "") {
|
||||
const identity = identifier || this.currentIid;
|
||||
const url = `${this.apiServer}/api/v2/report/plansByIssuer?${urlExtra}`;
|
||||
const token: string = await accessToken(identity);
|
||||
await this.projectDataLoader(url, token);
|
||||
}
|
||||
|
||||
public async getIdentity(activeDid: string): Promise<IIdentifier> {
|
||||
await accountsDB.open();
|
||||
const account = await accountsDB.accounts
|
||||
.where("did")
|
||||
.equals(activeDid)
|
||||
.first();
|
||||
const identity = JSON.parse((account?.identity as string) || "null");
|
||||
|
||||
if (!identity) {
|
||||
throw new Error(
|
||||
"Attempted to load project records with no identifier available.",
|
||||
);
|
||||
}
|
||||
return identity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle clicking on a project entry found in the list
|
||||
* @param id of the project
|
||||
@@ -219,32 +397,6 @@ export default class ProjectsView extends Vue {
|
||||
this.$router.push(route);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load projects initially
|
||||
* @param identity of the user
|
||||
**/
|
||||
async loadProjects(identity: IIdentifier) {
|
||||
const url = `${this.apiServer}/api/v2/report/plansByIssuer`;
|
||||
const token: string = await accessToken(identity);
|
||||
await this.dataLoader(url, token);
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
if (!identity) {
|
||||
throw new Error(
|
||||
"Attempted to load project records with no identifier available.",
|
||||
);
|
||||
}
|
||||
return identity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handling clicking on the new project button
|
||||
**/
|
||||
@@ -255,5 +407,120 @@ export default class ProjectsView extends Vue {
|
||||
};
|
||||
this.$router.push(route);
|
||||
}
|
||||
|
||||
onClickLoadClaim(jwtId: string) {
|
||||
const route = {
|
||||
path: "/claim/" + encodeURIComponent(jwtId),
|
||||
};
|
||||
this.$router.push(route);
|
||||
}
|
||||
|
||||
/**
|
||||
* Core offer data loader
|
||||
* @param url the url used to fetch the data
|
||||
* @param token Authorization token
|
||||
**/
|
||||
async offerDataLoader(url: string, token: string) {
|
||||
const headers: { [key: string]: string } = {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
};
|
||||
|
||||
try {
|
||||
this.isLoading = true;
|
||||
const resp = await this.axios.get(url, { headers });
|
||||
if (resp.status === 200 || !resp.data.data) {
|
||||
this.offers = this.offers.concat(resp.data.data);
|
||||
} else {
|
||||
console.error(
|
||||
"Bad server response & data for offers:",
|
||||
resp.status,
|
||||
resp.data,
|
||||
);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error",
|
||||
text: "Failed to get offers from the server. Try again later.",
|
||||
},
|
||||
-1,
|
||||
);
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (error: any) {
|
||||
console.error("Got error loading offers:", error.message || error);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error",
|
||||
text: "Got an error loading offers.",
|
||||
},
|
||||
-1,
|
||||
);
|
||||
} finally {
|
||||
this.isLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data loader used by infinite scroller
|
||||
* @param payload is the flag from the InfiniteScroll indicating if it should load
|
||||
**/
|
||||
async loadMoreOfferData(payload: boolean) {
|
||||
if (this.offers.length > 0 && payload) {
|
||||
const latestOffer = this.offers[this.offers.length - 1];
|
||||
await this.loadOffers(this.currentIid, `&beforeId=${latestOffer.jwtId}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load offers initially
|
||||
* @param identifier of the user
|
||||
* @param urlExtra additional url parameters in a string
|
||||
**/
|
||||
async loadOffers(identifier?: IIdentifier, urlExtra: string = "") {
|
||||
const identity = identifier || this.currentIid;
|
||||
const url = `${this.apiServer}/api/v2/report/offers?offeredByDid=${identity.did}${urlExtra}`;
|
||||
const token: string = await accessToken(identity);
|
||||
await this.offerDataLoader(url, token);
|
||||
}
|
||||
|
||||
public computedOfferTabClassNames() {
|
||||
return {
|
||||
"inline-block": true,
|
||||
"py-3": true,
|
||||
"rounded-t-lg": true,
|
||||
"border-b-2": true,
|
||||
|
||||
active: this.showOffers,
|
||||
"text-black": this.showOffers,
|
||||
"border-black": this.showOffers,
|
||||
"font-semibold": this.showOffers,
|
||||
|
||||
"text-blue-600": !this.showOffers,
|
||||
"border-transparent": !this.showOffers,
|
||||
"hover:border-slate-400": !this.showOffers,
|
||||
};
|
||||
}
|
||||
|
||||
public computedProjectTabClassNames() {
|
||||
return {
|
||||
"inline-block": true,
|
||||
"py-3": true,
|
||||
"rounded-t-lg": true,
|
||||
"border-b-2": true,
|
||||
|
||||
active: this.showProjects,
|
||||
"text-black": this.showProjects,
|
||||
"border-black": this.showProjects,
|
||||
"font-semibold": this.showProjects,
|
||||
|
||||
"text-blue-600": !this.showProjects,
|
||||
"border-transparent": !this.showProjects,
|
||||
"hover:border-slate-400": !this.showProjects,
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user