forked from jsnbuchanan/crowd-funder-for-time-pwa
add screens for the shortcuts for the BVC group (doesn't submit yet)
This commit is contained in:
14
package-lock.json
generated
14
package-lock.json
generated
@@ -17,6 +17,7 @@
|
|||||||
"@pvermeer/dexie-encrypted-addon": "^3.0.0",
|
"@pvermeer/dexie-encrypted-addon": "^3.0.0",
|
||||||
"@tweenjs/tween.js": "^21.0.0",
|
"@tweenjs/tween.js": "^21.0.0",
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
|
"@types/luxon": "^3.4.2",
|
||||||
"@veramo/core": "^5.4.1",
|
"@veramo/core": "^5.4.1",
|
||||||
"@veramo/credential-w3c": "^5.4.1",
|
"@veramo/credential-w3c": "^5.4.1",
|
||||||
"@veramo/data-store": "^5.4.1",
|
"@veramo/data-store": "^5.4.1",
|
||||||
@@ -41,7 +42,7 @@
|
|||||||
"js-generate-password": "^0.1.9",
|
"js-generate-password": "^0.1.9",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"localstorage-slim": "^2.5.0",
|
"localstorage-slim": "^2.5.0",
|
||||||
"luxon": "^3.4.3",
|
"luxon": "^3.4.4",
|
||||||
"merkletreejs": "^0.3.11",
|
"merkletreejs": "^0.3.11",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"notiwind": "^2.0.2",
|
"notiwind": "^2.0.2",
|
||||||
@@ -9170,6 +9171,11 @@
|
|||||||
"@types/geojson": "*"
|
"@types/geojson": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/luxon": {
|
||||||
|
"version": "3.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.4.2.tgz",
|
||||||
|
"integrity": "sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA=="
|
||||||
|
},
|
||||||
"node_modules/@types/mime": {
|
"node_modules/@types/mime": {
|
||||||
"version": "1.3.3",
|
"version": "1.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.3.tgz",
|
||||||
@@ -20251,9 +20257,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/luxon": {
|
"node_modules/luxon": {
|
||||||
"version": "3.4.3",
|
"version": "3.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.4.tgz",
|
||||||
"integrity": "sha512-tFWBiv3h7z+T/tDaoxA8rqTxy1CHV6gHS//QdaH4pulbq/JuBSGgQspQQqcgnwdAx6pNI7cmvz5Sv/addzHmUg==",
|
"integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
"@pvermeer/dexie-encrypted-addon": "^3.0.0",
|
"@pvermeer/dexie-encrypted-addon": "^3.0.0",
|
||||||
"@tweenjs/tween.js": "^21.0.0",
|
"@tweenjs/tween.js": "^21.0.0",
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
|
"@types/luxon": "^3.4.2",
|
||||||
"@veramo/core": "^5.4.1",
|
"@veramo/core": "^5.4.1",
|
||||||
"@veramo/credential-w3c": "^5.4.1",
|
"@veramo/credential-w3c": "^5.4.1",
|
||||||
"@veramo/data-store": "^5.4.1",
|
"@veramo/data-store": "^5.4.1",
|
||||||
@@ -41,7 +42,7 @@
|
|||||||
"js-generate-password": "^0.1.9",
|
"js-generate-password": "^0.1.9",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"localstorage-slim": "^2.5.0",
|
"localstorage-slim": "^2.5.0",
|
||||||
"luxon": "^3.4.3",
|
"luxon": "^3.4.4",
|
||||||
"merkletreejs": "^0.3.11",
|
"merkletreejs": "^0.3.11",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"notiwind": "^2.0.2",
|
"notiwind": "^2.0.2",
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export type Settings = {
|
|||||||
}>;
|
}>;
|
||||||
|
|
||||||
showContactGivesInline?: boolean; // Display contact inline or not
|
showContactGivesInline?: boolean; // Display contact inline or not
|
||||||
|
showShortcutBvc?: boolean; // Show shortcut for BVC actions
|
||||||
vapid?: string; // VAPID (Voluntary Application Server Identification) field for web push
|
vapid?: string; // VAPID (Voluntary Application Server Identification) field for web push
|
||||||
warnIfProdServer?: boolean; // Warn if using a production server
|
warnIfProdServer?: boolean; // Warn if using a production server
|
||||||
warnIfTestServer?: boolean; // Warn if using a testing server
|
warnIfTestServer?: boolean; // Warn if using a testing server
|
||||||
|
|||||||
@@ -50,9 +50,9 @@ export interface GenericVerifiableCredential {
|
|||||||
|
|
||||||
export interface GenericServerRecord extends GenericVerifiableCredential {
|
export interface GenericServerRecord extends GenericVerifiableCredential {
|
||||||
handleId?: string;
|
handleId?: string;
|
||||||
id?: string;
|
id: string;
|
||||||
issuedAt?: string;
|
issuedAt: string;
|
||||||
issuer?: string;
|
issuer: string;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
claim: Record<any, any>;
|
claim: Record<any, any>;
|
||||||
claimType?: string;
|
claimType?: string;
|
||||||
@@ -61,6 +61,9 @@ export const BLANK_GENERIC_SERVER_RECORD: GenericServerRecord = {
|
|||||||
"@context": SCHEMA_ORG_CONTEXT,
|
"@context": SCHEMA_ORG_CONTEXT,
|
||||||
"@type": "",
|
"@type": "",
|
||||||
claim: {},
|
claim: {},
|
||||||
|
id: "",
|
||||||
|
issuedAt: "",
|
||||||
|
issuer: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface GiveServerRecord {
|
export interface GiveServerRecord {
|
||||||
@@ -579,6 +582,182 @@ export async function createAndSubmitClaim(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
export const isAccept = (claim: Record<any, any>) => {
|
||||||
|
return (
|
||||||
|
claim &&
|
||||||
|
claim["@context"] === SCHEMA_ORG_CONTEXT &&
|
||||||
|
claim["@type"] === "AcceptAction"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
export const isOffer = (claim: Record<any, any>) => {
|
||||||
|
return (
|
||||||
|
claim &&
|
||||||
|
claim["@context"] === SCHEMA_ORG_CONTEXT &&
|
||||||
|
claim["@type"] === "Offer"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export function currencyShortWordForCode(unitCode: string, single: boolean) {
|
||||||
|
return unitCode === "HUR" ? (single ? "hour" : "hours") : unitCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function displayAmount(code: string, amt: number) {
|
||||||
|
return "" + amt + " " + currencyShortWordForCode(code, amt === 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert a space before any capital letters except the initial letter
|
||||||
|
// (and capitalize initial letter, just in case)
|
||||||
|
export const capitalizeAndInsertSpacesBeforeCaps = (text: string) => {
|
||||||
|
return !text
|
||||||
|
? ""
|
||||||
|
: text[0].toUpperCase() + text.substr(1).replace(/([A-Z])/g, " $1");
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
return readable summary of claim, or something generic
|
||||||
|
|
||||||
|
similar code is also contained in endorser-mobile
|
||||||
|
**/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const claimSummary = (claim: Record<any, any>) => {
|
||||||
|
if (!claim) {
|
||||||
|
// to differentiate from "something" above
|
||||||
|
return "something";
|
||||||
|
}
|
||||||
|
if (claim.claim) {
|
||||||
|
// probably a Verified Credential
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
claim = claim.claim as Record<any, any>;
|
||||||
|
}
|
||||||
|
if (Array.isArray(claim)) {
|
||||||
|
if (claim.length === 1) {
|
||||||
|
claim = claim[0];
|
||||||
|
} else {
|
||||||
|
return "multiple claims";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const type = claim["@type"];
|
||||||
|
if (!type) {
|
||||||
|
return "a claim";
|
||||||
|
} else {
|
||||||
|
let typeExpl = capitalizeAndInsertSpacesBeforeCaps(type);
|
||||||
|
if (typeExpl === "Person") {
|
||||||
|
typeExpl += " claim";
|
||||||
|
}
|
||||||
|
return "a " + typeExpl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
return readable description of claim if possible, as a past-tense action
|
||||||
|
|
||||||
|
identifiers is a list of objects with a 'did' field, each representing the user
|
||||||
|
contacts is a list of objects with a 'did' field for others and a 'name' field for their name
|
||||||
|
|
||||||
|
similar code is also contained in endorser-mobile
|
||||||
|
**/
|
||||||
|
export const claimSpecialDescription = (
|
||||||
|
record: GenericServerRecord,
|
||||||
|
activeDid: string,
|
||||||
|
identifiers: Array<string>,
|
||||||
|
contacts: Array<Contact>,
|
||||||
|
) => {
|
||||||
|
let claim = record.claim;
|
||||||
|
if (claim.claim) {
|
||||||
|
// it's probably a Verified Credential
|
||||||
|
claim = claim.claim;
|
||||||
|
}
|
||||||
|
|
||||||
|
const issuer = didInfo(record.issuer, activeDid, identifiers, contacts);
|
||||||
|
const type = claim["@type"] || "UnknownType";
|
||||||
|
|
||||||
|
if (type === "AgreeAction") {
|
||||||
|
return issuer + " agreed with " + claimSummary(claim.object);
|
||||||
|
} else if (isAccept(claim)) {
|
||||||
|
return issuer + " accepted " + claimSummary(claim.object);
|
||||||
|
} else if (type === "GiveAction") {
|
||||||
|
// agent.did is for legacy data, before March 2023
|
||||||
|
const giver = claim.agent?.identifier || claim.agent?.did;
|
||||||
|
const giverInfo = didInfo(giver, activeDid, identifiers, contacts);
|
||||||
|
let gaveAmount = claim.object?.amountOfThisGood
|
||||||
|
? displayAmount(claim.object.unitCode, claim.object.amountOfThisGood)
|
||||||
|
: "";
|
||||||
|
if (claim.description) {
|
||||||
|
if (gaveAmount) {
|
||||||
|
gaveAmount = gaveAmount + ", and also: ";
|
||||||
|
}
|
||||||
|
gaveAmount = gaveAmount + claim.description;
|
||||||
|
}
|
||||||
|
if (!gaveAmount) {
|
||||||
|
gaveAmount = "something not described";
|
||||||
|
}
|
||||||
|
// recipient.did is for legacy data, before March 2023
|
||||||
|
const gaveRecipientId = claim.recipient?.identifier || claim.recipient?.did;
|
||||||
|
const gaveRecipientInfo = gaveRecipientId
|
||||||
|
? " to " + didInfo(gaveRecipientId, activeDid, identifiers, contacts)
|
||||||
|
: "";
|
||||||
|
return giverInfo + " gave" + gaveRecipientInfo + ": " + gaveAmount;
|
||||||
|
} else if (type === "JoinAction") {
|
||||||
|
// agent.did is for legacy data, before March 2023
|
||||||
|
const agent = claim.agent?.identifier || claim.agent?.did;
|
||||||
|
const contactInfo = didInfo(agent, activeDid, identifiers, contacts);
|
||||||
|
|
||||||
|
let eventOrganizer =
|
||||||
|
claim.event && claim.event.organizer && claim.event.organizer.name;
|
||||||
|
eventOrganizer = eventOrganizer || "";
|
||||||
|
let eventName = claim.event && claim.event.name;
|
||||||
|
eventName = eventName ? " " + eventName : "";
|
||||||
|
let fullEvent = eventOrganizer + eventName;
|
||||||
|
fullEvent = fullEvent ? " attended the " + fullEvent : "";
|
||||||
|
|
||||||
|
let eventDate = claim.event && claim.event.startTime;
|
||||||
|
eventDate = eventDate ? " at " + eventDate : "";
|
||||||
|
return contactInfo + fullEvent + eventDate;
|
||||||
|
} else if (isOffer(claim)) {
|
||||||
|
const offerer = claim.offeredBy?.identifier;
|
||||||
|
const contactInfo = didInfo(offerer, activeDid, identifiers, contacts);
|
||||||
|
let offering = "";
|
||||||
|
if (claim.includesObject) {
|
||||||
|
offering +=
|
||||||
|
" " +
|
||||||
|
displayAmount(
|
||||||
|
claim.includesObject.unitCode,
|
||||||
|
claim.includesObject.amountOfThisGood,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (claim.itemOffered?.description) {
|
||||||
|
offering += ", saying: " + claim.itemOffered?.description;
|
||||||
|
}
|
||||||
|
// recipient.did is for legacy data, before March 2023
|
||||||
|
const offerRecipientId =
|
||||||
|
claim.recipient?.identifier || claim.recipient?.did;
|
||||||
|
const offerRecipientInfo = offerRecipientId
|
||||||
|
? " to " + didInfo(offerRecipientId, activeDid, identifiers, contacts)
|
||||||
|
: "";
|
||||||
|
return contactInfo + " offered" + offering + offerRecipientInfo;
|
||||||
|
} else if (type === "PlanAction") {
|
||||||
|
const claimer = claim.agent?.identifier || record.issuer;
|
||||||
|
const claimerInfo = didInfo(claimer, activeDid, identifiers, contacts);
|
||||||
|
return claimerInfo + " announced a project: " + claim.name;
|
||||||
|
} else if (type === "Tenure") {
|
||||||
|
// party.did is for legacy data, before March 2023
|
||||||
|
const claimer = claim.party?.identifier || claim.party?.did;
|
||||||
|
const contactInfo = didInfo(claimer, activeDid, identifiers, contacts);
|
||||||
|
const polygon = claim.spatialUnit?.geo?.polygon || "";
|
||||||
|
return (
|
||||||
|
contactInfo +
|
||||||
|
" possesses [" +
|
||||||
|
polygon.substring(0, polygon.indexOf(" ")) +
|
||||||
|
"...]"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return issuer + " declared " + claimSummary(claim as GenericServerRecord);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// from https://stackoverflow.com/a/175787/845494
|
// from https://stackoverflow.com/a/175787/845494
|
||||||
//
|
//
|
||||||
export function isNumeric(str: string): boolean {
|
export function isNumeric(str: string): boolean {
|
||||||
|
|||||||
@@ -165,6 +165,30 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
import(/* webpackChunkName: "projects" */ "../views/ProjectsView.vue"),
|
import(/* webpackChunkName: "projects" */ "../views/ProjectsView.vue"),
|
||||||
beforeEnter: enterOrStart,
|
beforeEnter: enterOrStart,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/quick-action-bvc",
|
||||||
|
name: "quick-action-bvc",
|
||||||
|
component: () =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "quick-action-bvc" */ "../views/QuickActionBvcView.vue"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/quick-action-bvc-begin",
|
||||||
|
name: "quick-action-bvc-begin",
|
||||||
|
component: () =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "quick-action-bvc-begin" */ "../views/QuickActionBvcBeginView.vue"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/quick-action-bvc-end",
|
||||||
|
name: "quick-action-bvc-end",
|
||||||
|
component: () =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "quick-action-bvc-end" */ "../views/QuickActionBvcEndView.vue"
|
||||||
|
),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/scan-contact",
|
path: "/scan-contact",
|
||||||
name: "scan-contact",
|
name: "scan-contact",
|
||||||
|
|||||||
@@ -299,7 +299,7 @@
|
|||||||
<label
|
<label
|
||||||
for="toggleShowAmounts"
|
for="toggleShowAmounts"
|
||||||
class="flex items-center justify-between cursor-pointer my-4"
|
class="flex items-center justify-between cursor-pointer my-4"
|
||||||
@click="handleChange"
|
@click="toggleShowContactAmounts"
|
||||||
>
|
>
|
||||||
<!-- label -->
|
<!-- label -->
|
||||||
<span class="text-slate-500 text-sm font-bold">Contacts Display</span>
|
<span class="text-slate-500 text-sm font-bold">Contacts Display</span>
|
||||||
@@ -439,6 +439,28 @@
|
|||||||
{{ DEFAULT_PUSH_SERVER }}
|
{{ DEFAULT_PUSH_SERVER }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<label
|
||||||
|
for="toggleShowShortcutBvc"
|
||||||
|
class="flex items-center justify-between cursor-pointer my-4"
|
||||||
|
@click="toggleShowShortcutBvc"
|
||||||
|
>
|
||||||
|
<!-- label -->
|
||||||
|
<span class="text-slate-500 text-sm font-bold"
|
||||||
|
>Show Shortcut on Home Page</span
|
||||||
|
>
|
||||||
|
<!-- toggle -->
|
||||||
|
<div class="relative ml-2">
|
||||||
|
<!-- input -->
|
||||||
|
<input type="checkbox" v-model="showShortcutBvc" class="sr-only" />
|
||||||
|
<!-- line -->
|
||||||
|
<div class="block bg-slate-500 w-14 h-8 rounded-full"></div>
|
||||||
|
<!-- dot -->
|
||||||
|
<div
|
||||||
|
class="dot absolute left-1 top-1 bg-slate-400 w-6 h-6 rounded-full transition"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<h2 class="text-slate-500 text-sm font-bold">
|
<h2 class="text-slate-500 text-sm font-bold">
|
||||||
Contacts & Settings Database
|
Contacts & Settings Database
|
||||||
@@ -537,6 +559,7 @@ export default class AccountViewView extends Vue {
|
|||||||
showB64Copy = false;
|
showB64Copy = false;
|
||||||
showPubCopy = false;
|
showPubCopy = false;
|
||||||
showAdvanced = false;
|
showAdvanced = false;
|
||||||
|
showShortcutBvc = false;
|
||||||
subscription: PushSubscription | null = null;
|
subscription: PushSubscription | null = null;
|
||||||
warnIfProdServer = false;
|
warnIfProdServer = false;
|
||||||
warnIfTestServer = false;
|
warnIfTestServer = false;
|
||||||
@@ -596,6 +619,7 @@ export default class AccountViewView extends Vue {
|
|||||||
(settings?.lastName ? ` ${settings.lastName}` : ""); // pre v 0.1.3
|
(settings?.lastName ? ` ${settings.lastName}` : ""); // pre v 0.1.3
|
||||||
this.isRegistered = !!settings?.isRegistered;
|
this.isRegistered = !!settings?.isRegistered;
|
||||||
this.showContactGives = !!settings?.showContactGivesInline;
|
this.showContactGives = !!settings?.showContactGivesInline;
|
||||||
|
this.showShortcutBvc = !!settings?.showShortcutBvc;
|
||||||
this.warnIfProdServer = !!settings?.warnIfProdServer;
|
this.warnIfProdServer = !!settings?.warnIfProdServer;
|
||||||
this.warnIfTestServer = !!settings?.warnIfTestServer;
|
this.warnIfTestServer = !!settings?.warnIfTestServer;
|
||||||
this.webPushServer = (settings?.webPushServer as string) || "";
|
this.webPushServer = (settings?.webPushServer as string) || "";
|
||||||
@@ -653,7 +677,7 @@ export default class AccountViewView extends Vue {
|
|||||||
.then(() => setTimeout(fn, 2000));
|
.then(() => setTimeout(fn, 2000));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChange() {
|
toggleShowContactAmounts() {
|
||||||
this.showContactGives = !this.showContactGives;
|
this.showContactGives = !this.showContactGives;
|
||||||
this.updateShowContactAmounts();
|
this.updateShowContactAmounts();
|
||||||
}
|
}
|
||||||
@@ -668,6 +692,11 @@ export default class AccountViewView extends Vue {
|
|||||||
this.updateWarnIfTestServer(this.warnIfTestServer);
|
this.updateWarnIfTestServer(this.warnIfTestServer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleShowShortcutBvc() {
|
||||||
|
this.showShortcutBvc = !this.showShortcutBvc;
|
||||||
|
this.updateShowShortcutBvc(this.showShortcutBvc);
|
||||||
|
}
|
||||||
|
|
||||||
readableTime(timeStr: string) {
|
readableTime(timeStr: string) {
|
||||||
return timeStr.substring(0, timeStr.indexOf("T"));
|
return timeStr.substring(0, timeStr.indexOf("T"));
|
||||||
}
|
}
|
||||||
@@ -763,7 +792,7 @@ export default class AccountViewView extends Vue {
|
|||||||
-1,
|
-1,
|
||||||
);
|
);
|
||||||
console.error(
|
console.error(
|
||||||
"Telling user to try again after contact setting update because:",
|
"Telling user to try again after contact-amounts setting update because:",
|
||||||
err,
|
err,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -786,7 +815,7 @@ export default class AccountViewView extends Vue {
|
|||||||
-1,
|
-1,
|
||||||
);
|
);
|
||||||
console.error(
|
console.error(
|
||||||
"Telling user to try again after setting update because:",
|
"Telling user to try again after prod-server-warning setting update because:",
|
||||||
err,
|
err,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -809,7 +838,30 @@ export default class AccountViewView extends Vue {
|
|||||||
-1,
|
-1,
|
||||||
);
|
);
|
||||||
console.error(
|
console.error(
|
||||||
"Telling user to try again after setting update because:",
|
"Telling user to try again after test-server-warning setting update because:",
|
||||||
|
err,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async updateShowShortcutBvc(newSetting: boolean) {
|
||||||
|
try {
|
||||||
|
await db.open();
|
||||||
|
db.settings.update(MASTER_SETTINGS_KEY, {
|
||||||
|
showShortcutBvc: newSetting,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
this.$notify(
|
||||||
|
{
|
||||||
|
group: "alert",
|
||||||
|
type: "danger",
|
||||||
|
title: "Error Updating BVC Shortcut Setting",
|
||||||
|
text: "The setting may not have saved. Try again, maybe after restarting the app.",
|
||||||
|
},
|
||||||
|
-1,
|
||||||
|
);
|
||||||
|
console.error(
|
||||||
|
"Telling user to try again after BVC-shortcut setting update because:",
|
||||||
err,
|
err,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="showShortcutBvc" class="mb-4">
|
||||||
|
<router-link
|
||||||
|
:to="{ name: 'quick-action-bvc' }"
|
||||||
|
class="block text-center text-md font-bold uppercase bg-blue-500 text-white mt-2 px-2 py-3 rounded-md"
|
||||||
|
>
|
||||||
|
Bountiful Voluntaryist Community Actions</router-link
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- show the actions for recognizing a give -->
|
<!-- show the actions for recognizing a give -->
|
||||||
<div class="mb-8">
|
<div class="mb-8">
|
||||||
<div v-if="isCreatingIdentifier">
|
<div v-if="isCreatingIdentifier">
|
||||||
@@ -287,6 +296,7 @@ export default class HomeView extends Vue {
|
|||||||
isCreatingIdentifier = false;
|
isCreatingIdentifier = false;
|
||||||
isFeedLoading = true;
|
isFeedLoading = true;
|
||||||
isRegistered = false;
|
isRegistered = false;
|
||||||
|
showShortcutBvc = false;
|
||||||
userAgentInfo = new UAParser(); // see https://docs.uaparser.js.org/v2/api/ua-parser-js/get-os.html
|
userAgentInfo = new UAParser(); // see https://docs.uaparser.js.org/v2/api/ua-parser-js/get-os.html
|
||||||
|
|
||||||
public async getIdentity(activeDid: string) {
|
public async getIdentity(activeDid: string) {
|
||||||
@@ -321,6 +331,7 @@ export default class HomeView extends Vue {
|
|||||||
this.allContacts = await db.contacts.toArray();
|
this.allContacts = await db.contacts.toArray();
|
||||||
this.feedLastViewedClaimId = settings?.lastViewedClaimId;
|
this.feedLastViewedClaimId = settings?.lastViewedClaimId;
|
||||||
this.isRegistered = !!settings?.isRegistered;
|
this.isRegistered = !!settings?.isRegistered;
|
||||||
|
this.showShortcutBvc = !!settings?.showShortcutBvc;
|
||||||
|
|
||||||
if (this.allMyDids.length === 0) {
|
if (this.allMyDids.length === 0) {
|
||||||
this.isCreatingIdentifier = true;
|
this.isCreatingIdentifier = true;
|
||||||
@@ -473,7 +484,7 @@ export default class HomeView extends Vue {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.status !== 200) {
|
if (!response.ok) {
|
||||||
throw await response.text();
|
throw await response.text();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
99
src/views/QuickActionBvcBeginView.vue
Normal file
99
src/views/QuickActionBvcBeginView.vue
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
<template>
|
||||||
|
<QuickNav />
|
||||||
|
<TopMessage />
|
||||||
|
|
||||||
|
<!-- CONTENT -->
|
||||||
|
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
|
||||||
|
<!-- Back -->
|
||||||
|
<div class="text-lg text-center font-light relative px-7">
|
||||||
|
<h1
|
||||||
|
class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
|
||||||
|
@click="$router.back()"
|
||||||
|
>
|
||||||
|
<fa icon="chevron-left" class="fa-fw"></fa>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Heading -->
|
||||||
|
<h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-4">
|
||||||
|
Beginning of BVC Saturday Meeting
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h2 class="text-2xl m-4">You're Here</h2>
|
||||||
|
<div class="m-4 flex">
|
||||||
|
<input type="checkbox" v-model="gaveTime" class="h-6 w-6" />
|
||||||
|
<span class="pb-2 pl-2 pr-2">Attended</span>
|
||||||
|
<span v-if="gaveTime">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="How much time"
|
||||||
|
v-model="hours"
|
||||||
|
size="1"
|
||||||
|
class="border border-slate-400 h-6 px-2"
|
||||||
|
/>
|
||||||
|
hour(s)
|
||||||
|
</span>
|
||||||
|
<!-- This is to match input height to avoid shifting when hiding & showing. -->
|
||||||
|
<span v-else class="h-6" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="m-4" v-if="gaveTime && hours && hours != '0'">
|
||||||
|
<button
|
||||||
|
@click="record()"
|
||||||
|
class="block text-center text-md font-bold bg-blue-500 text-white px-2 py-3 rounded-md"
|
||||||
|
>
|
||||||
|
Sign & Send
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { DateTime } from "luxon";
|
||||||
|
import { Component, Vue } from "vue-facing-decorator";
|
||||||
|
|
||||||
|
import QuickNav from "@/components/QuickNav.vue";
|
||||||
|
import TopMessage from "@/components/TopMessage.vue";
|
||||||
|
import { NotificationIface } from "@/constants/app";
|
||||||
|
import { numberOrZero } from "@/libs/endorserServer";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
components: {
|
||||||
|
QuickNav,
|
||||||
|
TopMessage,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
export default class QuickActionBvcBeginView extends Vue {
|
||||||
|
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
||||||
|
|
||||||
|
gaveTime = false;
|
||||||
|
hours = "1";
|
||||||
|
todayOrPreviousStartDate = "";
|
||||||
|
|
||||||
|
async mounted() {
|
||||||
|
let currentOrPreviousSat = DateTime.now().setZone("America/Denver");
|
||||||
|
if (currentOrPreviousSat.weekday < 6) {
|
||||||
|
// it's not Saturday or Sunday,
|
||||||
|
// so move back one week before setting to the Saturday
|
||||||
|
currentOrPreviousSat = currentOrPreviousSat.minus({ week: 1 });
|
||||||
|
}
|
||||||
|
const eventStartDateObj = currentOrPreviousSat
|
||||||
|
.set({ weekday: 6 })
|
||||||
|
.set({ hour: 9 })
|
||||||
|
.startOf("hour");
|
||||||
|
|
||||||
|
// Hack, but full ISO pushes the length to 340 which crashes verifyJWT!
|
||||||
|
this.todayOrPreviousStartDate =
|
||||||
|
eventStartDateObj.toISO({
|
||||||
|
suppressMilliseconds: true,
|
||||||
|
}) || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
record() {
|
||||||
|
const hoursNum = numberOrZero(this.hours);
|
||||||
|
alert("Nope" + hoursNum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
228
src/views/QuickActionBvcEndView.vue
Normal file
228
src/views/QuickActionBvcEndView.vue
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
<template>
|
||||||
|
<QuickNav />
|
||||||
|
<TopMessage />
|
||||||
|
|
||||||
|
<!-- CONTENT -->
|
||||||
|
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
|
||||||
|
<!-- Back -->
|
||||||
|
<div class="text-lg text-center font-light relative px-7">
|
||||||
|
<h1
|
||||||
|
class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
|
||||||
|
@click="$router.back()"
|
||||||
|
>
|
||||||
|
<fa icon="chevron-left" class="fa-fw"></fa>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Heading -->
|
||||||
|
<h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-4">
|
||||||
|
End of BVC Saturday Meeting
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h2 class="text-2xl m-4">Confirm</h2>
|
||||||
|
<div v-if="claimsToConfirm.length === 0">
|
||||||
|
There are no claims today yet for you to confirm.
|
||||||
|
<span v-if="claimCountWithHidden">
|
||||||
|
(There are {{ claimCountWithHidden }} hidden claims.)
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<ul class="border-t border-slate-300">
|
||||||
|
<li
|
||||||
|
class="border-b border-slate-300 py-2"
|
||||||
|
v-for="record in claimsToConfirm"
|
||||||
|
:key="record.id"
|
||||||
|
>
|
||||||
|
<div class="grid grid-cols-12">
|
||||||
|
<span class="col-span-11 justify-self-start">
|
||||||
|
<span>
|
||||||
|
<input type="checkbox" class="mr-2 h-6 w-6" />
|
||||||
|
</span>
|
||||||
|
{{
|
||||||
|
claimSpecialDescription(
|
||||||
|
record,
|
||||||
|
activeDid,
|
||||||
|
allMyDids,
|
||||||
|
allContacts,
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
<a @click="onClickLoadClaim(record.id)">
|
||||||
|
<fa
|
||||||
|
icon="circle-info"
|
||||||
|
class="pl-2 text-blue-500 cursor-pointer"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h2 class="text-2xl m-4">Anything else?</h2>
|
||||||
|
<div class="m-4 flex">
|
||||||
|
<input type="checkbox" v-model="someoneGave" class="h-6 w-6" />
|
||||||
|
<span class="pb-2 pl-2 pr-2">Someone gave</span>
|
||||||
|
<span v-if="someoneGave">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
v-model="description"
|
||||||
|
size="20"
|
||||||
|
class="border border-slate-400 h-6 px-2"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<!-- This is to match input height to avoid shifting when hiding & showing. -->
|
||||||
|
<span v-else class="h-6">...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="m-4" v-if="someoneGave && description">
|
||||||
|
<button
|
||||||
|
@click="record()"
|
||||||
|
class="block text-center text-md font-bold bg-blue-500 text-white px-2 py-3 rounded-md"
|
||||||
|
>
|
||||||
|
Sign & Send
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { DateTime } from "luxon";
|
||||||
|
import * as R from "ramda";
|
||||||
|
import { IIdentifier } from "@veramo/core";
|
||||||
|
import { Component, Vue } from "vue-facing-decorator";
|
||||||
|
|
||||||
|
import QuickNav from "@/components/QuickNav.vue";
|
||||||
|
import TopMessage from "@/components/TopMessage.vue";
|
||||||
|
import { NotificationIface } from "@/constants/app";
|
||||||
|
import { accountsDB, db } from "@/db/index";
|
||||||
|
import { Account } from "@/db/tables/accounts";
|
||||||
|
import { Contact } from "@/db/tables/contacts";
|
||||||
|
import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings";
|
||||||
|
import { accessToken } from "@/libs/crypto";
|
||||||
|
import {
|
||||||
|
claimSpecialDescription,
|
||||||
|
containsHiddenDid,
|
||||||
|
GenericServerRecord,
|
||||||
|
} from "@/libs/endorserServer";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
methods: { claimSpecialDescription },
|
||||||
|
components: {
|
||||||
|
QuickNav,
|
||||||
|
TopMessage,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
export default class QuickActionBvcBeginView extends Vue {
|
||||||
|
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
||||||
|
|
||||||
|
activeDid = "";
|
||||||
|
allContacts: Array<Contact> = [];
|
||||||
|
allMyDids: Array<string> = [];
|
||||||
|
apiServer = "";
|
||||||
|
claimCountWithHidden = 0;
|
||||||
|
claimsToConfirm: GenericServerRecord[] = [];
|
||||||
|
description = "";
|
||||||
|
someoneGave = false;
|
||||||
|
|
||||||
|
async created() {
|
||||||
|
await db.open();
|
||||||
|
const settings = (await db.settings.get(MASTER_SETTINGS_KEY)) as Settings;
|
||||||
|
this.apiServer = settings?.apiServer || "";
|
||||||
|
this.activeDid = settings?.activeDid || "";
|
||||||
|
this.allContacts = await db.contacts.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
async mounted() {
|
||||||
|
let currentOrPreviousSat = DateTime.now().setZone("America/Denver");
|
||||||
|
if (currentOrPreviousSat.weekday < 6) {
|
||||||
|
// it's not Saturday or Sunday,
|
||||||
|
// so move back one week before setting to the Saturday
|
||||||
|
currentOrPreviousSat = currentOrPreviousSat.minus({ week: 1 });
|
||||||
|
}
|
||||||
|
const eventStartDateObj = currentOrPreviousSat
|
||||||
|
.set({ weekday: 6 })
|
||||||
|
.set({ hour: 9 })
|
||||||
|
.startOf("hour");
|
||||||
|
|
||||||
|
// Hack, but full ISO pushes the length to 340 which crashes verifyJWT!
|
||||||
|
const todayOrPreviousStartDate =
|
||||||
|
eventStartDateObj.toISO({
|
||||||
|
suppressMilliseconds: true,
|
||||||
|
}) || "";
|
||||||
|
|
||||||
|
await accountsDB.open();
|
||||||
|
const allAccounts = await accountsDB.accounts.toArray();
|
||||||
|
this.allMyDids = allAccounts.map((acc) => acc.did);
|
||||||
|
const account: Account | undefined = await accountsDB.accounts
|
||||||
|
.where("did")
|
||||||
|
.equals(this.activeDid)
|
||||||
|
.first();
|
||||||
|
const identity: IIdentifier = JSON.parse(
|
||||||
|
(account?.identity as string) || "null",
|
||||||
|
);
|
||||||
|
const headers = {
|
||||||
|
Authorization: "Bearer " + (await accessToken(identity)),
|
||||||
|
};
|
||||||
|
console.log("todayOrPreviousStartDate", todayOrPreviousStartDate);
|
||||||
|
try {
|
||||||
|
console.log(
|
||||||
|
this.apiServer +
|
||||||
|
"/api/claim/?" +
|
||||||
|
"issuedAt_greaterThanOrEqualTo=" +
|
||||||
|
encodeURIComponent(todayOrPreviousStartDate) +
|
||||||
|
"&excludeConfirmations=true",
|
||||||
|
);
|
||||||
|
const response = await fetch(
|
||||||
|
this.apiServer +
|
||||||
|
"/api/claim/?" +
|
||||||
|
"issuedAt_greaterThanOrEqualTo=" +
|
||||||
|
encodeURIComponent(todayOrPreviousStartDate) +
|
||||||
|
"&excludeConfirmations=true",
|
||||||
|
{ headers },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
console.log("Bad response", response);
|
||||||
|
throw new Error("Bad response when retrieving claims.");
|
||||||
|
}
|
||||||
|
response.json().then((data) => {
|
||||||
|
const dataByOthers = R.reject(
|
||||||
|
(claim: GenericServerRecord) => claim.issuer === this.activeDid,
|
||||||
|
data,
|
||||||
|
);
|
||||||
|
const dataByOthersWithoutHidden = R.reject(
|
||||||
|
containsHiddenDid,
|
||||||
|
dataByOthers,
|
||||||
|
);
|
||||||
|
this.claimsToConfirm = dataByOthersWithoutHidden;
|
||||||
|
this.claimCountWithHidden =
|
||||||
|
dataByOthers.length - dataByOthersWithoutHidden.length;
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error:", error);
|
||||||
|
this.$notify(
|
||||||
|
{
|
||||||
|
group: "alert",
|
||||||
|
type: "danger",
|
||||||
|
title: "Error",
|
||||||
|
text: "There was an error retrieving today's claims to confirm.",
|
||||||
|
},
|
||||||
|
-1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickLoadClaim(jwtId: string) {
|
||||||
|
const route = {
|
||||||
|
path: "/claim/" + encodeURIComponent(jwtId),
|
||||||
|
};
|
||||||
|
this.$router.push(route);
|
||||||
|
}
|
||||||
|
|
||||||
|
record() {
|
||||||
|
alert("Nope");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
52
src/views/QuickActionBvcView.vue
Normal file
52
src/views/QuickActionBvcView.vue
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<template>
|
||||||
|
<QuickNav />
|
||||||
|
<TopMessage />
|
||||||
|
|
||||||
|
<!-- CONTENT -->
|
||||||
|
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
|
||||||
|
<!-- Back -->
|
||||||
|
<div class="text-lg text-center font-light relative px-7">
|
||||||
|
<h1
|
||||||
|
class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
|
||||||
|
@click="$router.back()"
|
||||||
|
>
|
||||||
|
<fa icon="chevron-left" class="fa-fw"></fa>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Heading -->
|
||||||
|
<h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-4">
|
||||||
|
Bountiful Voluntaryist Community Actions
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<router-link
|
||||||
|
:to="{ name: 'quick-action-bvc-begin' }"
|
||||||
|
class="block text-center text-md font-bold uppercase bg-blue-500 text-white mt-2 px-2 py-3 rounded-md"
|
||||||
|
>
|
||||||
|
Beginning of Meeting
|
||||||
|
</router-link>
|
||||||
|
<router-link
|
||||||
|
:to="{ name: 'quick-action-bvc-end' }"
|
||||||
|
class="block text-center text-md font-bold uppercase bg-blue-500 text-white mt-2 px-2 py-3 rounded-md"
|
||||||
|
>
|
||||||
|
End of Meeting
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Vue } from "vue-facing-decorator";
|
||||||
|
|
||||||
|
import QuickNav from "@/components/QuickNav.vue";
|
||||||
|
import TopMessage from "@/components/TopMessage.vue";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
components: {
|
||||||
|
QuickNav,
|
||||||
|
TopMessage,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
export default class QuickActionBvcView extends Vue {}
|
||||||
|
</script>
|
||||||
Reference in New Issue
Block a user