forked from trent_larson/crowd-funder-for-time-pwa
Compare commits
41 Commits
identity-s
...
cleanup-an
| Author | SHA1 | Date | |
|---|---|---|---|
| be348461f1 | |||
| 6e2c596030 | |||
|
|
c502869c5f | ||
|
|
b7aacd63e6 | ||
|
|
5bc0e27b30 | ||
|
|
a4fe94f081 | ||
|
|
8de95566df | ||
|
|
97569697f6 | ||
|
|
b9ed9d748b | ||
|
|
790d44db81 | ||
| e2bf469dc1 | |||
| 592ffacebc | |||
| b706e65598 | |||
|
|
6e3066ae92 | ||
|
|
e8eae544f3 | ||
|
|
34636d6047 | ||
| 91b46eaaee | |||
| 31d1a449ae | |||
| 1248132076 | |||
| 015704c94e | |||
| 540ef916c2 | |||
| bee7c87a8f | |||
| 6bbc88f86c | |||
| 624abbb179 | |||
| 110ed009b2 | |||
| a5892238d5 | |||
| 8eb80a9ede | |||
|
|
32125133f0 | ||
|
|
47ade49e31 | ||
|
|
47ce91cca1 | ||
|
|
3e52b504b0 | ||
|
|
4ecea1ab0e | ||
|
|
b9fdc920ea | ||
|
|
0907d59a6a | ||
| 59ce15c744 | |||
|
|
9960a96a20 | ||
|
|
098c6c0fa0 | ||
| d7a9fb6d54 | |||
| cf2b80b1f5 | |||
| b86323ec83 | |||
| 8add6448fb |
@@ -1,49 +1,41 @@
|
|||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- 01 design ideas for simple gives on the Home page
|
- 40 notifications :
|
||||||
- 02 Discover page - add infinite search
|
- push, where we trigger a ServiceWorker(?) in the app to reach out and check for new data assignee:matthew
|
||||||
|
|
||||||
- 01 add a location for a project via map pin
|
- 01 add a location for a project via map pin
|
||||||
- 04 search by a bounding box for local projects (see API by clicking on "Nearby")
|
- 04 search by a bounding box for local projects (see API by clicking on "Nearby")
|
||||||
- 01 remove all the "form" fields (or at least investigate to see if that page refresh is desired)
|
- 01 Replace Gifted/Give in ContactsView with GiftedDialog assignee:matthew
|
||||||
- 01 Replace Gifted/Give in ContactsView with GiftedDialog
|
- 02 Fix images on projectview - allow choice of image from a pallete of images or a url image (discovery page display also)
|
||||||
- 02 Fix images on projectview: allow choice of image from a pallete of images or a url image.
|
- SEE: https://github.com/dmester/jdenticon assignee:jose
|
||||||
|
|
||||||
- 08 Scan QR code to import into contacts.
|
- 08 Scan QR code to import into contacts assignee:matthew
|
||||||
|
- SEE: https://github.com/gruhn/vue-qrcode-reader
|
||||||
|
|
||||||
- contacts v1 :
|
- Show pop-up or some message confirming that settings & contacts download has been initiated/finished assignee:matthew
|
||||||
- 01 Import contact info a la QR code.
|
|
||||||
- .2 move all "identity" references to temporary account access assignee:trent
|
- Ensure each action sent to the server has a confirmation - eg registration (ie a toast something that dismisses after 5-10s)
|
||||||
|
- SEE: https://github.com/emmanuelsw/notiwind assignee:jose
|
||||||
|
|
||||||
|
- Home Feed & Quick Give screen :
|
||||||
|
- 01 save the feed-viewed status in settings storage ("afterQuery")
|
||||||
|
- 01 quick action - send action, maybe choose via canvas tool
|
||||||
|
- SEE: https://github.com/konvajs/vue-konva
|
||||||
|
|
||||||
|
- 24 Move to Vite assignee:matthew
|
||||||
|
|
||||||
|
- .5 fix where user 0 sees no txns from user 1 on contacts page but sees them on list page
|
||||||
|
- .2 on ProjectViewView, show different messages for "to" and "from" sections if none exist
|
||||||
|
- .2 fix static icon to the right on project page (Matthew - I've made "Rotary" into issuer?) assignee:jose
|
||||||
|
- .2 fix rate limit verbiage (with the new one-per-day allowance) assignee:trent
|
||||||
|
|
||||||
|
- Discuss whether the remaining tasks are worthwhile before MVP release.
|
||||||
|
|
||||||
- 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 show error to user when adding a duplicate contact
|
- .2 show error to user when adding a duplicate contact
|
||||||
- 01 parse input more robustly (with CSV lib and not commas)
|
- 01 parse input more robustly (with CSV lib and not commas)
|
||||||
|
|
||||||
- refactor UI :
|
|
||||||
- .5 Alerts show at the top and can be missed if you've scrolled down on the page, eg. account data download
|
|
||||||
- .2 Make alerts at the top more visible (because they're currently a similar color and sometimes aren't seen)
|
|
||||||
|
|
||||||
- Show pop-up or some message confirming that settings & contacts download has been initiated/finished
|
|
||||||
|
|
||||||
- Ensure each action sent to the server has a confirmation - eg registration
|
|
||||||
|
|
||||||
- Home Feed & Quick Give screen :
|
|
||||||
- 01 save the feed-viewed status in settings storage ("afterQuery")
|
|
||||||
- 01 quick action - send action, maybe choose via canvas tool https://github.com/konvajs/vue-konva
|
|
||||||
|
|
||||||
- .5 customize favicon
|
|
||||||
- 04 allow user to download claims, mine + ones I can see about me from others
|
|
||||||
|
|
||||||
- 24 Move to Vite
|
|
||||||
|
|
||||||
- 40 notifications :
|
|
||||||
- push
|
|
||||||
|
|
||||||
- Discuss whether the remaining tasks are worthwhile before MVP release.
|
|
||||||
|
|
||||||
- 01 fix images on project page, on discovery page
|
|
||||||
- .2 fix static icon to the right on project page (Matthew: I've made "Rotary" into issuer?)
|
|
||||||
|
|
||||||
- stats v1 :
|
- stats v1 :
|
||||||
- 01 show numeric stats
|
- 01 show numeric stats
|
||||||
- 01 link to world for specific stats
|
- 01 link to world for specific stats
|
||||||
@@ -51,7 +43,11 @@ tasks:
|
|||||||
- maybe - allow type annotations in World.js & landmarks.js (since we get this error - "Types are not supported by current JavaScript version")
|
- maybe - allow type annotations in World.js & landmarks.js (since we get this error - "Types are not supported by current JavaScript version")
|
||||||
- 08 convert to cleaner implementation (maybe Drie -- https://github.com/janvorisek/drie)
|
- 08 convert to cleaner implementation (maybe Drie -- https://github.com/janvorisek/drie)
|
||||||
|
|
||||||
- Do we want split first name & last name?
|
- .5 on ProjectView page, show immediate feedback when a gift is given (on list?) -- and consider the same for Home & Contacts pages
|
||||||
|
- .5 customize favicon
|
||||||
|
- 04 allow user to download claims, mine + ones I can see about me from others
|
||||||
|
- Do we want to combine first name & last name?
|
||||||
|
- Show a warning if both giver and recipient are the same (but still allow?)
|
||||||
|
|
||||||
- Release Minimum Viable Product :
|
- Release Minimum Viable Product :
|
||||||
- 08 thorough testing for errors & edge cases
|
- 08 thorough testing for errors & edge cases
|
||||||
@@ -64,9 +60,6 @@ tasks:
|
|||||||
- Test PWA features on Android and iOS.
|
- Test PWA features on Android and iOS.
|
||||||
blocks: ref:https://raw.githubusercontent.com/trentlarson/lives-of-gifts/master/project.yaml#kickstarter%20for%20time
|
blocks: ref:https://raw.githubusercontent.com/trentlarson/lives-of-gifts/master/project.yaml#kickstarter%20for%20time
|
||||||
|
|
||||||
- 40 notifications v+ :
|
|
||||||
- pull, w/ scheduled runs
|
|
||||||
|
|
||||||
- linking between projects or plans :
|
- linking between projects or plans :
|
||||||
- show total time given to & from a project
|
- show total time given to & from a project
|
||||||
- terminology:
|
- terminology:
|
||||||
@@ -95,6 +88,10 @@ tasks:
|
|||||||
|
|
||||||
- Write to or read from a different ledger (eg. private ACDC, attest.sh)
|
- Write to or read from a different ledger (eg. private ACDC, attest.sh)
|
||||||
|
|
||||||
|
- Do we want split first name & last name?
|
||||||
|
|
||||||
|
- 40 notifications v+ :
|
||||||
|
- pull, w/ scheduled runs
|
||||||
|
|
||||||
log:
|
log:
|
||||||
- videos for multiple identities https://youtu.be/p8L87AeD76w and for adding time to contacts https://youtu.be/7Yylczevp10 done:2023-03-29
|
- videos for multiple identities https://youtu.be/p8L87AeD76w and for adding time to contacts https://youtu.be/7Yylczevp10 done:2023-03-29
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-bind:class="computedAlertClassNames()">
|
<div v-bind:class="computedAlertClassNames()">
|
||||||
<button
|
<button
|
||||||
class="close-button bg-slate-200 w-8 leading-loose rounded-full absolute top-2 right-2"
|
class="close-button bg-amber-400 w-8 leading-loose rounded-full absolute top-2 right-2"
|
||||||
@click="onClickClose()"
|
@click="onClickClose()"
|
||||||
>
|
>
|
||||||
<fa icon="xmark"></fa>
|
<fa icon="xmark"></fa>
|
||||||
@@ -28,7 +28,7 @@ export default class AlertMessage extends Vue {
|
|||||||
return {
|
return {
|
||||||
hidden: !this.isAlertVisible,
|
hidden: !this.isAlertVisible,
|
||||||
"dismissable-alert": true,
|
"dismissable-alert": true,
|
||||||
"bg-slate-100": true,
|
"bg-amber-200": true,
|
||||||
"p-5": true,
|
"p-5": true,
|
||||||
rounded: true,
|
rounded: true,
|
||||||
"drop-shadow-lg": true,
|
"drop-shadow-lg": true,
|
||||||
|
|||||||
@@ -1,41 +1,51 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="visible" class="dialog-overlay">
|
<div v-if="visible" class="dialog-overlay">
|
||||||
<div class="dialog">
|
<div class="dialog">
|
||||||
<h1 class="text-lg text-center">
|
<h1 class="text-xl font-bold text-center mb-4">
|
||||||
{{ message }} {{ giver?.name || "somebody not specified" }}
|
{{ message }} {{ giver?.did || "somebody not specified" }}
|
||||||
</h1>
|
</h1>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
class="block w-full rounded border border-slate-400 mb-4 px-3 py-2"
|
class="block w-full rounded border border-slate-400 mb-2 px-3 py-2"
|
||||||
placeholder="What was received"
|
placeholder="What was received"
|
||||||
v-model="description"
|
v-model="description"
|
||||||
/>
|
/>
|
||||||
<div class="flex flex-row">
|
<div class="flex flex-row mb-6">
|
||||||
<span class="py-4">Hours</span>
|
<span
|
||||||
|
class="rounded-l border border-r-0 border-slate-400 bg-slate-200 w-1/3 text-center px-2 py-2"
|
||||||
|
>Hours</span
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="border border-r-0 border-slate-400 bg-slate-200 px-4 py-2"
|
||||||
|
@click="decrement()"
|
||||||
|
>
|
||||||
|
<fa icon="chevron-left" />
|
||||||
|
</div>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
class="block w-8 rounded border border-slate-400 ml-4 text-center"
|
class="w-full border border-r-0 border-slate-400 px-2 py-2 text-center"
|
||||||
v-model="hours"
|
v-model="hours"
|
||||||
/>
|
/>
|
||||||
<div class="flex flex-col px-1">
|
<div
|
||||||
<div>
|
class="rounded-r border border-slate-400 bg-slate-200 px-4 py-2"
|
||||||
<fa icon="square-caret-up" size="2xl" @click="increment()" />
|
@click="increment()"
|
||||||
</div>
|
>
|
||||||
<div>
|
<fa icon="chevron-right" />
|
||||||
<fa icon="square-caret-down" size="2xl" @click="decrement()" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-right">Sign & Send to publish to the world</p>
|
<p class="text-center mb-2 italic">Sign & Send to publish to the world</p>
|
||||||
<div class="text-right">
|
<button
|
||||||
<button class="rounded border border-slate-400" @click="confirm">
|
class="block w-full text-center text-lg font-bold uppercase bg-blue-600 text-white px-2 py-3 rounded-md mb-2"
|
||||||
<span class="m-2">Sign & Send</span>
|
@click="confirm"
|
||||||
</button>
|
>
|
||||||
|
Sign & Send
|
||||||
<button class="rounded border border-slate-400" @click="cancel">
|
</button>
|
||||||
<span class="m-2">Cancel</span>
|
<button
|
||||||
</button>
|
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md"
|
||||||
</div>
|
@click="cancel"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -106,12 +116,14 @@ export default class GiftedDialog extends Vue {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
padding: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog {
|
.dialog {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
width: 50%;
|
width: 100%;
|
||||||
|
max-width: 500px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -82,11 +82,8 @@ export function isHiddenDid(did) {
|
|||||||
/**
|
/**
|
||||||
always returns text, maybe UNNAMED_VISIBLE or UNKNOWN_ENTITY
|
always returns text, maybe UNNAMED_VISIBLE or UNKNOWN_ENTITY
|
||||||
**/
|
**/
|
||||||
export function didInfo(did, activeDid, identifiers, contacts) {
|
export function didInfo(did, activeDid, allMyDids, contacts) {
|
||||||
const myId: IIdentifier | undefined = R.find(
|
const myId: string | undefined = R.find(R.identity, allMyDids);
|
||||||
(i) => i.did === did,
|
|
||||||
identifiers,
|
|
||||||
);
|
|
||||||
if (myId) {
|
if (myId) {
|
||||||
return "You" + (myId.did !== activeDid ? " (Alt ID)" : "");
|
return "You" + (myId.did !== activeDid ? " (Alt ID)" : "");
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
faBurst,
|
faBurst,
|
||||||
faCalendar,
|
faCalendar,
|
||||||
faChevronLeft,
|
faChevronLeft,
|
||||||
|
faChevronRight,
|
||||||
faCircle,
|
faCircle,
|
||||||
faCircleCheck,
|
faCircleCheck,
|
||||||
faCircleQuestion,
|
faCircleQuestion,
|
||||||
@@ -53,6 +54,7 @@ library.add(
|
|||||||
faBurst,
|
faBurst,
|
||||||
faCalendar,
|
faCalendar,
|
||||||
faChevronLeft,
|
faChevronLeft,
|
||||||
|
faChevronRight,
|
||||||
faCircle,
|
faCircle,
|
||||||
faCircleCheck,
|
faCircleCheck,
|
||||||
faCircleQuestion,
|
faCircleQuestion,
|
||||||
|
|||||||
@@ -166,6 +166,14 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
/* webpackChunkName: "statistics" */ "../views/StatisticsView.vue"
|
/* webpackChunkName: "statistics" */ "../views/StatisticsView.vue"
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/contact-gives",
|
||||||
|
name: "contact-gives",
|
||||||
|
component: () =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "statistics" */ "../views/ContactGiftingView.vue"
|
||||||
|
),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
/** @type {*} */
|
/** @type {*} */
|
||||||
|
|||||||
@@ -143,25 +143,23 @@
|
|||||||
|
|
||||||
<!-- QR code popup -->
|
<!-- QR code popup -->
|
||||||
<dialog id="dlgQR" class="backdrop:bg-black/75 rounded-md">
|
<dialog id="dlgQR" class="backdrop:bg-black/75 rounded-md">
|
||||||
<form method="dialog">
|
<div class="text-slate-500 text-center">
|
||||||
<div class="text-slate-500 text-center">
|
<b>ID:</b> <code>did:peer:kl45kj41lk451kl3</code>
|
||||||
<b>ID:</b> <code>did:peer:kl45kj41lk451kl3</code>
|
</div>
|
||||||
</div>
|
<img src="/img/sample-qr-code.png" class="w-full mb-3" />
|
||||||
<img src="/img/sample-qr-code.png" class="w-full mb-3" />
|
|
||||||
|
|
||||||
<button
|
<button
|
||||||
value="cancel"
|
value="cancel"
|
||||||
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-2"
|
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-2"
|
||||||
>
|
>
|
||||||
Copy to Clipboard
|
Copy to Clipboard
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
value="cancel"
|
value="cancel"
|
||||||
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md"
|
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md"
|
||||||
>
|
>
|
||||||
Close
|
Close
|
||||||
</button>
|
</button>
|
||||||
</form>
|
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
<h3
|
<h3
|
||||||
@@ -260,20 +258,6 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="numAccounts > 0" class="flex py-2">
|
|
||||||
Switch Identifier
|
|
||||||
<span>
|
|
||||||
<button class="text-blue-500 px-2" @click="switchAccount(0)">
|
|
||||||
None
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
<span v-for="accountNum in numAccounts" :key="accountNum">
|
|
||||||
<button class="text-blue-500 px-2" @click="switchAccount(accountNum)">
|
|
||||||
#{{ accountNum }}
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button class="text-blue-500">
|
<button class="text-blue-500">
|
||||||
<router-link
|
<router-link
|
||||||
@@ -299,12 +283,12 @@ import { useClipboard } from "@vueuse/core";
|
|||||||
|
|
||||||
import { AppString } from "@/constants/app";
|
import { AppString } from "@/constants/app";
|
||||||
import { db, accountsDB } from "@/db";
|
import { db, accountsDB } from "@/db";
|
||||||
import { AccountsSchema } from "@/db/tables/accounts";
|
|
||||||
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
||||||
import { accessToken } from "@/libs/crypto";
|
import { accessToken } from "@/libs/crypto";
|
||||||
import { AxiosError } from "axios/index";
|
import { AxiosError } from "axios/index";
|
||||||
import AlertMessage from "@/components/AlertMessage";
|
import AlertMessage from "@/components/AlertMessage";
|
||||||
import QuickNav from "@/components/QuickNav";
|
import QuickNav from "@/components/QuickNav";
|
||||||
|
import { IIdentifier } from "@veramo/core";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const Buffer = require("buffer/").Buffer;
|
const Buffer = require("buffer/").Buffer;
|
||||||
@@ -326,7 +310,6 @@ export default class AccountViewView extends Vue {
|
|||||||
limitsMessage = "";
|
limitsMessage = "";
|
||||||
loadingLimits = true; // might as well now that we do it on mount, to avoid flashing the registration message
|
loadingLimits = true; // might as well now that we do it on mount, to avoid flashing the registration message
|
||||||
showContactGives = false;
|
showContactGives = false;
|
||||||
private accounts: AccountsSchema;
|
|
||||||
|
|
||||||
showDidCopy = false;
|
showDidCopy = false;
|
||||||
showDerCopy = false;
|
showDerCopy = false;
|
||||||
@@ -344,12 +327,6 @@ export default class AccountViewView extends Vue {
|
|||||||
.equals(activeDid)
|
.equals(activeDid)
|
||||||
.first();
|
.first();
|
||||||
const identity = JSON.parse(account?.identity || "null");
|
const identity = JSON.parse(account?.identity || "null");
|
||||||
|
|
||||||
if (!identity) {
|
|
||||||
throw new Error(
|
|
||||||
"Attempted to load Give records with no identity available.",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return identity;
|
return identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,9 +357,8 @@ export default class AccountViewView extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async beforeCreate() {
|
async beforeCreate() {
|
||||||
accountsDB.open();
|
await accountsDB.open();
|
||||||
this.accounts = accountsDB.accounts;
|
this.numAccounts = await accountsDB.accounts.count();
|
||||||
this.numAccounts = await this.accounts.count();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async created() {
|
async created() {
|
||||||
@@ -404,14 +380,18 @@ export default class AccountViewView extends Vue {
|
|||||||
|
|
||||||
const identity = await this.getIdentity(this.activeDid);
|
const identity = await this.getIdentity(this.activeDid);
|
||||||
|
|
||||||
this.publicHex = identity.keys[0].publicKeyHex;
|
if (identity) {
|
||||||
this.publicBase64 = Buffer.from(this.publicHex, "hex").toString("base64");
|
this.publicHex = identity.keys[0].publicKeyHex;
|
||||||
this.derivationPath = identity.keys[0].meta.derivationPath;
|
this.publicBase64 = Buffer.from(this.publicHex, "hex").toString(
|
||||||
|
"base64",
|
||||||
|
);
|
||||||
|
this.derivationPath = identity.keys[0].meta.derivationPath;
|
||||||
|
|
||||||
db.settings.update(MASTER_SETTINGS_KEY, {
|
db.settings.update(MASTER_SETTINGS_KEY, {
|
||||||
activeDid: identity.did,
|
activeDid: identity.did,
|
||||||
});
|
});
|
||||||
this.checkLimits();
|
this.checkLimitsFor(identity);
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (
|
if (
|
||||||
err.message ===
|
err.message ===
|
||||||
@@ -470,12 +450,18 @@ export default class AccountViewView extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async checkLimits() {
|
async checkLimits() {
|
||||||
|
const identity = await this.getIdentity(this.activeDid);
|
||||||
|
if (identity) {
|
||||||
|
this.checkLimitsFor(identity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkLimitsFor(identity: IIdentifier) {
|
||||||
this.loadingLimits = true;
|
this.loadingLimits = true;
|
||||||
this.limitsMessage = "";
|
this.limitsMessage = "";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const url = this.apiServer + "/api/report/rateLimits";
|
const url = this.apiServer + "/api/report/rateLimits";
|
||||||
const identity = await this.getIdentity(this.activeDid);
|
|
||||||
const headers = await this.getHeaders(identity);
|
const headers = await this.getHeaders(identity);
|
||||||
|
|
||||||
const resp = await this.axios.get(url, { headers });
|
const resp = await this.axios.get(url, { headers });
|
||||||
|
|||||||
@@ -15,35 +15,33 @@
|
|||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form>
|
<p class="text-center text-xl mb-4 font-light">
|
||||||
<p class="text-center text-xl mb-4 font-light">
|
Would you like to add <i>Firstname</i> to your network?
|
||||||
Would you like to add <i>Firstname</i> to your network?
|
</p>
|
||||||
</p>
|
|
||||||
|
|
||||||
<!-- Account Details -->
|
<!-- Account Details -->
|
||||||
<div class="bg-slate-100 rounded-md overflow-hidden px-4 py-3 mb-4">
|
<div class="bg-slate-100 rounded-md overflow-hidden px-4 py-3 mb-4">
|
||||||
<h2 class="text-xl font-semibold mb-2">Firstname Lastname</h2>
|
<h2 class="text-xl font-semibold mb-2">Firstname Lastname</h2>
|
||||||
|
|
||||||
<div class="text-slate-500 text-sm font-bold">ID</div>
|
<div class="text-slate-500 text-sm font-bold">ID</div>
|
||||||
<div class="text-sm text-slate-500 mb-1">
|
<div class="text-sm text-slate-500 mb-1">
|
||||||
<span><code>did:peer:kl45kj41lk451kl3</code></span>
|
<span><code>did:peer:kl45kj41lk451kl3</code></span>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="mt-8">
|
<div class="mt-8">
|
||||||
<input
|
<input
|
||||||
type="submit"
|
type="submit"
|
||||||
class="block w-full text-center text-lg font-bold uppercase bg-blue-600 text-white px-2 py-3 rounded-md mb-2"
|
class="block w-full text-center text-lg font-bold uppercase bg-blue-600 text-white px-2 py-3 rounded-md mb-2"
|
||||||
value="Add Contact"
|
value="Add Contact"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md"
|
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md"
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<QuickNav selected="Contacts"></QuickNav>
|
<QuickNav selected="Contacts"></QuickNav>
|
||||||
<section id="Content" class="p-6 pb-24">
|
<section id="Content" class="p-6 pb-24">
|
||||||
|
<!-- Breadcrumb -->
|
||||||
|
<div id="ViewBreadcrumb" class="mb-8">
|
||||||
|
<h1 class="text-lg text-center font-light relative px-7">
|
||||||
|
<!-- Back -->
|
||||||
|
<router-link
|
||||||
|
:to="{ name: 'contacts' }"
|
||||||
|
class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
|
||||||
|
><fa icon="chevron-left" class="fa-fw"></fa
|
||||||
|
></router-link>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8">
|
<h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8">
|
||||||
Given with {{ contact?.name }}
|
Given with {{ contact?.name }}
|
||||||
</h1>
|
</h1>
|
||||||
@@ -78,7 +90,6 @@ import * as R from "ramda";
|
|||||||
|
|
||||||
import { Component, Vue } from "vue-facing-decorator";
|
import { Component, Vue } from "vue-facing-decorator";
|
||||||
import { accountsDB, db } from "@/db";
|
import { accountsDB, db } from "@/db";
|
||||||
import { AccountsSchema } from "@/db/tables/accounts";
|
|
||||||
import { Contact } from "@/db/tables/contacts";
|
import { Contact } from "@/db/tables/contacts";
|
||||||
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
||||||
import { accessToken, SimpleSigner } from "@/libs/crypto";
|
import { accessToken, SimpleSigner } from "@/libs/crypto";
|
||||||
@@ -101,13 +112,11 @@ export default class ContactsView extends Vue {
|
|||||||
giveRecords: Array<GiveServerRecord> = [];
|
giveRecords: Array<GiveServerRecord> = [];
|
||||||
alertTitle = "";
|
alertTitle = "";
|
||||||
alertMessage = "";
|
alertMessage = "";
|
||||||
accounts: AccountsSchema;
|
|
||||||
numAccounts = 0;
|
numAccounts = 0;
|
||||||
|
|
||||||
async beforeCreate() {
|
async beforeCreate() {
|
||||||
accountsDB.open();
|
await accountsDB.open();
|
||||||
this.accounts = accountsDB.accounts;
|
this.numAccounts = await accountsDB.accounts.count();
|
||||||
this.numAccounts = await this.accounts.count();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getIdentity(activeDid) {
|
public async getIdentity(activeDid) {
|
||||||
|
|||||||
265
src/views/ContactGiftingView.vue
Normal file
265
src/views/ContactGiftingView.vue
Normal file
@@ -0,0 +1,265 @@
|
|||||||
|
<template>
|
||||||
|
<QuickNav selected="Home"></QuickNav>
|
||||||
|
<!-- CONTENT -->
|
||||||
|
<section id="Content" class="p-6 pb-24">
|
||||||
|
<!-- Breadcrumb -->
|
||||||
|
<div id="ViewBreadcrumb" class="mb-8">
|
||||||
|
<h1 class="text-lg text-center font-light relative px-7">
|
||||||
|
<!-- Back -->
|
||||||
|
<router-link
|
||||||
|
:to="{ name: 'home' }"
|
||||||
|
class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
|
||||||
|
><fa icon="chevron-left" class="fa-fw"></fa
|
||||||
|
></router-link>
|
||||||
|
|
||||||
|
Give to Contacts
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Quick Search -->
|
||||||
|
|
||||||
|
<!-- Initial Loading Animation -->
|
||||||
|
|
||||||
|
<!-- Results List -->
|
||||||
|
<ul class="border-t border-slate-300">
|
||||||
|
<li class="border-b border-slate-300 py-3">
|
||||||
|
<h2 class="text-base flex gap-4 items-center">
|
||||||
|
<span class="grow italic"
|
||||||
|
><fa icon="question-circle" class="fa-fw fa-xl text-slate-400"></fa>
|
||||||
|
Anonymous
|
||||||
|
</span>
|
||||||
|
<span class="text-right">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
@click="openDialog()"
|
||||||
|
class="block w-full text-center text-sm uppercase bg-blue-600 text-white px-3 py-1.5 rounded-md"
|
||||||
|
>
|
||||||
|
<fa icon="gift" class="fa-fw"></fa>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
v-for="contact in allContacts"
|
||||||
|
:key="contact.did"
|
||||||
|
class="border-b border-slate-300 py-3"
|
||||||
|
>
|
||||||
|
<h2 class="text-base flex gap-4 items-center">
|
||||||
|
<span class="grow font-semibold"
|
||||||
|
><fa icon="user" class="fa-fw fa-xl text-slate-400"></fa>
|
||||||
|
{{ contact.name || "(no name)" }}
|
||||||
|
</span>
|
||||||
|
<span class="text-right">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
@click="openDialog(contact)"
|
||||||
|
class="block w-full text-center text-sm uppercase bg-blue-600 text-white px-3 py-1.5 rounded-md"
|
||||||
|
>
|
||||||
|
<fa icon="gift" class="fa-fw"></fa>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<GiftedDialog
|
||||||
|
ref="customDialog"
|
||||||
|
@dialog-result="handleDialogResult"
|
||||||
|
message="Received from"
|
||||||
|
>
|
||||||
|
</GiftedDialog>
|
||||||
|
<AlertMessage
|
||||||
|
:alertTitle="alertTitle"
|
||||||
|
:alertMessage="alertMessage"
|
||||||
|
></AlertMessage>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Vue } from "vue-facing-decorator";
|
||||||
|
import GiftedDialog from "@/components/GiftedDialog.vue";
|
||||||
|
import { db, accountsDB } from "@/db";
|
||||||
|
import { AccountsSchema } from "@/db/tables/accounts";
|
||||||
|
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
||||||
|
import { accessToken } from "@/libs/crypto";
|
||||||
|
import { createAndSubmitGive } from "@/libs/endorserServer";
|
||||||
|
import { Account } from "@/db/tables/accounts";
|
||||||
|
import { Contact } from "@/db/tables/contacts";
|
||||||
|
import AlertMessage from "@/components/AlertMessage";
|
||||||
|
import QuickNav from "@/components/QuickNav";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
components: { GiftedDialog, AlertMessage, QuickNav },
|
||||||
|
})
|
||||||
|
export default class HomeView extends Vue {
|
||||||
|
activeDid = "";
|
||||||
|
allAccounts: Array<Account> = [];
|
||||||
|
allContacts: Array<Contact> = [];
|
||||||
|
apiServer = "";
|
||||||
|
isHiddenSpinner = true;
|
||||||
|
alertTitle = "";
|
||||||
|
alertMessage = "";
|
||||||
|
accounts: AccountsSchema;
|
||||||
|
numAccounts = 0;
|
||||||
|
|
||||||
|
async beforeCreate() {
|
||||||
|
accountsDB.open();
|
||||||
|
this.accounts = accountsDB.accounts;
|
||||||
|
this.numAccounts = await this.accounts.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getIdentity(activeDid) {
|
||||||
|
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 Give records with no identity available.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getHeaders(identity) {
|
||||||
|
const token = await accessToken(identity);
|
||||||
|
const headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: "Bearer " + token,
|
||||||
|
};
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
async created() {
|
||||||
|
try {
|
||||||
|
await accountsDB.open();
|
||||||
|
this.allAccounts = await accountsDB.accounts.toArray();
|
||||||
|
await db.open();
|
||||||
|
const settings = await db.settings.get(MASTER_SETTINGS_KEY);
|
||||||
|
this.apiServer = settings?.apiServer || "";
|
||||||
|
this.activeDid = settings?.activeDid || "";
|
||||||
|
this.allContacts = await db.contacts.toArray();
|
||||||
|
this.feedLastViewedId = settings?.lastViewedClaimId;
|
||||||
|
this.updateAllFeed();
|
||||||
|
} catch (err) {
|
||||||
|
this.alertTitle = "Error";
|
||||||
|
this.alertMessage =
|
||||||
|
err.userMessage ||
|
||||||
|
"There was an error retrieving the latest sweet, sweet action.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async buildHeaders() {
|
||||||
|
const headers = { "Content-Type": "application/json" };
|
||||||
|
|
||||||
|
if (this.activeDid) {
|
||||||
|
await accountsDB.open();
|
||||||
|
const allAccounts = await accountsDB.accounts.toArray();
|
||||||
|
const account = allAccounts.find((acc) => acc.did === this.activeDid);
|
||||||
|
const identity = JSON.parse(account?.identity || "null");
|
||||||
|
|
||||||
|
if (!identity) {
|
||||||
|
throw new Error(
|
||||||
|
"An ID is chosen but there are no keys for it so it cannot be used to talk with the service.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
headers["Authorization"] = "Bearer " + (await accessToken(identity));
|
||||||
|
} else {
|
||||||
|
// it's OK without auth... we just won't get any identifiers
|
||||||
|
}
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
openDialog(giver) {
|
||||||
|
this.$refs.customDialog.open(giver);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDialogResult(result) {
|
||||||
|
if (result.action === "confirm") {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
this.recordGive(result.contact?.did, result.description, result.hours);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// action was "cancel" so do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param giverDid may be null
|
||||||
|
* @param description may be an empty string
|
||||||
|
* @param hours may be 0
|
||||||
|
*/
|
||||||
|
public async recordGive(giverDid, description, hours) {
|
||||||
|
if (!this.activeDid) {
|
||||||
|
this.setAlert(
|
||||||
|
"Error",
|
||||||
|
"You must select an identity before you can record a give.",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!description && !hours) {
|
||||||
|
this.setAlert(
|
||||||
|
"Error",
|
||||||
|
"You must enter a description or some number of hours.",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const identity = await this.getIdentity(this.activeDid);
|
||||||
|
const result = await createAndSubmitGive(
|
||||||
|
this.axios,
|
||||||
|
this.apiServer,
|
||||||
|
identity,
|
||||||
|
giverDid,
|
||||||
|
this.activeDid,
|
||||||
|
description,
|
||||||
|
hours,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isGiveCreationError(result)) {
|
||||||
|
const errorMessage = getGiveCreationErrorMessage(result);
|
||||||
|
console.log("Error with give result:", result);
|
||||||
|
this.setAlert(
|
||||||
|
"Error",
|
||||||
|
errorMessage || "There was an error recording the give.",
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.setAlert("Success", "That gift was recorded.");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error with give caught:", error);
|
||||||
|
this.setAlert(
|
||||||
|
"Error",
|
||||||
|
getGiveErrorMessage(error) || "There was an error recording the give.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private setAlert(title, message) {
|
||||||
|
this.alertTitle = title;
|
||||||
|
this.alertMessage = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper functions for readability
|
||||||
|
|
||||||
|
isGiveCreationError(result) {
|
||||||
|
return result.status !== 201 || result.data?.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
getGiveCreationErrorMessage(result) {
|
||||||
|
return result.data?.error?.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
getGiveErrorMessage(error) {
|
||||||
|
return error.userMessage || error.response?.data?.error?.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -14,75 +14,69 @@
|
|||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form>
|
<h3 class="text-sm uppercase font-semibold mb-2">Scan a QR Code…</h3>
|
||||||
<h3 class="text-sm uppercase font-semibold mb-2">Scan a QR Code…</h3>
|
<div class="bg-black rounded overflow-hidden relative mb-4">
|
||||||
<div class="bg-black rounded overflow-hidden relative mb-4">
|
<img src="https://picsum.photos/400/400?random=1" class="w-full" />
|
||||||
<img src="https://picsum.photos/400/400?random=1" class="w-full" />
|
|
||||||
|
|
||||||
<!-- Darken overlay -->
|
<!-- Darken overlay -->
|
||||||
<!-- Top -->
|
<!-- Top -->
|
||||||
<div class="absolute top-0 left-0 right-0 bg-black/50 h-1/4"></div>
|
<div class="absolute top-0 left-0 right-0 bg-black/50 h-1/4"></div>
|
||||||
<!-- Reft -->
|
<!-- Reft -->
|
||||||
<div class="absolute top-1/4 bottom-1/4 left-0 bg-black/50 w-1/4"></div>
|
<div class="absolute top-1/4 bottom-1/4 left-0 bg-black/50 w-1/4"></div>
|
||||||
<!-- Right -->
|
<!-- Right -->
|
||||||
<div
|
<div class="absolute top-1/4 bottom-1/4 right-0 bg-black/50 w-1/4"></div>
|
||||||
class="absolute top-1/4 bottom-1/4 right-0 bg-black/50 w-1/4"
|
<!-- Bottom -->
|
||||||
></div>
|
<div class="absolute bottom-0 left-0 right-0 bg-black/50 h-1/4"></div>
|
||||||
<!-- Bottom -->
|
|
||||||
<div class="absolute bottom-0 left-0 right-0 bg-black/50 h-1/4"></div>
|
|
||||||
|
|
||||||
<!-- Reticle overlay -->
|
<!-- Reticle overlay -->
|
||||||
<!-- Top-left -->
|
<!-- Top-left -->
|
||||||
<div
|
<div
|
||||||
class="absolute top-1/4 left-1/4 h-6 w-6 border-white border-t-4 border-l-4 drop-shadow"
|
class="absolute top-1/4 left-1/4 h-6 w-6 border-white border-t-4 border-l-4 drop-shadow"
|
||||||
></div>
|
></div>
|
||||||
<!-- Top-right -->
|
<!-- Top-right -->
|
||||||
<div
|
<div
|
||||||
class="absolute top-1/4 right-1/4 h-6 w-6 border-white border-t-4 border-r-4 drop-shadow"
|
class="absolute top-1/4 right-1/4 h-6 w-6 border-white border-t-4 border-r-4 drop-shadow"
|
||||||
></div>
|
></div>
|
||||||
<!-- Bottom-left -->
|
<!-- Bottom-left -->
|
||||||
<div
|
<div
|
||||||
class="absolute bottom-1/4 left-1/4 h-6 w-6 border-white border-b-4 border-l-4 drop-shadow"
|
class="absolute bottom-1/4 left-1/4 h-6 w-6 border-white border-b-4 border-l-4 drop-shadow"
|
||||||
></div>
|
></div>
|
||||||
<!-- Bottom-right -->
|
<!-- Bottom-right -->
|
||||||
<div
|
<div
|
||||||
class="absolute bottom-1/4 right-1/4 h-6 w-6 border-white border-b-4 border-r-4 drop-shadow"
|
class="absolute bottom-1/4 right-1/4 h-6 w-6 border-white border-b-4 border-r-4 drop-shadow"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 class="text-sm uppercase font-semibold mb-2">
|
<h3 class="text-sm uppercase font-semibold mb-2">…or Enter Contact Data</h3>
|
||||||
…or Enter Contact Data
|
<input
|
||||||
</h3>
|
type="text"
|
||||||
|
placeholder="Name (optional)"
|
||||||
|
class="block w-full rounded border border-slate-400 mb-2 px-3 py-2"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="ID"
|
||||||
|
class="block w-full rounded border border-slate-400 mb-2 px-3 py-2"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Public Key (optional)"
|
||||||
|
class="block w-full rounded border border-slate-400 mb-4 px-3 py-2"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="mt-8">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="submit"
|
||||||
placeholder="Name (optional)"
|
class="block w-full text-center text-lg font-bold uppercase bg-blue-600 text-white px-2 py-3 rounded-md mb-2"
|
||||||
class="block w-full rounded border border-slate-400 mb-2 px-3 py-2"
|
value="Look Up Contact"
|
||||||
/>
|
/>
|
||||||
<input
|
<button
|
||||||
type="text"
|
type="button"
|
||||||
placeholder="ID"
|
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md"
|
||||||
class="block w-full rounded border border-slate-400 mb-2 px-3 py-2"
|
>
|
||||||
/>
|
Cancel
|
||||||
<input
|
</button>
|
||||||
type="text"
|
</div>
|
||||||
placeholder="Public Key (optional)"
|
|
||||||
class="block w-full rounded border border-slate-400 mb-4 px-3 py-2"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="mt-8">
|
|
||||||
<input
|
|
||||||
type="submit"
|
|
||||||
class="block w-full text-center text-lg font-bold uppercase bg-blue-600 text-white px-2 py-3 rounded-md mb-2"
|
|
||||||
value="Look Up Contact"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md"
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -140,12 +140,6 @@
|
|||||||
>
|
>
|
||||||
{{ givenByMeDescriptions[contact.did] }}
|
{{ givenByMeDescriptions[contact.did] }}
|
||||||
</span>
|
</span>
|
||||||
<button
|
|
||||||
class="text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-6"
|
|
||||||
@click="onClickAddGive(activeDid, contact.did)"
|
|
||||||
>
|
|
||||||
+
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="tooltip px-2">
|
<div class="tooltip px-2">
|
||||||
from:
|
from:
|
||||||
@@ -165,12 +159,6 @@
|
|||||||
>
|
>
|
||||||
{{ givenToMeDescriptions[contact.did] }}
|
{{ givenToMeDescriptions[contact.did] }}
|
||||||
</span>
|
</span>
|
||||||
<button
|
|
||||||
class="text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-6"
|
|
||||||
@click="onClickAddGive(contact.did, activeDid)"
|
|
||||||
>
|
|
||||||
+
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<router-link
|
<router-link
|
||||||
:to="{
|
:to="{
|
||||||
@@ -179,6 +167,9 @@
|
|||||||
}"
|
}"
|
||||||
class="tooltip"
|
class="tooltip"
|
||||||
>
|
>
|
||||||
|
<button>
|
||||||
|
<fa icon="gift" class="pt-1 pr-2 text-slate-500" />Give
|
||||||
|
</button>
|
||||||
<fa icon="file-lines" class="text-slate-600 fa-fw ml-1" />
|
<fa icon="file-lines" class="text-slate-600 fa-fw ml-1" />
|
||||||
<span class="tooltiptext-left">See All Given Activity</span>
|
<span class="tooltiptext-left">See All Given Activity</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
@@ -212,12 +203,13 @@ import {
|
|||||||
import { Component, Vue } from "vue-facing-decorator";
|
import { Component, Vue } from "vue-facing-decorator";
|
||||||
import AlertMessage from "@/components/AlertMessage";
|
import AlertMessage from "@/components/AlertMessage";
|
||||||
import QuickNav from "@/components/QuickNav";
|
import QuickNav from "@/components/QuickNav";
|
||||||
|
import GiftedDialog from "@/components/GiftedDialog.vue";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const Buffer = require("buffer/").Buffer;
|
const Buffer = require("buffer/").Buffer;
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: { AlertMessage, QuickNav },
|
components: { AlertMessage, QuickNav, GiftedDialog },
|
||||||
})
|
})
|
||||||
export default class ContactsView extends Vue {
|
export default class ContactsView extends Vue {
|
||||||
activeDid = "";
|
activeDid = "";
|
||||||
|
|||||||
@@ -7,22 +7,41 @@
|
|||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<div class="mb-8">
|
<div class="mb-8">
|
||||||
<h1 class="text-2xl">Quick Action</h1>
|
<h2 class="text-xl font-bold">Quick Action</h2>
|
||||||
<p>Choose a contact to whom to show appreciation:</p>
|
<p class="mb-4">Show appreciation to a contact:</p>
|
||||||
<!-- similar contact selection code is in multiple places -->
|
|
||||||
<div class="px-4">
|
<ul class="grid grid-cols-4 gap-x-3 gap-y-5 text-center mb-5">
|
||||||
<button
|
<li
|
||||||
v-for="contact in allContacts"
|
v-for="contact in allContacts"
|
||||||
:key="contact.did"
|
:key="contact.did"
|
||||||
@click="openDialog(contact)"
|
@click="openDialog(contact)"
|
||||||
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-2"
|
|
||||||
>
|
>
|
||||||
{{ contact.name || "(no name)" }}
|
<div class="mb-1">
|
||||||
</button>
|
<fa icon="user" class="fa-fw fa-xl text-slate-400"></fa>
|
||||||
<span v-if="allContacts.length > 0"> or </span>
|
</div>
|
||||||
<button @click="openDialog()" class="text-blue-500">
|
<h3
|
||||||
someone not specified
|
class="text-xs font-medium text-ellipsis whitespace-nowrap overflow-hidden"
|
||||||
</button>
|
>
|
||||||
|
{{ contact.name || contact.did }}
|
||||||
|
</h3>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<!-- Ideally, this button should only be visible when the active account has more than 7 or 11 contacts in their list (we want to limit the grid count above to 8 or 12 accounts to keep it compact) -->
|
||||||
|
<router-link
|
||||||
|
v-if="allContacts.length > 7"
|
||||||
|
:to="{ name: 'contact-gives' }"
|
||||||
|
class="block text-center text-md font-bold uppercase bg-slate-500 text-white px-2 py-3 rounded-md"
|
||||||
|
>
|
||||||
|
Show More Contacts…
|
||||||
|
</router-link>
|
||||||
|
|
||||||
|
<!-- If there are no contacts, show this instead: -->
|
||||||
|
<div
|
||||||
|
class="rounded border border-dashed border-slate-300 bg-slate-100 px-4 py-3 text-center italic text-slate-500"
|
||||||
|
v-if="allContacts.length === 0"
|
||||||
|
>
|
||||||
|
(No contacts to show.)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -33,29 +52,27 @@
|
|||||||
>
|
>
|
||||||
</GiftedDialog>
|
</GiftedDialog>
|
||||||
|
|
||||||
<div>
|
<div class="bg-slate-100 rounded-md overflow-hidden px-4 py-3 mb-4">
|
||||||
<h1 class="text-2xl">Latest Activity</h1>
|
<h2 class="text-xl font-bold mb-4">Latest Activity</h2>
|
||||||
<span :class="{ hidden: isHiddenSpinner }">
|
<div :class="{ hidden: isHiddenSpinner }">
|
||||||
<fa icon="spinner" class="fa-spin-pulse"></fa>
|
<p class="text-slate-500 text-center italic mt-4 mb-4">
|
||||||
Loading…
|
<fa icon="spinner" class="fa-spin-pulse"></fa> Loading…
|
||||||
</span>
|
</p>
|
||||||
<ul>
|
</div>
|
||||||
|
<ul class="border-t border-slate-300">
|
||||||
<li
|
<li
|
||||||
class="border-b border-slate-300 py-2"
|
class="border-b border-slate-300 py-2"
|
||||||
v-for="record in feedData"
|
v-for="record in feedData"
|
||||||
:key="record.jwtId"
|
:key="record.jwtId"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="border-b border-dashed border-slate-400 text-orange-400 py-2 mb-2 font-bold uppercase text-sm"
|
class="border-b border-dashed border-slate-400 text-orange-400 pb-2 mb-2 font-bold uppercase text-sm"
|
||||||
v-if="record.jwtId == feedLastViewedId"
|
v-if="record.jwtId == feedLastViewedId"
|
||||||
>
|
>
|
||||||
You've seen all claims below:
|
You've seen all claims below:
|
||||||
</div>
|
</div>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<fa
|
<fa icon="gift" class="pt-1 pr-2 text-slate-500"></fa>
|
||||||
icon="gift"
|
|
||||||
class="fa-fw flex-none pt-1 pr-2 text-slate-500"
|
|
||||||
></fa>
|
|
||||||
<!-- icon values: "coins" = money; "clock" = time; "gift" = others -->
|
<!-- icon values: "coins" = money; "clock" = time; "gift" = others -->
|
||||||
<span class="">{{ this.giveDescription(record) }}</span>
|
<span class="">{{ this.giveDescription(record) }}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -73,11 +90,9 @@
|
|||||||
import { Component, Vue } from "vue-facing-decorator";
|
import { Component, Vue } from "vue-facing-decorator";
|
||||||
import GiftedDialog from "@/components/GiftedDialog.vue";
|
import GiftedDialog from "@/components/GiftedDialog.vue";
|
||||||
import { db, accountsDB } from "@/db";
|
import { db, accountsDB } from "@/db";
|
||||||
import { AccountsSchema } from "@/db/tables/accounts";
|
|
||||||
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
||||||
import { accessToken } from "@/libs/crypto";
|
import { accessToken } from "@/libs/crypto";
|
||||||
import { createAndSubmitGive, didInfo } from "@/libs/endorserServer";
|
import { createAndSubmitGive, didInfo } from "@/libs/endorserServer";
|
||||||
import { Account } from "@/db/tables/accounts";
|
|
||||||
import { Contact } from "@/db/tables/contacts";
|
import { Contact } from "@/db/tables/contacts";
|
||||||
import AlertMessage from "@/components/AlertMessage";
|
import AlertMessage from "@/components/AlertMessage";
|
||||||
import QuickNav from "@/components/QuickNav";
|
import QuickNav from "@/components/QuickNav";
|
||||||
@@ -87,8 +102,8 @@ import QuickNav from "@/components/QuickNav";
|
|||||||
})
|
})
|
||||||
export default class HomeView extends Vue {
|
export default class HomeView extends Vue {
|
||||||
activeDid = "";
|
activeDid = "";
|
||||||
allAccounts: Array<Account> = [];
|
|
||||||
allContacts: Array<Contact> = [];
|
allContacts: Array<Contact> = [];
|
||||||
|
allMyDids: Array<string> = [];
|
||||||
apiServer = "";
|
apiServer = "";
|
||||||
feedAllLoaded = false;
|
feedAllLoaded = false;
|
||||||
feedData = [];
|
feedData = [];
|
||||||
@@ -97,13 +112,11 @@ export default class HomeView extends Vue {
|
|||||||
isHiddenSpinner = true;
|
isHiddenSpinner = true;
|
||||||
alertTitle = "";
|
alertTitle = "";
|
||||||
alertMessage = "";
|
alertMessage = "";
|
||||||
accounts: AccountsSchema;
|
|
||||||
numAccounts = 0;
|
numAccounts = 0;
|
||||||
|
|
||||||
async beforeCreate() {
|
async beforeCreate() {
|
||||||
accountsDB.open();
|
await accountsDB.open();
|
||||||
this.accounts = accountsDB.accounts;
|
this.numAccounts = await accountsDB.accounts.count();
|
||||||
this.numAccounts = await this.accounts.count();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getIdentity(activeDid) {
|
public async getIdentity(activeDid) {
|
||||||
@@ -134,7 +147,9 @@ export default class HomeView extends Vue {
|
|||||||
async created() {
|
async created() {
|
||||||
try {
|
try {
|
||||||
await accountsDB.open();
|
await accountsDB.open();
|
||||||
this.allAccounts = await accountsDB.accounts.toArray();
|
const allAccounts = await accountsDB.accounts.toArray();
|
||||||
|
this.allMyDids = allAccounts.map((acc) => acc.did);
|
||||||
|
|
||||||
await db.open();
|
await db.open();
|
||||||
const settings = await db.settings.get(MASTER_SETTINGS_KEY);
|
const settings = await db.settings.get(MASTER_SETTINGS_KEY);
|
||||||
this.apiServer = settings?.apiServer || "";
|
this.apiServer = settings?.apiServer || "";
|
||||||
@@ -231,14 +246,13 @@ export default class HomeView extends Vue {
|
|||||||
if (claim.claim) {
|
if (claim.claim) {
|
||||||
claim = claim.claim;
|
claim = claim.claim;
|
||||||
}
|
}
|
||||||
|
|
||||||
// agent.did is for legacy data, before March 2023
|
// agent.did is for legacy data, before March 2023
|
||||||
const giverDid =
|
const giverDid =
|
||||||
claim.agent?.identifier || claim.agent?.did || giveRecord.issuer;
|
claim.agent?.identifier || claim.agent?.did || giveRecord.issuer;
|
||||||
const giverInfo = didInfo(
|
const giverInfo = didInfo(
|
||||||
giverDid,
|
giverDid,
|
||||||
this.activeDid,
|
this.activeDid,
|
||||||
this.allAccounts,
|
this.allMyDids,
|
||||||
this.allContacts,
|
this.allContacts,
|
||||||
);
|
);
|
||||||
const gaveAmount = claim.object?.amountOfThisGood
|
const gaveAmount = claim.object?.amountOfThisGood
|
||||||
@@ -251,7 +265,7 @@ export default class HomeView extends Vue {
|
|||||||
didInfo(
|
didInfo(
|
||||||
gaveRecipientId,
|
gaveRecipientId,
|
||||||
this.activeDid,
|
this.activeDid,
|
||||||
this.allAccounts,
|
this.allMyDids,
|
||||||
this.allContacts,
|
this.allContacts,
|
||||||
)
|
)
|
||||||
: "";
|
: "";
|
||||||
@@ -273,7 +287,7 @@ export default class HomeView extends Vue {
|
|||||||
handleDialogResult(result) {
|
handleDialogResult(result) {
|
||||||
if (result.action === "confirm") {
|
if (result.action === "confirm") {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
this.recordGive(result.contact?.did, result.description, result.hours);
|
this.recordGive(result.giver?.did, result.description, result.hours);
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -316,8 +330,8 @@ export default class HomeView extends Vue {
|
|||||||
hours,
|
hours,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isGiveCreationError(result)) {
|
if (this.isGiveCreationError(result)) {
|
||||||
const errorMessage = getGiveCreationErrorMessage(result);
|
const errorMessage = this.getGiveCreationErrorMessage(result);
|
||||||
console.log("Error with give result:", result);
|
console.log("Error with give result:", result);
|
||||||
this.setAlert(
|
this.setAlert(
|
||||||
"Error",
|
"Error",
|
||||||
@@ -330,7 +344,8 @@ export default class HomeView extends Vue {
|
|||||||
console.log("Error with give caught:", error);
|
console.log("Error with give caught:", error);
|
||||||
this.setAlert(
|
this.setAlert(
|
||||||
"Error",
|
"Error",
|
||||||
getGiveErrorMessage(error) || "There was an error recording the give.",
|
this.getGiveErrorMessage(error) ||
|
||||||
|
"There was an error recording the give.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,38 +13,37 @@
|
|||||||
[New/Edit] Identity
|
[New/Edit] Identity
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<form>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="First Name"
|
|
||||||
class="block w-full rounded border border-slate-400 mb-4 px-3 py-2"
|
|
||||||
v-model="firstName"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="Last Name"
|
|
||||||
class="block w-full rounded border border-slate-400 mb-4 px-3 py-2"
|
|
||||||
v-model="lastName"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="mt-8">
|
<input
|
||||||
<button
|
type="text"
|
||||||
type="button"
|
placeholder="First Name"
|
||||||
class="block w-full text-center text-lg font-bold uppercase bg-blue-600 text-white px-2 py-3 rounded-md mb-2"
|
class="block w-full rounded border border-slate-400 mb-4 px-3 py-2"
|
||||||
@click="onClickSaveChanges()"
|
v-model="firstName"
|
||||||
>
|
/>
|
||||||
Save Changes
|
<input
|
||||||
</button>
|
type="text"
|
||||||
<!-- SHOW ME instead while processing saving changes -->
|
placeholder="Last Name"
|
||||||
<button
|
class="block w-full rounded border border-slate-400 mb-4 px-3 py-2"
|
||||||
type="button"
|
v-model="lastName"
|
||||||
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md"
|
/>
|
||||||
@click="onClickCancel()"
|
|
||||||
>
|
<div class="mt-8">
|
||||||
Cancel
|
<button
|
||||||
</button>
|
type="button"
|
||||||
</div>
|
class="block w-full text-center text-lg font-bold uppercase bg-blue-600 text-white px-2 py-3 rounded-md mb-2"
|
||||||
</form>
|
@click="onClickSaveChanges()"
|
||||||
|
>
|
||||||
|
Save Changes
|
||||||
|
</button>
|
||||||
|
<!-- SHOW ME instead while processing saving changes -->
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md"
|
||||||
|
@click="onClickCancel()"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -16,47 +16,44 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Project Details -->
|
<!-- Project Details -->
|
||||||
<form>
|
|
||||||
<select
|
<select class="block w-full rounded border border-slate-400 mb-4 px-3 py-2">
|
||||||
class="block w-full rounded border border-slate-400 mb-4 px-3 py-2"
|
<option disabled>Choose a commitment type…</option>
|
||||||
|
<option selected>Time</option>
|
||||||
|
<option>Cryptocurrency</option>
|
||||||
|
<option>Money</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- Time amount -->
|
||||||
|
<div class="mb-4 flex items-stretch">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="0.0"
|
||||||
|
class="block w-full rounded-l border border-slate-400 px-3 py-2"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
class="px-4 py-2 rounded-r bg-slate-200 border border-l-0 border-slate-400"
|
||||||
|
>hours</span
|
||||||
>
|
>
|
||||||
<option disabled>Choose a commitment type…</option>
|
</div>
|
||||||
<option selected>Time</option>
|
|
||||||
<option>Cryptocurrency</option>
|
|
||||||
<option>Money</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<!-- Time amount -->
|
<!-- Crypto amount -->
|
||||||
<div class="mb-4 flex items-stretch">
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
placeholder="0.0"
|
|
||||||
class="block w-full rounded-l border border-slate-400 px-3 py-2"
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
class="px-4 py-2 rounded-r bg-slate-200 border border-l-0 border-slate-400"
|
|
||||||
>hours</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Crypto amount -->
|
<!-- Money amount -->
|
||||||
|
|
||||||
<!-- Money amount -->
|
<div class="mt-8">
|
||||||
|
<input
|
||||||
<div class="mt-8">
|
type="submit"
|
||||||
<input
|
class="block w-full text-center text-lg font-bold uppercase bg-blue-600 text-white px-2 py-3 rounded-md mb-2"
|
||||||
type="submit"
|
value="Commit"
|
||||||
class="block w-full text-center text-lg font-bold uppercase bg-blue-600 text-white px-2 py-3 rounded-md mb-2"
|
/>
|
||||||
value="Commit"
|
<button
|
||||||
/>
|
type="button"
|
||||||
<button
|
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md"
|
||||||
type="button"
|
>
|
||||||
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md"
|
Maybe Later
|
||||||
>
|
</button>
|
||||||
Maybe Later
|
</div>
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ import * as didJwt from "did-jwt";
|
|||||||
import { Component, Vue } from "vue-facing-decorator";
|
import { Component, Vue } from "vue-facing-decorator";
|
||||||
|
|
||||||
import { accountsDB, db } from "@/db";
|
import { accountsDB, db } from "@/db";
|
||||||
import { AccountsSchema } from "@/db/tables/accounts";
|
|
||||||
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
||||||
import { accessToken, SimpleSigner } from "@/libs/crypto";
|
import { accessToken, SimpleSigner } from "@/libs/crypto";
|
||||||
import { useAppStore } from "@/store/app";
|
import { useAppStore } from "@/store/app";
|
||||||
@@ -92,15 +91,13 @@ export default class NewEditProjectView extends Vue {
|
|||||||
projectName = "";
|
projectName = "";
|
||||||
description = "";
|
description = "";
|
||||||
errorMessage = "";
|
errorMessage = "";
|
||||||
accounts: AccountsSchema;
|
|
||||||
numAccounts = 0;
|
numAccounts = 0;
|
||||||
alertTitle = "";
|
alertTitle = "";
|
||||||
alertMessage = "";
|
alertMessage = "";
|
||||||
|
|
||||||
async beforeCreate() {
|
async beforeCreate() {
|
||||||
accountsDB.open();
|
await accountsDB.open();
|
||||||
this.accounts = accountsDB.accounts;
|
this.numAccounts = await accountsDB.accounts.count();
|
||||||
this.numAccounts = await this.accounts.count();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getIdentity(activeDid) {
|
public async getIdentity(activeDid) {
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ export default class AccountViewView extends Vue {
|
|||||||
loading = true;
|
loading = true;
|
||||||
|
|
||||||
async mounted() {
|
async mounted() {
|
||||||
await accountsDB.open();
|
|
||||||
const mnemonic = generateSeed();
|
const mnemonic = generateSeed();
|
||||||
// address is 0x... ETH address, without "did:eth:"
|
// address is 0x... ETH address, without "did:eth:"
|
||||||
const [address, privateHex, publicHex, derivationPath] =
|
const [address, privateHex, publicHex, derivationPath] =
|
||||||
@@ -58,6 +57,8 @@ export default class AccountViewView extends Vue {
|
|||||||
|
|
||||||
const newId = newIdentifier(address, publicHex, privateHex, derivationPath);
|
const newId = newIdentifier(address, publicHex, privateHex, derivationPath);
|
||||||
const identity = JSON.stringify(newId);
|
const identity = JSON.stringify(newId);
|
||||||
|
|
||||||
|
await accountsDB.open();
|
||||||
await accountsDB.accounts.add({
|
await accountsDB.accounts.add({
|
||||||
dateCreated: new Date().toISOString(),
|
dateCreated: new Date().toISOString(),
|
||||||
derivationPath: derivationPath,
|
derivationPath: derivationPath,
|
||||||
|
|||||||
@@ -12,13 +12,6 @@
|
|||||||
>
|
>
|
||||||
<fa icon="chevron-left" class="fa-fw"></fa>
|
<fa icon="chevron-left" class="fa-fw"></fa>
|
||||||
</button>
|
</button>
|
||||||
<!-- Context Menu -->
|
|
||||||
<a
|
|
||||||
href=""
|
|
||||||
class="text-lg text-center px-2 py-1 absolute -right-2 -top-1"
|
|
||||||
><fa icon="ellipsis-vertical" class="fa-fw"></fa
|
|
||||||
></a>
|
|
||||||
|
|
||||||
View Plan
|
View Plan
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
@@ -56,6 +49,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
v-if="issuer == activeDid"
|
||||||
type="button"
|
type="button"
|
||||||
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md"
|
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md"
|
||||||
@click="onEditClick()"
|
@click="onEditClick()"
|
||||||
@@ -104,7 +98,7 @@
|
|||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<fa icon="user" class="fa-fw text-slate-400"></fa>
|
<fa icon="user" class="fa-fw text-slate-400"></fa>
|
||||||
<span>{{
|
<span>{{
|
||||||
didInfo(give.agentDid, activeDid, accounts, allContacts)
|
didInfo(give.agentDid, activeDid, allMyDids, allContacts)
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2" v-if="give.amount">
|
<div class="flex gap-2" v-if="give.amount">
|
||||||
@@ -126,7 +120,7 @@
|
|||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<fa icon="user" class="fa-fw text-slate-400"></fa>
|
<fa icon="user" class="fa-fw text-slate-400"></fa>
|
||||||
<span>{{
|
<span>{{
|
||||||
didInfo(give.agentDid, activeDid, accounts, allContacts)
|
didInfo(give.agentDid, activeDid, allMyDids, allContacts)
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2" v-if="give.amount">
|
<div class="flex gap-2" v-if="give.amount">
|
||||||
@@ -163,7 +157,6 @@ import { Component, Vue } from "vue-facing-decorator";
|
|||||||
|
|
||||||
import GiftedDialog from "@/components/GiftedDialog.vue";
|
import GiftedDialog from "@/components/GiftedDialog.vue";
|
||||||
import { accountsDB, db } from "@/db";
|
import { accountsDB, db } from "@/db";
|
||||||
import { AccountsSchema } from "@/db/tables/accounts";
|
|
||||||
import { Contact } from "@/db/tables/contacts";
|
import { Contact } from "@/db/tables/contacts";
|
||||||
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
||||||
import { accessToken } from "@/libs/crypto";
|
import { accessToken } from "@/libs/crypto";
|
||||||
@@ -179,10 +172,10 @@ import QuickNav from "@/components/QuickNav";
|
|||||||
components: { GiftedDialog, AlertMessage, QuickNav },
|
components: { GiftedDialog, AlertMessage, QuickNav },
|
||||||
})
|
})
|
||||||
export default class ProjectViewView extends Vue {
|
export default class ProjectViewView extends Vue {
|
||||||
accounts: AccountsSchema;
|
|
||||||
activeDid = "";
|
activeDid = "";
|
||||||
alertMessage = "";
|
alertMessage = "";
|
||||||
alertTitle = "";
|
alertTitle = "";
|
||||||
|
allMyDids: Array<string> = [];
|
||||||
allContacts: Array<Contact> = [];
|
allContacts: Array<Contact> = [];
|
||||||
apiServer = "";
|
apiServer = "";
|
||||||
description = "";
|
description = "";
|
||||||
@@ -191,18 +184,11 @@ export default class ProjectViewView extends Vue {
|
|||||||
givesByThis: Array<GiveServerRecord> = [];
|
givesByThis: Array<GiveServerRecord> = [];
|
||||||
name = "";
|
name = "";
|
||||||
issuer = "";
|
issuer = "";
|
||||||
numAccounts = 0;
|
|
||||||
projectId = localStorage.getItem("projectId") || ""; // handle ID
|
projectId = localStorage.getItem("projectId") || ""; // handle ID
|
||||||
timeSince = "";
|
timeSince = "";
|
||||||
truncatedDesc = "";
|
truncatedDesc = "";
|
||||||
truncateLength = 40;
|
truncateLength = 40;
|
||||||
|
|
||||||
async beforeCreate() {
|
|
||||||
accountsDB.open();
|
|
||||||
this.accounts = accountsDB.accounts;
|
|
||||||
this.numAccounts = (await this.accounts?.count()) || 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
async created() {
|
async created() {
|
||||||
await db.open();
|
await db.open();
|
||||||
const settings = await db.settings.get(MASTER_SETTINGS_KEY);
|
const settings = await db.settings.get(MASTER_SETTINGS_KEY);
|
||||||
@@ -210,9 +196,11 @@ export default class ProjectViewView extends Vue {
|
|||||||
this.apiServer = settings?.apiServer || "";
|
this.apiServer = settings?.apiServer || "";
|
||||||
this.allContacts = await db.contacts.toArray();
|
this.allContacts = await db.contacts.toArray();
|
||||||
|
|
||||||
this.accounts = accountsDB.accounts;
|
await accountsDB.open();
|
||||||
const accountsArr = await this.accounts?.toArray();
|
const accounts = accountsDB.accounts;
|
||||||
const account = accountsArr.find((acc) => acc.did === this.activeDid);
|
const accountsArr = await accounts?.toArray();
|
||||||
|
this.allMyDids = accountsArr.map((acc) => acc.did);
|
||||||
|
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);
|
this.LoadProject(identity);
|
||||||
}
|
}
|
||||||
@@ -251,8 +239,8 @@ export default class ProjectViewView extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Isn't there a better way to make this available to the template?
|
// Isn't there a better way to make this available to the template?
|
||||||
didInfo(did, activeDid, identities, contacts) {
|
didInfo(did, activeDid, dids, contacts) {
|
||||||
return didInfo(did, activeDid, identities, contacts);
|
return didInfo(did, activeDid, dids, contacts);
|
||||||
}
|
}
|
||||||
|
|
||||||
expandText() {
|
expandText() {
|
||||||
|
|||||||
@@ -7,18 +7,17 @@
|
|||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<!-- Quick Search -->
|
<!-- Quick Search -->
|
||||||
<form id="QuickSearch" class="mb-4 flex">
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search…"
|
placeholder="Search…"
|
||||||
class="block w-full rounded-l border border-r-0 border-slate-400 px-3 py-2"
|
class="block w-full rounded-l border border-r-0 border-slate-400 px-3 py-2"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
class="px-4 rounded-r bg-slate-200 border border-l-0 border-slate-400"
|
class="px-4 rounded-r bg-slate-200 border border-l-0 border-slate-400"
|
||||||
>
|
>
|
||||||
<fa icon="magnifying-glass" class="fa-fw"></fa>
|
<fa icon="magnifying-glass" class="fa-fw"></fa>
|
||||||
</button>
|
</button>
|
||||||
</form>
|
|
||||||
|
|
||||||
<!-- New Project -->
|
<!-- New Project -->
|
||||||
<button
|
<button
|
||||||
@@ -75,7 +74,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Vue } from "vue-facing-decorator";
|
import { Component, Vue } from "vue-facing-decorator";
|
||||||
import { accountsDB, db } from "@/db";
|
import { accountsDB, db } from "@/db";
|
||||||
import { AccountsSchema } from "@/db/tables/accounts";
|
|
||||||
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
||||||
import { accessToken } from "@/libs/crypto";
|
import { accessToken } from "@/libs/crypto";
|
||||||
import { IIdentifier } from "@veramo/core";
|
import { IIdentifier } from "@veramo/core";
|
||||||
@@ -93,13 +91,11 @@ export default class ProjectsView extends Vue {
|
|||||||
isLoading = false;
|
isLoading = false;
|
||||||
alertTitle = "";
|
alertTitle = "";
|
||||||
alertMessage = "";
|
alertMessage = "";
|
||||||
accounts: AccountsSchema;
|
|
||||||
numAccounts = 0;
|
numAccounts = 0;
|
||||||
|
|
||||||
async beforeCreate() {
|
async beforeCreate() {
|
||||||
accountsDB.open();
|
await accountsDB.open();
|
||||||
this.accounts = accountsDB.accounts;
|
this.numAccounts = await accountsDB.accounts.count();
|
||||||
this.numAccounts = await this.accounts.count();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user