allow access to a different project by link #79

Merged
anomalist merged 2 commits from project-ids into master 1 year ago
  1. 1
      project.task.yaml
  2. 2
      src/router/index.ts
  3. 138
      src/views/ProjectViewView.vue
  4. 6
      web-push.md

1
project.task.yaml

@ -49,6 +49,7 @@ tasks:
- .5 make a VC details page - .5 make a VC details page
- .1 Add units or different icon to the coins (to distinguish $, BTC, hours, etc) - .1 Add units or different icon to the coins (to distinguish $, BTC, hours, etc)
- .5 include the hash of the latest commit on help page next to version - .5 include the hash of the latest commit on help page next to version
- .5 remove references to localStorage for projectId (now that it's pulling from the path)
- contacts v+ : - contacts v+ :
- 01 Import all the non-sensitive data (ie. contacts & settings). - 01 Import all the non-sensitive data (ie. contacts & settings).

2
src/router/index.ts

@ -148,7 +148,7 @@ const routes: Array<RouteRecordRaw> = [
), ),
}, },
{ {
path: "/project", path: "/project/:id?",
name: "project", name: "project",
component: () => component: () =>
import(/* webpackChunkName: "project" */ "../views/ProjectViewView.vue"), import(/* webpackChunkName: "project" */ "../views/ProjectViewView.vue"),

138
src/views/ProjectViewView.vue

@ -137,10 +137,12 @@
<div class="grid items-start grid-cols-1 sm:grid-cols-2 gap-4"> <div class="grid items-start grid-cols-1 sm:grid-cols-2 gap-4">
<div class="bg-slate-100 px-4 py-3 rounded-md"> <div class="bg-slate-100 px-4 py-3 rounded-md">
<h3 class="text-sm uppercase font-semibold mb-3"> <h3 class="text-sm uppercase font-semibold mb-3">
Given to this Project Given To This Project
</h3> </h3>
<ul class="text-sm border-t border-slate-300"> <div v-if="givesToThis.length === 0">(None yet. Record one above.)</div>
<ul v-else class="text-sm border-t border-slate-300">
<li <li
v-for="give in givesToThis" v-for="give in givesToThis"
:key="give.id" :key="give.id"
@ -164,31 +166,33 @@
</ul> </ul>
</div> </div>
<div class="bg-slate-100 px-4 py-3 rounded-md"> <div v-if="fulfilledByThis" class="bg-slate-100 px-4 py-3 rounded-md">
<h3 class="text-sm uppercase font-semibold mb-3"> <h3 class="text-sm uppercase font-semibold mb-3">
&hellip;and from this Project Contributions By This Project
</h3> </h3>
<button
<ul class="text-sm border-t border-slate-300"> @click="onClickLoadProject(fulfilledByThis.handleId)"
<li class="text-blue-500"
v-for="give in givesByThis"
:key="give.id"
class="py-1.5 border-b border-slate-300"
> >
<div class="flex justify-between gap-4"> {{ fulfilledByThis.name }}
<span </button>
><fa icon="user" class="fa-fw text-slate-400"></fa>
{{ didInfo(give.agentDid, activeDid, allMyDids, allContacts) }}
</span>
<span v-if="give.amount"
><fa icon="coins" class="fa-fw text-slate-400"></fa>
{{ give.amount }}
</span>
</div>
<div v-if="give.description" class="text-slate-500">
<fa icon="comment" class="fa-fw text-slate-400"></fa>
{{ give.description }}
</div> </div>
<div
v-if="fulfillersToThis.length > 0"
class="bg-slate-100 px-4 py-3 rounded-md"
>
<h3 class="text-sm uppercase font-semibold mb-3">
Contributions To This Project
</h3>
<ul>
<li v-for="plan in fulfillersToThis" :key="plan.handleId">
<button
@click="onClickLoadProject(plan.handleId)"
class="text-blue-500"
>
{{ plan.name }}
</button>
</li> </li>
</ul> </ul>
</div> </div>
@ -218,6 +222,7 @@ import {
didInfo, didInfo,
GiverInputInfo, GiverInputInfo,
GiveServerRecord, GiveServerRecord,
PlanServerRecord,
} from "@/libs/endorserServer"; } from "@/libs/endorserServer";
import QuickNav from "@/components/QuickNav.vue"; import QuickNav from "@/components/QuickNav.vue";
import EntityIcon from "@/components/EntityIcon.vue"; import EntityIcon from "@/components/EntityIcon.vue";
@ -242,8 +247,9 @@ export default class ProjectViewView extends Vue {
apiServer = ""; apiServer = "";
description = ""; description = "";
expanded = false; expanded = false;
fulfilledByThis: PlanServerRecord | null = null;
fulfillersToThis: Array<PlanServerRecord> = [];
givesToThis: Array<GiveServerRecord> = []; givesToThis: Array<GiveServerRecord> = [];
givesByThis: Array<GiveServerRecord> = [];
latitude = 0; latitude = 0;
longitude = 0; longitude = 0;
name = ""; name = "";
@ -266,7 +272,12 @@ export default class ProjectViewView extends Vue {
this.allMyDids = accountsArr.map((acc) => acc.did); this.allMyDids = accountsArr.map((acc) => acc.did);
const account = accountsArr.find((acc) => acc.did === this.activeDid); const account = accountsArr.find((acc) => acc.did === this.activeDid);
const identity = JSON.parse(account?.identity || "null"); const identity = JSON.parse(account?.identity || "null");
this.LoadProject(identity);
const pathParam = window.location.pathname.substring("/project/".length);
if (pathParam) {
this.projectId = decodeURIComponent(pathParam);
}
this.LoadProject(this.projectId, identity);
} }
public async getIdentity(activeDid: string) { public async getIdentity(activeDid: string) {
@ -320,11 +331,11 @@ export default class ProjectViewView extends Vue {
this.expanded = false; this.expanded = false;
} }
async LoadProject(identity: IIdentifier) { async LoadProject(projectId: string, identity: IIdentifier) {
this.projectId = projectId;
const url = const url =
this.apiServer + this.apiServer + "/api/claim/byHandle/" + encodeURIComponent(projectId);
"/api/claim/byHandle/" +
encodeURIComponent(this.projectId);
const headers: RawAxiosRequestHeaders = { const headers: RawAxiosRequestHeaders = {
"Content-Type": "application/json", "Content-Type": "application/json",
}; };
@ -389,7 +400,7 @@ export default class ProjectViewView extends Vue {
const givesInUrl = const givesInUrl =
this.apiServer + this.apiServer +
"/api/v2/report/givesForPlans?planIds=" + "/api/v2/report/givesForPlans?planIds=" +
encodeURIComponent(JSON.stringify([this.projectId])); encodeURIComponent(JSON.stringify([projectId]));
try { try {
const resp = await this.axios.get(givesInUrl, { headers }); const resp = await this.axios.get(givesInUrl, { headers });
if (resp.status === 200 && resp.data.data) { if (resp.status === 200 && resp.data.data) {
@ -422,21 +433,21 @@ export default class ProjectViewView extends Vue {
); );
} }
const givesOutUrl = const fulfilledByUrl =
this.apiServer + this.apiServer +
"/api/v2/report/givesProvidedBy?providerId=" + "/api/v2/report/planFulfilledByPlan?planHandleId=" +
encodeURIComponent(this.projectId); encodeURIComponent(projectId);
try { try {
const resp = await this.axios.get(givesOutUrl, { headers }); const resp = await this.axios.get(fulfilledByUrl, { headers });
if (resp.status === 200 && resp.data.data) { if (resp.status === 200) {
this.givesByThis = resp.data.data; this.fulfilledByThis = resp.data.data;
} else { } else {
this.$notify( this.$notify(
{ {
group: "alert", group: "alert",
type: "danger", type: "danger",
title: "Error", title: "Error",
text: "Failed to retrieve gives by this project.", text: "Failed to retrieve plans fulfilled by this project.",
}, },
-1, -1,
); );
@ -448,15 +459,64 @@ export default class ProjectViewView extends Vue {
group: "alert", group: "alert",
type: "danger", type: "danger",
title: "Error", title: "Error",
text: "Something went wrong retrieving gives by project.", text: "Something went wrong retrieving plans fulfilled by this project.",
}, },
-1, -1,
); );
console.error( console.error(
"Error retrieving gives by this project:", "Error retrieving plans fulfilled by this project:",
serverError.message, serverError.message,
); );
} }
const fulfillersToUrl =
this.apiServer +
"/api/v2/report/planFulfillersToPlan?planHandleId=" +
encodeURIComponent(projectId);
try {
const resp = await this.axios.get(fulfillersToUrl, { headers });
if (resp.status === 200) {
this.fulfillersToThis = resp.data.data;
} else {
this.$notify(
{
group: "alert",
type: "danger",
title: "Error",
text: "Failed to retrieve plan fulfillers to this project.",
},
-1,
);
}
} catch (error: unknown) {
const serverError = error as AxiosError;
this.$notify(
{
group: "alert",
type: "danger",
title: "Error",
text: "Something went wrong retrieving plan fulfillers to this project.",
},
-1,
);
console.error(
"Error retrieving plan fulfillers to this project:",
serverError.message,
);
}
}
/**
* Handle clicking on a project entry found in the list
* @param id of the project
**/
async onClickLoadProject(projectId: string) {
localStorage.setItem("projectId", projectId);
const route = {
path: "/project/" + encodeURIComponent(projectId),
};
this.$router.push(route);
this.LoadProject(projectId, await this.getIdentity(this.activeDid));
} }
getOpenStreetMapUrl() { getOpenStreetMapUrl() {

6
web-push.md

@ -29,8 +29,8 @@ from the SERVICE.
The SERVICE will provide context and obtain explicit permission before prompting The SERVICE will provide context and obtain explicit permission before prompting
for notification permission: for notification permission:
In order to provide this context and explict permission a two-step opt-in process In order to provide this context and explicit permission, a two-step opt-in process
where the user is first presented with a pre-permission dialog box that explains first presents the user with a pre-permission dialog box that explains
what the notifications are for and why they are useful. This may help reduce the what the notifications are for and why they are useful. This may help reduce the
possibility of users clicking "don't allow". possibility of users clicking "don't allow".
@ -91,7 +91,7 @@ The `sw.js` file contains the logic for what a service worker should do.
It executes in a separate thread of execution from the web page but provides a It executes in a separate thread of execution from the web page but provides a
means of communicating between itself and the web page via messages. means of communicating between itself and the web page via messages.
Note that there is a scope can specify what network requests it may Note that there is a scope that can specify what network requests it may
intercept. intercept.
The Vue project already has its own service worker but it is possible to The Vue project already has its own service worker but it is possible to

Loading…
Cancel
Save