Compare commits

..

28 Commits

Author SHA1 Message Date
f269f5fa77 Merge branch 'master' into no-accounts-in-memory 2023-07-18 06:41:44 -04:00
Matthew Raymer
e8eae544f3 Merge remote-tracking branch 'origin/no-accounts-in-memory' 2023-07-18 18:50:55 +08:00
Jose Olarte III
34636d6047 Fixed alert message visibility 2023-07-18 17:14:03 +08:00
91b46eaaee Update 'project.task.yaml' 2023-07-18 02:15:04 -04:00
31d1a449ae Update 'project.task.yaml' 2023-07-18 02:12:40 -04:00
1248132076 Update 'project.task.yaml' 2023-07-18 02:12:29 -04:00
015704c94e Merge pull request 'home-gifting-improvements' (#43) from home-gifting-improvements into master
Reviewed-on: trent_larson/kick-starter-for-time-pwa#43
2023-07-18 01:35:40 -04:00
540ef916c2 Merge pull request 'Remove form tags' (#44) from form-tag-cleanup into master
Reviewed-on: trent_larson/kick-starter-for-time-pwa#44
2023-07-18 01:35:22 -04:00
bee7c87a8f Merge branch 'master' into no-accounts-in-memory 2023-07-17 16:16:44 -04:00
6bbc88f86c avoid console errors when no identity exists, and add to tasks 2023-07-14 20:54:26 -06:00
624abbb179 correct an error with unseen internal methods 2023-07-14 20:53:41 -06:00
110ed009b2 remove unused reference, call out another verbiage fix to do 2023-07-14 20:30:40 -06:00
a5892238d5 on tasks: prioritize notifications, add fixes 2023-07-14 20:25:15 -06:00
8eb80a9ede remove unnecessary async (that also happens to break display) 2023-07-14 20:11:04 -06:00
Jose Olarte III
32125133f0 No-contacts state 2023-07-14 21:33:56 +08:00
Matthew Raymer
47ade49e31 Cleanup after some testing 2023-07-14 20:29:38 +08:00
Jose Olarte III
47ce91cca1 Implemented design of Contact Gifting List view 2023-07-14 19:42:13 +08:00
Jose Olarte III
3e52b504b0 Polished gifted dialog UI 2023-07-14 18:27:43 +08:00
Matthew Raymer
4ecea1ab0e Add a special page for seeing all contacts to gift 2023-07-14 18:12:39 +08:00
Matthew Raymer
b9fdc920ea Remove the old identity management. Update project tasks 2023-07-13 18:13:41 +08:00
Matthew Aaron Raymer
0907d59a6a Remove form tags 2023-07-13 16:06:09 +08:00
59ce15c744 Merge pull request 'Adding Identity Management stubs' (#42) from identity-switcher into master
Reviewed-on: trent_larson/kick-starter-for-time-pwa#42
2023-07-12 22:49:58 -04:00
Jose Olarte III
9960a96a20 Design tweaks to Latest Activity 2023-07-12 23:17:28 +08:00
Jose Olarte III
098c6c0fa0 Compacted Quick Action section 2023-07-12 22:52:58 +08:00
d7a9fb6d54 Merge branch 'master' into no-accounts-in-memory 2023-07-11 22:57:20 -04:00
cf2b80b1f5 Merge branch 'master' into no-accounts-in-memory 2023-07-11 21:28:51 -04:00
b86323ec83 adjust didInfo so we can use DIDs and not identities, removing last of identities in memory 2023-07-11 15:30:51 -06:00
8add6448fb remove code that keeps the private key (account) data in memory 2023-07-11 09:10:25 -06:00
18 changed files with 601 additions and 344 deletions

View File

@@ -1,23 +1,14 @@
tasks: tasks:
- 01 design ideas for simple gives on the Home page
- 02 Discover page - add infinite search
- 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:jose
- 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.
- 02 Fix images on projectview: allow choice of image from a pallete of images or a url image.
- 08 Scan QR code to import into contacts. - 08 Scan QR code to import into contacts.
- contacts v1 : - 40 notifications :
- 01 Import contact info a la QR code. - push, where we trigger a ServiceWorker(?) in the app to reach out and check for new data
- .2 move all "identity" references to temporary account access assignee:trent
- contacts v+ :
- 01 Import all the non-sensitive data (ie. contacts & settings).
- .2 show error to user when adding a duplicate contact
- 01 parse input more robustly (with CSV lib and not commas)
- refactor UI : - refactor UI :
- .5 Alerts show at the top and can be missed if you've scrolled down on the page, eg. account data download - .5 Alerts show at the top and can be missed if you've scrolled down on the page, eg. account data download
@@ -31,18 +22,22 @@ tasks:
- 01 save the feed-viewed status in settings storage ("afterQuery") - 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 - 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 - 24 Move to Vite
- 40 notifications : - .5 remove edit from project page for projects owned by others
- push - .5 fix where user 0 sees no txns from user 1 on contacts page but sees them on list page
- .2 there are three dots at the top of ProjectViewView that refreshes the page but doesn't do anything else
- 01 fix images on project page, on discovery 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?)
- .2 fix rate limit verbiage (with the new one-per-day allowance) assignee:trent
- Discuss whether the remaining tasks are worthwhile before MVP release. - Discuss whether the remaining tasks are worthwhile before MVP release.
- 01 fix images on project page, on discovery page - contacts v+ :
- .2 fix static icon to the right on project page (Matthew: I've made "Rotary" into issuer?) - 01 Import all the non-sensitive data (ie. contacts & settings).
- .2 show error to user when adding a duplicate contact
- 01 parse input more robustly (with CSV lib and not commas)
- stats v1 : - stats v1 :
- 01 show numeric stats - 01 show numeric stats
@@ -51,7 +46,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 +63,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 +91,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

View File

@@ -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,

View File

@@ -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?.name || "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> >
&nbsp; Sign &amp; 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>

View File

@@ -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 {

View File

@@ -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,

View File

@@ -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 {*} */

View File

@@ -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 });

View File

@@ -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>

View File

@@ -78,7 +78,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 +100,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) {

View 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>

View File

@@ -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>

View File

@@ -7,22 +7,40 @@
</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">&nbsp;or&nbsp;</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 || "(no name)" }}
</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&hellip;
</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"
>
(No contacts to show.)
</div> </div>
</div> </div>
@@ -33,29 +51,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&hellip; <fa icon="spinner" class="fa-spin-pulse"></fa> Loading&hellip;
</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 +89,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 +101,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 +111,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 +146,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 +245,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 +264,7 @@ export default class HomeView extends Vue {
didInfo( didInfo(
gaveRecipientId, gaveRecipientId,
this.activeDid, this.activeDid,
this.allAccounts, this.allMyDids,
this.allContacts, this.allContacts,
) )
: ""; : "";
@@ -316,8 +329,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 +343,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.",
); );
} }
} }

View File

@@ -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>

View File

@@ -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>

View File

@@ -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) {

View File

@@ -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,

View File

@@ -104,7 +104,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 +126,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 +163,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 +178,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 +190,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 +202,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 +245,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() {

View File

@@ -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();
} }
/** /**