Compare commits

..

14 Commits

Author SHA1 Message Date
be348461f1 Update 'project.task.yaml' 2023-07-19 22:07:02 -04:00
6e2c596030 proposed move to jdenticon 2023-07-19 22:06:30 -04:00
Matthew Raymer
c502869c5f Add button with gift icon for future dialog 2023-07-19 18:41:12 +08:00
Matthew Raymer
b7aacd63e6 Add and edit project tasks list 2023-07-19 18:26:59 +08:00
Matthew Raymer
5bc0e27b30 Use a DID instead of a name ... this may need some better design on the dialog @jose 2023-07-19 18:25:58 +08:00
Matthew Raymer
a4fe94f081 Add a back arrow 2023-07-19 18:25:03 +08:00
Matthew Raymer
8de95566df Cleaning up this page to switch to GiftedDialog 2023-07-19 18:23:55 +08:00
Matthew Raymer
97569697f6 * show DID if no name
* hide no contacts when there are no contacts
* replace contact property with giver (? can you have another contact give you something ?)
2023-07-19 18:22:35 +08:00
Matthew Raymer
b9ed9d748b Only project owner may see edit button of a project 2023-07-19 18:19:29 +08:00
Matthew Raymer
790d44db81 Remove the stub context menu causing vertical ellipsis 2023-07-19 16:30:56 +08:00
e2bf469dc1 set assignees on several tasks. 2023-07-19 02:26:44 -04:00
592ffacebc possible image uploader 2023-07-19 02:16:28 -04:00
b706e65598 Remove completed tasks. 2023-07-19 02:11:18 -04:00
Matthew Raymer
6e3066ae92 Stub update of project task list 2023-07-18 21:04:48 +08:00
24 changed files with 409 additions and 1275 deletions

40
package-lock.json generated
View File

@@ -33,13 +33,11 @@
"ethereum-cryptography": "^2.0.0", "ethereum-cryptography": "^2.0.0",
"ethereumjs-util": "^7.1.5", "ethereumjs-util": "^7.1.5",
"ethr-did-resolver": "^8.0.0", "ethr-did-resolver": "^8.0.0",
"jdenticon": "^3.2.0",
"js-generate-password": "^0.1.9", "js-generate-password": "^0.1.9",
"localstorage-slim": "^2.4.0", "localstorage-slim": "^2.4.0",
"luxon": "^3.3.0", "luxon": "^3.3.0",
"merkletreejs": "^0.3.10", "merkletreejs": "^0.3.10",
"moment": "^2.29.4", "moment": "^2.29.4",
"notiwind": "^2.0.2",
"papaparse": "^5.4.1", "papaparse": "^5.4.1",
"pina": "^0.20.2204228", "pina": "^0.20.2204228",
"pinia-plugin-persistedstate": "^3.1.0", "pinia-plugin-persistedstate": "^3.1.0",
@@ -12050,14 +12048,6 @@
"resolved": "https://registry.npmjs.org/canonicalize/-/canonicalize-2.0.0.tgz", "resolved": "https://registry.npmjs.org/canonicalize/-/canonicalize-2.0.0.tgz",
"integrity": "sha512-ulDEYPv7asdKvqahuAY35c1selLdzDwHqugK92hfkzvlDCwXRRelDkR+Er33md/PtnpqHemgkuDPanZ4fiYZ8w==" "integrity": "sha512-ulDEYPv7asdKvqahuAY35c1selLdzDwHqugK92hfkzvlDCwXRRelDkR+Er33md/PtnpqHemgkuDPanZ4fiYZ8w=="
}, },
"node_modules/canvas-renderer": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/canvas-renderer/-/canvas-renderer-2.2.1.tgz",
"integrity": "sha512-RrBgVL5qCEDIXpJ6NrzyRNoTnXxYarqm/cS/W6ERhUJts5UQtt/XPEosGN3rqUkZ4fjBArlnCbsISJ+KCFnIAg==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/case-sensitive-paths-webpack-plugin": { "node_modules/case-sensitive-paths-webpack-plugin": {
"version": "2.4.0", "version": "2.4.0",
"resolved": "https://registry.npmmirror.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", "resolved": "https://registry.npmmirror.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz",
@@ -17807,20 +17797,6 @@
"integrity": "sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==", "integrity": "sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==",
"dev": true "dev": true
}, },
"node_modules/jdenticon": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/jdenticon/-/jdenticon-3.2.0.tgz",
"integrity": "sha512-z6Iq3fTODUMSOiR2nNYrqigS6Y0GvdXfyQWrUby7htDHvX7GNEwaWR4hcaL+FmhEgBe08Xkup/BKxXQhDJByPA==",
"dependencies": {
"canvas-renderer": "~2.2.0"
},
"bin": {
"jdenticon": "bin/jdenticon.js"
},
"engines": {
"node": ">=6.4.0"
}
},
"node_modules/jest-environment-node": { "node_modules/jest-environment-node": {
"version": "29.5.0", "version": "29.5.0",
"resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.5.0.tgz", "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.5.0.tgz",
@@ -20952,11 +20928,6 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/mitt": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="
},
"node_modules/mkdirp": { "node_modules/mkdirp": {
"version": "0.5.6", "version": "0.5.6",
"resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.6.tgz", "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.6.tgz",
@@ -21304,17 +21275,6 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/notiwind": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/notiwind/-/notiwind-2.0.2.tgz",
"integrity": "sha512-wMCf+07E093d0Q78C5UHroT9GQHm4mIGerhg7dGLJ0GN6zONqKj8nTR3clkq/Y44On9k28/0DtDNwOX7FT5p/A==",
"dependencies": {
"mitt": "^3.0.1"
},
"peerDependencies": {
"vue": "^3.3.4"
}
},
"node_modules/npm-package-arg": { "node_modules/npm-package-arg": {
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-7.0.0.tgz", "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-7.0.0.tgz",

View File

@@ -33,13 +33,11 @@
"ethereum-cryptography": "^2.0.0", "ethereum-cryptography": "^2.0.0",
"ethereumjs-util": "^7.1.5", "ethereumjs-util": "^7.1.5",
"ethr-did-resolver": "^8.0.0", "ethr-did-resolver": "^8.0.0",
"jdenticon": "^3.2.0",
"js-generate-password": "^0.1.9", "js-generate-password": "^0.1.9",
"localstorage-slim": "^2.4.0", "localstorage-slim": "^2.4.0",
"luxon": "^3.3.0", "luxon": "^3.3.0",
"merkletreejs": "^0.3.10", "merkletreejs": "^0.3.10",
"moment": "^2.29.4", "moment": "^2.29.4",
"notiwind": "^2.0.2",
"papaparse": "^5.4.1", "papaparse": "^5.4.1",
"pina": "^0.20.2204228", "pina": "^0.20.2204228",
"pinia-plugin-persistedstate": "^3.1.0", "pinia-plugin-persistedstate": "^3.1.0",

View File

@@ -1,42 +1,33 @@
tasks: tasks:
- .2 bug - on contacts view, click on "to" & "from" and nothing happens
- 01 add a location for a project via map pin :
- add with a "location" field containing this: { "geo":{ "@type":"GeoCoordinates", "latitude":40.883944, "longitude":-111.884787 } }
- 04 search by a bounding box for local projects (see API by clicking on "Nearby")
- 01 Replace Gifted/Give in ContactsView with GiftedDialog assignee:jose
- 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.
- 40 notifications : - 40 notifications :
- push, where we trigger a ServiceWorker(?) in the app to reach out and check for new data - push, where we trigger a ServiceWorker(?) in the app to reach out and check for new data assignee:matthew
- refactor UI : - 01 add a location for a project via map pin
- .5 Alerts show at the top and can be missed if you've scrolled down on the page, eg. account data download - 04 search by a bounding box for local projects (see API by clicking on "Nearby")
- .2 Make alerts at the top more visible (because they're currently a similar color and sometimes aren't seen) - 01 Replace Gifted/Give in ContactsView with GiftedDialog assignee:matthew
- 02 Fix images on projectview - allow choice of image from a pallete of images or a url image (discovery page display also)
- SEE: https://github.com/dmester/jdenticon assignee:jose
- Show pop-up or some message confirming that settings & contacts download has been initiated/finished - 08 Scan QR code to import into contacts assignee:matthew
- SEE: https://github.com/gruhn/vue-qrcode-reader
- Ensure each action sent to the server has a confirmation - eg registration - Show pop-up or some message confirming that settings & contacts download has been initiated/finished assignee:matthew
- 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 : - Home Feed & Quick Give screen :
- 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
- SEE: https://github.com/konvajs/vue-konva
- 24 Move to Vite - 24 Move to Vite assignee:matthew
- .5 add link to further project / people when a project pays ahead
- .5 add project ID to the URL, to make a project publicly-accessible
- .5 remove edit from project page for projects owned by others
- .5 fix where user 0 sees no txns from user 1 on contacts page but sees them on list page - .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 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 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 - .2 fix rate limit verbiage (with the new one-per-day allowance) assignee:trent
- .2 move 'switch identity' to the advanced section
- .1 remove the logic to exclude beforeId in list of plans after server has commit 26b25af605e715600d4f12b6416ed9fd7142d164
- Discuss whether the remaining tasks are worthwhile before MVP release. - Discuss whether the remaining tasks are worthwhile before MVP release.

View File

@@ -1,132 +1,5 @@
<template> <template>
<router-view /> <router-view />
<NotificationGroup group="alert">
<div
class="fixed top-4 right-4 w-full max-w-sm flex flex-col items-start justify-end"
>
<Notification
v-slot="{ notifications, close }"
enter="transform ease-out duration-300 transition"
enter-from="translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-4"
enter-to="translate-y-0 opacity-100 sm:translate-x-0"
leave="transition ease-in duration-500"
leave-from="opacity-100"
leave-to="opacity-0"
move="transition duration-500"
move-delay="delay-300"
>
<div
v-for="notification in notifications"
:key="notification.id"
class="w-full"
role="alert"
>
<div
v-if="notification.type === 'toast'"
class="w-full max-w-sm mx-auto mb-3 overflow-hidden bg-slate-900/90 text-white rounded-lg shadow-md"
>
<div class="w-full px-4 py-3">
<span class="font-semibold">{{ notification.title }}</span>
<p class="text-sm">{{ notification.text }}</p>
</div>
</div>
<div
v-if="notification.type === 'info'"
class="flex w-full max-w-sm mx-auto mb-3 overflow-hidden bg-slate-100 rounded-lg shadow-md"
>
<div
class="flex items-center justify-center w-12 bg-slate-600 text-slate-100"
>
<fa icon="circle-info" class="fa-fw fa-xl"></fa>
</div>
<div class="relative w-full pl-4 pr-8 py-2 text-slate-900">
<span class="font-semibold">{{ notification.title }}</span>
<p class="text-sm">{{ notification.text }}</p>
<button
@click="close(notification.id)"
class="absolute top-2 right-2 px-0.5 py-0 rounded-full bg-slate-200 text-slate-600"
>
<fa icon="xmark" class="fa-fw"></fa>
</button>
</div>
</div>
<div
v-if="notification.type === 'success'"
class="flex w-full max-w-sm mx-auto mb-3 overflow-hidden bg-emerald-100 rounded-lg shadow-md"
>
<div
class="flex items-center justify-center w-12 bg-emerald-600 text-emerald-100"
>
<fa icon="circle-info" class="fa-fw fa-xl"></fa>
</div>
<div class="relative w-full pl-4 pr-8 py-2 text-emerald-900">
<span class="font-semibold">{{ notification.title }}</span>
<p class="text-sm">{{ notification.text }}</p>
<button
@click="close(notification.id)"
class="absolute top-2 right-2 px-0.5 py-0 rounded-full bg-emerald-200 text-emerald-600"
>
<fa icon="xmark" class="fa-fw"></fa>
</button>
</div>
</div>
<div
v-if="notification.type === 'warning'"
class="flex w-full max-w-sm mx-auto mb-3 overflow-hidden bg-amber-100 rounded-lg shadow-md"
>
<div
class="flex items-center justify-center w-12 bg-amber-600 text-amber-100"
>
<fa icon="triangle-exclamation" class="fa-fw fa-xl"></fa>
</div>
<div class="relative w-full pl-4 pr-8 py-2 text-amber-900">
<span class="font-semibold">{{ notification.title }}</span>
<p class="text-sm">{{ notification.text }}</p>
<button
@click="close(notification.id)"
class="absolute top-2 right-2 px-0.5 py-0 rounded-full bg-amber-200 text-amber-600"
>
<fa icon="xmark" class="fa-fw"></fa>
</button>
</div>
</div>
<div
v-if="notification.type === 'danger'"
class="flex w-full max-w-sm mx-auto mb-3 overflow-hidden bg-rose-100 rounded-lg shadow-md"
>
<div
class="flex items-center justify-center w-12 bg-rose-600 text-rose-100"
>
<fa icon="triangle-exclamation" class="fa-fw fa-xl"></fa>
</div>
<div class="relative w-full pl-4 pr-8 py-2 text-rose-900">
<span class="font-semibold">{{ notification.title }}</span>
<p class="text-sm">{{ notification.text }}</p>
<button
@click="close(notification.id)"
class="absolute top-2 right-2 px-0.5 py-0 rounded-full bg-rose-200 text-rose-600"
>
<fa icon="xmark" class="fa-fw"></fa>
</button>
</div>
</div>
</div>
</Notification>
</div>
</NotificationGroup>
</template> </template>
<style></style> <style></style>

View File

@@ -1,19 +0,0 @@
<template>
<div v-html="generateIdenticon()" class="w-fit"></div>
</template>
<script lang="ts">
import { Vue, Component, Prop } from "vue-facing-decorator";
import { toSvg } from "jdenticon";
@Component
export default class EntityIcon extends Vue {
@Prop entityId = "";
@Prop iconSize = "";
generateIdenticon() {
const svgString = toSvg(this.entityId, this.iconSize);
return svgString;
}
}
</script>
<style scoped></style>

View File

@@ -2,7 +2,7 @@
<div v-if="visible" class="dialog-overlay"> <div v-if="visible" class="dialog-overlay">
<div class="dialog"> <div class="dialog">
<h1 class="text-xl font-bold text-center mb-4"> <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"

View File

@@ -4,7 +4,7 @@
export enum AppString { export enum AppString {
APP_NAME = "Kick-Start with Time", APP_NAME = "Kick-Start with Time",
PROD_ENDORSER_API_SERVER = "https://api.endorser.ch", PROD_ENDORSER_API_SERVER = "https://endorser.ch:3000",
TEST_ENDORSER_API_SERVER = "https://test.endorser.ch:8000", TEST_ENDORSER_API_SERVER = "https://test.endorser.ch:8000",
LOCAL_ENDORSER_API_SERVER = "http://localhost:3000", LOCAL_ENDORSER_API_SERVER = "http://localhost:3000",

View File

@@ -82,15 +82,10 @@ 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( export function didInfo(did, activeDid, allMyDids, contacts) {
did: string, const myId: string | undefined = R.find(R.identity, allMyDids);
activeDid: string,
allMyDids: Array<string>,
contacts: Array<Contact>,
): string {
const myId: string | undefined = R.find(R.equals(did), allMyDids, did);
if (myId) { if (myId) {
return "You" + (myId !== activeDid ? " (Alt ID)" : ""); return "You" + (myId.did !== activeDid ? " (Alt ID)" : "");
} else { } else {
const contact: Contact | undefined = R.find((c) => c.did === did, contacts); const contact: Contact | undefined = R.find((c) => c.did === did, contacts);
if (contact) { if (contact) {
@@ -106,7 +101,7 @@ export function didInfo(
} }
/** /**
* For result, see https://api.endorser.ch/api-docs/#/claims/post_api_v2_claim * For result, see https://endorser.ch:3000/api-docs/#/claims/post_api_v2_claim
* *
* @param identity * @param identity
* @param fromDid may be null * @param fromDid may be null

View File

@@ -5,7 +5,6 @@ import "./registerServiceWorker";
import router from "./router"; import router from "./router";
import axios from "axios"; import axios from "axios";
import VueAxios from "vue-axios"; import VueAxios from "vue-axios";
import Notifications from "notiwind";
import "./assets/styles/tailwind.css"; import "./assets/styles/tailwind.css";
@@ -17,7 +16,6 @@ import {
faChevronRight, faChevronRight,
faCircle, faCircle,
faCircleCheck, faCircleCheck,
faCircleInfo,
faCircleQuestion, faCircleQuestion,
faCircleUser, faCircleUser,
faClock, faClock,
@@ -47,7 +45,6 @@ import {
faSquareCaretDown, faSquareCaretDown,
faSquareCaretUp, faSquareCaretUp,
faTrashCan, faTrashCan,
faTriangleExclamation,
faUser, faUser,
faUsers, faUsers,
faXmark, faXmark,
@@ -60,7 +57,6 @@ library.add(
faChevronRight, faChevronRight,
faCircle, faCircle,
faCircleCheck, faCircleCheck,
faCircleInfo,
faCircleQuestion, faCircleQuestion,
faCircleUser, faCircleUser,
faClock, faClock,
@@ -90,7 +86,6 @@ library.add(
faSquareCaretDown, faSquareCaretDown,
faSquareCaretUp, faSquareCaretUp,
faTrashCan, faTrashCan,
faTriangleExclamation,
faUser, faUser,
faUsers, faUsers,
faXmark, faXmark,
@@ -103,5 +98,4 @@ createApp(App)
.use(createPinia()) .use(createPinia())
.use(VueAxios, axios) .use(VueAxios, axios)
.use(router) .use(router)
.use(Notifications)
.mount("#app"); .mount("#app");

View File

@@ -182,14 +182,4 @@ const router = createRouter({
routes, routes,
}); });
const errorHandler = (error, to, from) => {
// Handle the error here
console.error(error, to, from);
console.log("XXXXX");
// You can also perform additional actions, such as displaying an error message or redirecting the user to a specific page
};
router.onError(errorHandler); // Assign the error handler to the router instance
export default router; export default router;

View File

@@ -400,19 +400,13 @@ export default class AccountViewView extends Vue {
this.limitsMessage = "No identity."; this.limitsMessage = "No identity.";
this.loadingLimits = false; this.loadingLimits = false;
} else { } else {
this.$notify( this.alertMessage =
{ "Clear your cache and start over (after data backup).";
group: "alert",
type: "danger",
title: "Error Creating Account",
text: "Clear your cache and start over (after data backup).",
},
-1,
);
console.error( console.error(
"Telling user to clear cache at page create because:", "Telling user to clear cache at page create because:",
err, err,
); );
this.alertTitle = "Error Creating Account";
} }
} }
} }
@@ -424,19 +418,13 @@ export default class AccountViewView extends Vue {
showContactGivesInline: this.showContactGives, showContactGivesInline: this.showContactGives,
}); });
} catch (err) { } catch (err) {
this.$notify( this.alertMessage =
{ "Clear your cache and start over (after data backup).";
group: "alert",
type: "danger",
title: "Error Updating Contact Setting",
text: "Clear your cache and start over (after data backup).",
},
-1,
);
console.error( console.error(
"Telling user to clear cache after contact setting update because:", "Telling user to clear cache after contact setting update because:",
err, err,
); );
this.alertTitle = "Error Updating Contact Setting";
} }
} }
@@ -452,25 +440,11 @@ export default class AccountViewView extends Vue {
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
this.$notify( this.alertTitle = "Download Started";
{ this.alertMessage = "See your downloads directory for the backup.";
group: "alert",
type: "toast",
title: "Download Started",
text: "See your downloads directory for the backup.",
},
5000,
);
} catch (error) { } catch (error) {
this.$notify( this.alertTitle = "Export Error";
{ this.alertMessage = "See console logs for more info.";
group: "alert",
type: "danger",
title: "Export Error",
text: "See console logs for more info.",
},
-1,
);
console.error("Export Error:", error); console.error("Export Error:", error);
} }
} }

View File

@@ -1,14 +1,21 @@
<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>
<div class="flex justify-around">
<span />
<span class="justify-around">(Only 50 most recent)</span>
<span />
</div>
<!-- Results List --> <!-- Results List -->
<div> <div>
@@ -151,17 +158,10 @@ export default class ContactsView extends Vue {
this.loadGives(this.activeDid, this.contact); this.loadGives(this.activeDid, this.contact);
} }
} catch (err) { } catch (err) {
this.$notify( this.alertTitle = "Error";
{ this.alertMessage =
group: "alert", err.userMessage ||
type: "danger", "There was an error retrieving the latest sweet, sweet action.";
title: "Error",
text:
err.userMessage ||
"There was an error retrieving the latest sweet, sweet action.",
},
-1,
);
} }
} }
@@ -175,7 +175,7 @@ export default class ContactsView extends Vue {
encodeURIComponent(identity.did) + encodeURIComponent(identity.did) +
"&recipientDid=" + "&recipientDid=" +
encodeURIComponent(contact.did); encodeURIComponent(contact.did);
const headers = await this.getHeaders(identity); const headers = this.getHeaders(identity);
const resp = await this.axios.get(url, { headers }); const resp = await this.axios.get(url, { headers });
if (resp.status === 200) { if (resp.status === 200) {
result = resp.data.data; result = resp.data.data;
@@ -185,15 +185,9 @@ export default class ContactsView extends Vue {
resp.status, resp.status,
resp.data, resp.data,
); );
this.$notify( this.alertTitle = "Error With Server";
{ this.alertMessage =
group: "alert", "Got an error retrieving your given time from the server.";
type: "danger",
title: "Error With Server",
text: "Got an error retrieving your given time from the server.",
},
-1,
);
} }
const url2 = const url2 =
@@ -212,15 +206,9 @@ export default class ContactsView extends Vue {
resp2.status, resp2.status,
resp2.data, resp2.data,
); );
this.$notify( this.alertTitle = "Error With Server";
{ this.alertMessage =
group: "alert", "Got an error retrieving your given time from the server.";
type: "danger",
title: "Error With Server",
text: "Got an error retrieving your given time from the server.",
},
-1,
);
} }
const sortedResult: Array<GiveServerRecord> = R.sort( const sortedResult: Array<GiveServerRecord> = R.sort(
@@ -230,15 +218,8 @@ export default class ContactsView extends Vue {
); );
this.giveRecords = sortedResult; this.giveRecords = sortedResult;
} catch (error) { } catch (error) {
this.$notify( this.alertTitle = "Error With Server";
{ this.alertMessage = error as string;
group: "alert",
type: "danger",
title: "Error With Server",
text: error as string,
},
-1,
);
} }
} }
@@ -307,29 +288,15 @@ export default class ContactsView extends Vue {
userMessage = error as string; userMessage = error as string;
} }
// Now set that error for the user to see. // Now set that error for the user to see.
this.$notify( this.alertTitle = "Error With Server";
{ this.alertMessage = userMessage;
group: "alert",
type: "danger",
title: "Error With Server",
text: userMessage,
},
-1,
);
} }
} }
} }
cannotConfirmMessage() { cannotConfirmMessage() {
this.$notify( this.alertTitle = "Not Allowed";
{ this.alertMessage = "Only the recipient can confirm final receipt.";
group: "alert",
type: "danger",
title: "Not Allowed",
text: "Only the recipient can confirm final receipt.",
},
-1,
);
} }
} }
</script> </script>

View File

@@ -24,12 +24,8 @@
<ul class="border-t border-slate-300"> <ul class="border-t border-slate-300">
<li class="border-b border-slate-300 py-3"> <li class="border-b border-slate-300 py-3">
<h2 class="text-base flex gap-4 items-center"> <h2 class="text-base flex gap-4 items-center">
<span class="grow italic text-slate-500" <span class="grow italic"
><EntityIcon ><fa icon="question-circle" class="fa-fw fa-xl text-slate-400"></fa>
entityId="Anonymous"
:iconSize="32"
class="opacity-50 inline-block align-middle border border-dashed border-slate-400 bg-slate-200 rounded-md mr-1"
></EntityIcon>
Anonymous Anonymous
</span> </span>
<span class="text-right"> <span class="text-right">
@@ -50,11 +46,7 @@
> >
<h2 class="text-base flex gap-4 items-center"> <h2 class="text-base flex gap-4 items-center">
<span class="grow font-semibold" <span class="grow font-semibold"
><EntityIcon ><fa icon="user" class="fa-fw fa-xl text-slate-400"></fa>
:entityId="contact.did"
:iconSize="32"
class="inline-block align-middle border border-slate-300 rounded-md mr-1"
></EntityIcon>
{{ contact.name || "(no name)" }} {{ contact.name || "(no name)" }}
</span> </span>
<span class="text-right"> <span class="text-right">
@@ -95,10 +87,9 @@ 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";
import EntityIcon from "@/components/EntityIcon";
@Component({ @Component({
components: { GiftedDialog, AlertMessage, QuickNav, EntityIcon }, components: { GiftedDialog, AlertMessage, QuickNav },
}) })
export default class HomeView extends Vue { export default class HomeView extends Vue {
activeDid = ""; activeDid = "";
@@ -154,17 +145,10 @@ export default class HomeView extends Vue {
this.feedLastViewedId = settings?.lastViewedClaimId; this.feedLastViewedId = settings?.lastViewedClaimId;
this.updateAllFeed(); this.updateAllFeed();
} catch (err) { } catch (err) {
this.$notify( this.alertTitle = "Error";
{ this.alertMessage =
group: "alert", err.userMessage ||
type: "danger", "There was an error retrieving the latest sweet, sweet action.";
title: "Error",
text:
err.userMessage ||
"There was an error retrieving the latest sweet, sweet action.",
},
-1,
);
} }
} }
@@ -213,27 +197,17 @@ export default class HomeView extends Vue {
*/ */
public async recordGive(giverDid, description, hours) { public async recordGive(giverDid, description, hours) {
if (!this.activeDid) { if (!this.activeDid) {
this.$notify( this.setAlert(
{ "Error",
group: "alert", "You must select an identity before you can record a give.",
type: "danger",
title: "Error",
text: "You must select an identity before you can record a give.",
},
-1,
); );
return; return;
} }
if (!description && !hours) { if (!description && !hours) {
this.$notify( this.setAlert(
{ "Error",
group: "alert", "You must enter a description or some number of hours.",
type: "danger",
title: "Error",
text: "You must enter a description or some number of hours.",
},
-1,
); );
return; return;
} }
@@ -253,38 +227,18 @@ export default class HomeView extends Vue {
if (isGiveCreationError(result)) { if (isGiveCreationError(result)) {
const errorMessage = getGiveCreationErrorMessage(result); const errorMessage = getGiveCreationErrorMessage(result);
console.log("Error with give result:", result); console.log("Error with give result:", result);
this.$notify( this.setAlert(
{ "Error",
group: "alert", errorMessage || "There was an error recording the give.",
type: "danger",
title: "Error",
text: errorMessage || "There was an error recording the give.",
},
-1,
); );
} else { } else {
this.$notify( this.setAlert("Success", "That gift was recorded.");
{
group: "alert",
type: "success",
title: "Success",
text: "That gift was recorded.",
},
-1,
);
} }
} catch (error) { } catch (error) {
console.log("Error with give caught:", error); console.log("Error with give caught:", error);
this.$notify( this.setAlert(
{ "Error",
group: "alert", getGiveErrorMessage(error) || "There was an error recording the give.",
type: "danger",
title: "Error",
text:
getGiveErrorMessage(error) ||
"There was an error recording the give.",
},
-1,
); );
} }
} }

View File

@@ -85,15 +85,7 @@ export default class ContactQRScanShow extends Vue {
const accounts = await accountsDB.accounts.toArray(); const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === this.activeDid, accounts); const account = R.find((acc) => acc.did === this.activeDid, accounts);
if (!account) { if (!account) {
this.$notify( this.alertMessage = "You have no identity yet.";
{
group: "alert",
type: "warning",
title: "",
text: "You have no identity yet.",
},
-1,
);
} else { } else {
const identity = await this.getIdentity(this.activeDid); const identity = await this.getIdentity(this.activeDid);
const publicKeyHex = identity.keys[0].publicKeyHex; const publicKeyHex = identity.keys[0].publicKeyHex;

View File

@@ -66,27 +66,18 @@
: "Unconfirmed" : "Unconfirmed"
}} }}
</button> </button>
<br />
(Only hours shown)
<br />
(Only recent shown)
</div> </div>
</div> </div>
<!-- Results List --> <!-- Results List -->
<ul v-if="contacts.length > 0" class="border-t border-slate-300"> <ul v-if="contacts.length > 0">
<li <li
class="border-b border-slate-300 pt-2.5 pb-4" class="border-b border-slate-300"
v-for="contact in contacts" v-for="contact in contacts"
:key="contact.did" :key="contact.did"
> >
<div class="grow overflow-hidden"> <div class="grow overflow-hidden">
<h2 class="text-base font-semibold"> <h2 class="text-base font-semibold">
<EntityIcon
:entityId="contact.did"
:iconSize="24"
class="inline-block align-text-bottom border border-slate-300 rounded"
></EntityIcon>
{{ contact.name || "(no name)" }} {{ contact.name || "(no name)" }}
</h2> </h2>
<div class="text-sm truncate">{{ contact.did }}</div> <div class="text-sm truncate">{{ contact.did }}</div>
@@ -94,85 +85,64 @@
Public Key (base 64): {{ contact.publicKeyBase64 }} Public Key (base 64): {{ contact.publicKeyBase64 }}
</div> </div>
<div id="ContactActions" class="flex gap-1.5 mt-2"> <button
<button v-if="contact.seesMe"
v-if="contact.seesMe" class="tooltip"
class="text-sm uppercase bg-slate-500 text-white px-2 py-1.5 rounded-md" @click="setVisibility(contact, false)"
@click="setVisibility(contact, false)" >
title="They can see you" <fa icon="eye" class="text-slate-900 fa-fw ml-1" />
> <span class="tooltiptext">They can see you</span>
<fa icon="eye" class="fa-fw" /> </button>
</button> <button v-else class="tooltip" @click="setVisibility(contact, true)">
<button <span class="tooltiptext">They cannot see you</span>
v-else <fa icon="eye-slash" class="text-slate-900 fa-fw ml-1" />
class="text-sm uppercase bg-slate-500 text-white px-2 py-1.5 rounded-md" </button>
@click="setVisibility(contact, true)"
title="They cannot see you"
>
<fa icon="eye-slash" class="fa-fw" />
</button>
<button <button class="tooltip" @click="checkVisibility(contact)">
class="text-sm uppercase bg-slate-500 text-white px-2 py-1.5 rounded-md" <span class="tooltiptext">Check Visibility</span>
@click="checkVisibility(contact)" <fa icon="rotate" class="text-slate-900 fa-fw ml-1" />
title="Check Visibility" </button>
>
<fa icon="rotate" class="fa-fw" />
</button>
<button <button v-if="contact.registered" class="tooltip">
v-if="contact.registered" <span class="tooltiptext">Registered</span>
class="text-sm uppercase bg-slate-500 text-white px-2 py-1.5 rounded-md" <fa icon="person-circle-check" class="text-slate-900 fa-fw ml-1" />
title="Registered" </button>
> <button v-else @click="register(contact)" class="tooltip">
<fa icon="person-circle-check" class="fa-fw" /> <span class="tooltiptext">Registration Unknown</span>
</button> <fa
<button icon="person-circle-question"
v-else class="text-slate-900 fa-fw ml-1"
@click="register(contact)" />
class="text-sm uppercase bg-slate-500 text-white px-2 py-1.5 rounded-md" </button>
title="Registration unknown"
>
<fa icon="person-circle-question" class="fa-fw" />
</button>
<button <button @click="deleteContact(contact)" class="px-9 tooltip">
@click="deleteContact(contact)" <span class="tooltiptext">Delete!</span>
class="text-sm uppercase bg-red-600 text-white px-2 py-1.5 rounded-md" <fa icon="trash-can" class="text-red-600 fa-fw ml-1" />
title="Delete" </button>
>
<fa icon="trash-can" class="fa-fw" />
</button>
<div <div v-if="showGiveNumbers" class="float-right">
v-if="showGiveNumbers && contact.did != activeDid" <div class="float-right">
class="ml-auto flex gap-1.5" <div class="tooltip">
> to:
<button
class="text-sm bg-blue-600 text-white px-2 py-1.5 rounded-l-md"
@click="onClickAddGive(activeDid, contact.did)"
title="givenByMeDescriptions[contact.did]"
>
To:
{{ {{
/* eslint-disable prettier/prettier */ /* eslint-disable prettier/prettier */
this.showGiveTotals this.showGiveTotals
? ((givenByMeConfirmed[contact.did] || 0) ? ((givenByMeConfirmed[contact.did] || 0)
+ (givenByMeUnconfirmed[contact.did] || 0)) + (givenByMeUnconfirmed[contact.did] || 0))
: this.showGiveConfirmed : this.showGiveConfirmed
? (givenByMeConfirmed[contact.did] || 0) ? (givenByMeConfirmed[contact.did] || 0)
: (givenByMeUnconfirmed[contact.did] || 0) : (givenByMeUnconfirmed[contact.did] || 0)
/* eslint-enable prettier/prettier */ /* eslint-enable prettier/prettier */
}} }}
<fa icon="plus" /> <span
</button> v-if="givenByMeDescriptions[contact.did]"
class="tooltiptext-left"
<button >
class="text-sm bg-blue-600 text-white px-2 py-1.5 rounded-r-md -ml-1.5 border-l border-blue-400" {{ givenByMeDescriptions[contact.did] }}
@click="onClickAddGive(contact.did, activeDid)" </span>
title="givenToMeDescriptions[contact.did]" </div>
> <div class="tooltip px-2">
From: from:
{{ {{
/* eslint-disable prettier/prettier */ /* eslint-disable prettier/prettier */
this.showGiveTotals this.showGiveTotals
@@ -183,18 +153,25 @@
: (givenToMeUnconfirmed[contact.did] || 0) : (givenToMeUnconfirmed[contact.did] || 0)
/* eslint-enable prettier/prettier */ /* eslint-enable prettier/prettier */
}} }}
<fa icon="plus" /> <span
</button> v-if="givenToMeDescriptions[contact.did]"
class="tooltiptext-left"
>
{{ givenToMeDescriptions[contact.did] }}
</span>
</div>
<router-link <router-link
:to="{ :to="{
name: 'contact-amounts', name: 'contact-amounts',
query: { contactDid: contact.did }, query: { contactDid: contact.did },
}" }"
class="text-sm uppercase bg-slate-500 text-white px-2 py-1.5 rounded-md" class="tooltip"
title="See all given activity"
> >
<fa icon="file-lines" class="fa-fw" /> <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" />
<span class="tooltiptext-left">See All Given Activity</span>
</router-link> </router-link>
</div> </div>
</div> </div>
@@ -226,13 +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 EntityIcon from "@/components/EntityIcon"; 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, EntityIcon }, components: { AlertMessage, QuickNav, GiftedDialog },
}) })
export default class ContactsView extends Vue { export default class ContactsView extends Vue {
activeDid = ""; activeDid = "";
@@ -307,27 +284,20 @@ export default class ContactsView extends Vue {
} }
async loadGives() { async loadGives() {
const handleResponse = ( const handleResponse = (resp, descriptions, confirmed, unconfirmed) => {
resp,
descriptions,
confirmed,
unconfirmed,
useRecipient,
) => {
if (resp.status === 200) { if (resp.status === 200) {
const allData = resp.data.data; const allData = resp.data.data;
for (const give of allData) { for (const give of allData) {
const otherDid = useRecipient ? give.recipientDid : give.agentDid;
if (give.unit === "HUR") { if (give.unit === "HUR") {
if (give.amountConfirmed) { if (give.amountConfirmed) {
const prevAmount = confirmed[otherDid] || 0; const prevAmount = confirmed[give.agentDid] || 0;
confirmed[otherDid] = prevAmount + give.amount; confirmed[give.agentDid] = prevAmount + give.amount;
} else { } else {
const prevAmount = unconfirmed[otherDid] || 0; const prevAmount = unconfirmed[give.agentDid] || 0;
unconfirmed[otherDid] = prevAmount + give.amount; unconfirmed[give.agentDid] = prevAmount + give.amount;
} }
if (!descriptions[otherDid] && give.description) { if (!descriptions[give.agentDid] && give.description) {
descriptions[otherDid] = give.description; descriptions[give.agentDid] = give.description;
} }
} }
} }
@@ -337,32 +307,27 @@ export default class ContactsView extends Vue {
resp.status, resp.status,
resp.data, resp.data,
); );
this.$notify( this.alertTitle = "Error With Server";
{ this.alertMessage =
group: "alert", "Got an error retrieving your " +
type: "danger", resp.config.url.includes("recipientDid")
title: "Error With Server", ? "received"
text: : "given" + " time from the server.";
"Got an error retrieving your " +
resp.config.url.includes("recipientDid")
? "received"
: "given" + " time from the server.",
},
-1,
);
} }
}; };
try { try {
const { headers } = await this.getHeadersAndIdentity(this.activeDid); const { headers, identity } = await this.getHeadersAndIdentity(
this.activeDid,
);
const givenByUrl = const givenByUrl =
this.apiServer + this.apiServer +
"/api/v2/report/gives?agentDid=" + "/api/v2/report/gives?agentDid=" +
encodeURIComponent(this.activeDid); encodeURIComponent(identity.did);
const givenToUrl = const givenToUrl =
this.apiServer + this.apiServer +
"/api/v2/report/gives?recipientDid=" + "/api/v2/report/gives?recipientDid=" +
encodeURIComponent(this.activeDid); encodeURIComponent(identity.did);
const [givenByMeResp, givenToMeResp] = await Promise.all([ const [givenByMeResp, givenToMeResp] = await Promise.all([
this.axios.get(givenByUrl, { headers }), this.axios.get(givenByUrl, { headers }),
@@ -377,7 +342,6 @@ export default class ContactsView extends Vue {
givenByMeDescriptions, givenByMeDescriptions,
givenByMeConfirmed, givenByMeConfirmed,
givenByMeUnconfirmed, givenByMeUnconfirmed,
true,
); );
this.givenByMeDescriptions = givenByMeDescriptions; this.givenByMeDescriptions = givenByMeDescriptions;
this.givenByMeConfirmed = givenByMeConfirmed; this.givenByMeConfirmed = givenByMeConfirmed;
@@ -391,21 +355,13 @@ export default class ContactsView extends Vue {
givenToMeDescriptions, givenToMeDescriptions,
givenToMeConfirmed, givenToMeConfirmed,
givenToMeUnconfirmed, givenToMeUnconfirmed,
false,
); );
this.givenToMeDescriptions = givenToMeDescriptions; this.givenToMeDescriptions = givenToMeDescriptions;
this.givenToMeConfirmed = givenToMeConfirmed; this.givenToMeConfirmed = givenToMeConfirmed;
this.givenToMeUnconfirmed = givenToMeUnconfirmed; this.givenToMeUnconfirmed = givenToMeUnconfirmed;
} catch (error) { } catch (error) {
this.$notify( this.alertTitle = "Error With Server";
{ this.alertMessage = error as string;
group: "alert",
type: "danger",
title: "Error With Server",
text: error as string,
},
-1,
);
} }
} }
@@ -498,32 +454,18 @@ export default class ContactsView extends Vue {
try { try {
const resp = await this.axios.post(url, payload, { headers }); const resp = await this.axios.post(url, payload, { headers });
if (resp.data?.success?.embeddedRecordError) { if (resp.data?.success?.embeddedRecordError) {
this.alertTitle = "Registration Still Unknown";
let message = "There was some problem with the registration."; let message = "There was some problem with the registration.";
if (typeof resp.data.success.embeddedRecordError == "string") { if (typeof resp.data.success.embeddedRecordError == "string") {
message += " " + resp.data.success.embeddedRecordError; message += " " + resp.data.success.embeddedRecordError;
} }
this.$notify( this.alertMessage = message;
{
group: "alert",
type: "danger",
title: "Registration Still Unknown",
text: message,
},
-1,
);
} else if (resp.data?.success?.handleId) { } else if (resp.data?.success?.handleId) {
contact.registered = true; contact.registered = true;
db.contacts.update(contact.did, { registered: true }); db.contacts.update(contact.did, { registered: true });
this.$notify( this.alertTitle = "Registration Success";
{ this.alertMessage = contact.name + " has been registered.";
group: "alert",
type: "info",
title: "Registration Success",
text: contact.name + " has been registered.",
},
-1,
);
} }
} catch (error) { } catch (error) {
let userMessage = "There was an error. See logs for more info."; let userMessage = "There was an error. See logs for more info.";
@@ -538,15 +480,8 @@ export default class ContactsView extends Vue {
userMessage = error as string; userMessage = error as string;
} }
// Now set that error for the user to see. // Now set that error for the user to see.
this.$notify( this.alertTitle = "Error With Server";
{ this.alertMessage = userMessage;
group: "alert",
type: "danger",
title: "Error With Server",
text: userMessage,
},
-1,
);
} }
} }
} }
@@ -567,39 +502,17 @@ export default class ContactsView extends Vue {
contact.seesMe = visibility; contact.seesMe = visibility;
db.contacts.update(contact.did, { seesMe: visibility }); db.contacts.update(contact.did, { seesMe: visibility });
} else { } else {
this.alertTitle = "Error With Server";
console.error("Bad response setting visibility: ", resp.data); console.error("Bad response setting visibility: ", resp.data);
if (resp.data.error?.message) { if (resp.data.error?.message) {
this.$notify( this.alertMessage = resp.data.error?.message;
{
group: "alert",
type: "danger",
title: "Error With Server",
text: resp.data.error?.message,
},
-1,
);
} else { } else {
this.$notify( this.alertMessage = "Bad server response of " + resp.status;
{
group: "alert",
type: "danger",
title: "Error With Server",
text: "Bad server response of " + resp.status,
},
-1,
);
} }
} }
} catch (err) { } catch (err) {
this.$notify( this.alertTitle = "Error With Server";
{ this.alertMessage = err as string;
group: "alert",
type: "danger",
title: "Error With Server",
text: err as string,
},
-1,
);
} }
} }
@@ -616,52 +529,23 @@ export default class ContactsView extends Vue {
contact.seesMe = visibility; contact.seesMe = visibility;
db.contacts.update(contact.did, { seesMe: visibility }); db.contacts.update(contact.did, { seesMe: visibility });
this.$notify( this.alertTitle = "Refreshed";
{ this.alertMessage =
group: "alert", this.nameForContact(contact, true) +
type: "toast", " can " +
title: "Refreshed", (visibility ? "" : "not ") +
text: "see your activity.";
this.nameForContact(contact, true) +
" can " +
(visibility ? "" : "not ") +
"see your activity.",
},
5000,
);
} else { } else {
this.alertTitle = "Error With Server";
if (resp.data.error?.message) { if (resp.data.error?.message) {
this.$notify( this.alertMessage = resp.data.error?.message;
{
group: "alert",
type: "danger",
title: "Error With Server",
text: resp.data.error?.message,
},
-1,
);
} else { } else {
this.$notify( this.alertMessage = "Bad server response of " + resp.status;
{
group: "alert",
type: "danger",
title: "Error With Server",
text: "Bad server response of " + resp.status,
},
-1,
);
} }
} }
} catch (err) { } catch (err) {
this.$notify( this.alertTitle = "Error With Server";
{ this.alertMessage = err as string;
group: "alert",
type: "danger",
title: "Error With Server",
text: err as string,
},
-1,
);
} }
} }
@@ -700,35 +584,15 @@ export default class ContactsView extends Vue {
} }
} }
if (!this.isNumeric(this.hourInput)) { if (!this.isNumeric(this.hourInput)) {
this.$notify( this.alertTitle = "Input Error";
{ this.alertMessage =
group: "alert", "This is not a valid number of hours: " + this.hourInput;
type: "danger",
title: "Input Error",
text: "This is not a valid number of hours: " + this.hourInput,
},
-1,
);
} else if (!parseFloat(this.hourInput)) { } else if (!parseFloat(this.hourInput)) {
this.$notify( this.alertTitle = "Input Error";
{ this.alertMessage = "Giving 0 hours does nothing.";
group: "alert",
type: "danger",
title: "Input Error",
text: "Giving 0 hours does nothing.",
},
-1,
);
} else if (!identity) { } else if (!identity) {
this.$notify( this.alertTitle = "Status Error";
{ this.alertMessage = "No identity is available.";
group: "alert",
type: "danger",
title: "Status Error",
text: "No identity is available.",
},
-1,
);
} else { } else {
// ask to confirm amount // ask to confirm amount
let toFrom; let toFrom;
@@ -812,15 +676,8 @@ export default class ContactsView extends Vue {
try { try {
const resp = await this.axios.post(url, payload, { headers }); const resp = await this.axios.post(url, payload, { headers });
if (resp.data?.success?.handleId) { if (resp.data?.success?.handleId) {
this.$notify( this.alertTitle = "Done";
{ this.alertMessage = "Successfully logged time to the server.";
group: "alert",
type: "success",
title: "Done",
text: "Successfully logged time to the server.",
},
-1,
);
if (fromDid === identity.did) { if (fromDid === identity.did) {
const newList = R.clone(this.givenByMeUnconfirmed); const newList = R.clone(this.givenByMeUnconfirmed);
@@ -845,15 +702,8 @@ export default class ContactsView extends Vue {
userMessage = error as string; userMessage = error as string;
} }
// Now set that error for the user to see. // Now set that error for the user to see.
this.$notify( this.alertTitle = "Error With Server";
{ this.alertMessage = userMessage;
group: "alert",
type: "danger",
title: "Error With Server",
text: userMessage,
},
-1,
);
} }
} }
} }

View File

@@ -83,21 +83,17 @@
class="block py-4 flex gap-4" class="block py-4 flex gap-4"
> >
<div class="w-12"> <div class="w-12">
<EntityIcon <img
:entityId="project.handleId" src="https://picsum.photos/200/200?random=1"
:iconSize="48" class="w-full rounded"
class="block border border-slate-300 rounded-md" />
></EntityIcon>
</div> </div>
<div class="grow"> <div class="grow">
<h2 class="text-base font-semibold">{{ project.name }}</h2> <h2 class="text-base font-semibold">Canyon cleanup</h2>
<h2 class="text-base font-semibold">{{ project.name }}</h2>
<div class="text-sm"> <div class="text-sm">
<fa icon="user" class="fa-fw text-slate-400"></fa> <fa icon="user" class="fa-fw text-slate-400"></fa>
{{ {{ project.name }}
didInfo(project.issuerDid, activeDid, allMyDids, allContacts)
}}
</div> </div>
</div> </div>
</a> </a>
@@ -115,22 +111,17 @@
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 { 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";
import { didInfo } from "@/libs/endorserServer";
import AlertMessage from "@/components/AlertMessage"; import AlertMessage from "@/components/AlertMessage";
import QuickNav from "@/components/QuickNav"; import QuickNav from "@/components/QuickNav";
import InfiniteScroll from "@/components/InfiniteScroll"; import InfiniteScroll from "@/components/InfiniteScroll";
import EntityIcon from "@/components/EntityIcon";
@Component({ @Component({
components: { AlertMessage, QuickNav, InfiniteScroll, EntityIcon }, components: { AlertMessage, QuickNav, InfiniteScroll },
}) })
export default class DiscoverView extends Vue { export default class DiscoverView extends Vue {
activeDid = ""; activeDid = "";
allContacts: Array<Contact> = [];
allMyDids: Array<string> = [];
apiServer = ""; apiServer = "";
searchTerms = ""; searchTerms = "";
alertMessage = ""; alertMessage = "";
@@ -142,20 +133,11 @@ export default class DiscoverView extends Vue {
remoteCount = 0; remoteCount = 0;
isLoading = false; isLoading = false;
// make this function available to the Vue template
didInfo = didInfo;
async mounted() { async mounted() {
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.activeDid = settings?.activeDid || ""; this.activeDid = settings?.activeDid || "";
this.apiServer = settings?.apiServer || ""; this.apiServer = settings?.apiServer || "";
this.allContacts = await db.contacts.toArray();
await accountsDB.open();
const allAccounts = await accountsDB.accounts.toArray();
this.allMyDids = allAccounts.map((acc) => acc.did);
this.searchLocal(); this.searchLocal();
} }
@@ -184,6 +166,7 @@ export default class DiscoverView extends Vue {
public async search(beforeId?: string) { public async search(beforeId?: string) {
let queryParams = "claimContents=" + encodeURIComponent(this.searchTerms); let queryParams = "claimContents=" + encodeURIComponent(this.searchTerms);
console.log(beforeId);
if (beforeId) { if (beforeId) {
queryParams = queryParams + `&beforeId=${beforeId}`; queryParams = queryParams + `&beforeId=${beforeId}`;
} }
@@ -203,16 +186,6 @@ export default class DiscoverView extends Vue {
if (response.status !== 200) { if (response.status !== 200) {
const details = await response.text(); const details = await response.text();
this.$notify(
{
group: "alert",
type: "danger",
title: "Error",
text: `There was a problem accessing the server. Please try again later. (${details})`,
},
-1,
);
throw details; throw details;
} }
@@ -221,8 +194,9 @@ export default class DiscoverView extends Vue {
const plans: ProjectData[] = results.data; const plans: ProjectData[] = results.data;
if (plans) { if (plans) {
for (const plan of plans) { for (const plan of plans) {
const { name, description, handleId, rowid, issuerDid } = plan; const { name, description, handleId = plan.handleId, rowid } = plan;
this.projects.push({ name, description, handleId, rowid, issuerDid }); console.log("here");
this.projects.push({ name, description, handleId, rowid });
} }
this.remoteCount = this.projects.length; this.remoteCount = this.projects.length;
} else { } else {
@@ -230,15 +204,9 @@ export default class DiscoverView extends Vue {
} }
} catch (e) { } catch (e) {
console.log("Error with feed load:", e); console.log("Error with feed load:", e);
this.$notify( this.alertMessage =
{ e.userMessage || "There was an error retrieving projects.";
group: "alert", this.alertTitle = "Error";
type: "danger",
title: "Error",
text: e.userMessage || "There was a problem retrieving projects.",
},
-1,
);
} finally { } finally {
this.isLoading = false; this.isLoading = false;
} }
@@ -272,16 +240,6 @@ export default class DiscoverView extends Vue {
); );
if (response.status !== 200) { if (response.status !== 200) {
const details = await response.text();
this.$notify(
{
group: "alert",
type: "danger",
title: "Error",
text: `There was a problem accessing the server. Please try again later. (${details})`,
},
-1,
);
throw await response.text(); throw await response.text();
} }
@@ -305,15 +263,9 @@ export default class DiscoverView extends Vue {
} }
} catch (e) { } catch (e) {
console.log("Error with feed load:", e); console.log("Error with feed load:", e);
this.$notify( this.alertMessage =
{ e.userMessage || "There was an error retrieving projects.";
group: "alert", this.alertTitle = "Error";
type: "danger",
title: "Error",
text: e.userMessage || "There was a problem retrieving projects.",
},
-1,
);
} finally { } finally {
this.isLoading = false; this.isLoading = false;
} }
@@ -326,6 +278,8 @@ export default class DiscoverView extends Vue {
async loadMoreData(payload: boolean) { async loadMoreData(payload: boolean) {
if (this.projects.length > 0 && payload) { if (this.projects.length > 0 && payload) {
const latestProject = this.projects[this.projects.length - 1]; const latestProject = this.projects[this.projects.length - 1];
console.log("rowid", latestProject, payload);
console.log(Object.keys(latestProject));
if (this.isLocalActive) { if (this.isLocalActive) {
this.searchLocal(latestProject["rowid"]); this.searchLocal(latestProject["rowid"]);
} else if (this.isRemoteActive) { } else if (this.isRemoteActive) {

View File

@@ -128,14 +128,6 @@
key. key.
</p> </p>
<h2 class="text-xl font-semibold">How do I create another identity?</h2>
<p>
Go
<router-link to="start" class="text-blue-500">
create another identity here.
</router-link>
</p>
<h2 class="text-xl font-semibold"> <h2 class="text-xl font-semibold">
I know there is a record from someone, so why can't I see that info? I know there is a record from someone, so why can't I see that info?
</h2> </h2>
@@ -154,6 +146,14 @@
page. page.
</p> </p>
<h2 class="text-xl font-semibold">How do I create another identity?</h2>
<p>
Go
<router-link to="start" class="text-blue-500">
create another identity here.
</router-link>
</p>
<h2 class="text-xl font-semibold">What is your privacy policy?</h2> <h2 class="text-xl font-semibold">What is your privacy policy?</h2>
<p> <p>
See See

View File

@@ -6,94 +6,6 @@
Time Safari Time Safari
</h1> </h1>
<div class="mb-8">
<h2 class="text-xl font-bold mb-4">Notiwind Alert Test Suite</h2>
<button
@click="
this.$notify(
{
group: 'alert',
type: 'toast',
text: 'I\'m a toast. Don\'t mind me.',
},
5000,
)
"
class="font-bold uppercase bg-slate-400 text-white px-3 py-2 rounded-md mr-2"
>
Toast (self-dismiss)
</button>
<button
@click="
this.$notify(
{
group: 'alert',
type: 'info',
title: 'Information Alert',
text: 'Just wanted you to know.',
},
-1,
)
"
class="font-bold uppercase bg-slate-600 text-white px-3 py-2 rounded-md mr-2"
>
Info
</button>
<button
@click="
this.$notify(
{
group: 'alert',
type: 'success',
title: 'Success Alert',
text: 'Congratulations!',
},
-1,
)
"
class="font-bold uppercase bg-emerald-600 text-white px-3 py-2 rounded-md mr-2"
>
Success
</button>
<button
@click="
this.$notify(
{
group: 'alert',
type: 'warning',
title: 'Warning Alert',
text: 'You might wanna look at this.',
},
-1,
)
"
class="font-bold uppercase bg-amber-600 text-white px-3 py-2 rounded-md mr-2"
>
Warning
</button>
<button
@click="
this.$notify(
{
group: 'alert',
type: 'danger',
title: 'Danger Alert',
text: 'Something terrible has happened!',
},
-1,
)
"
class="font-bold uppercase bg-rose-600 text-white px-3 py-2 rounded-md mr-2"
>
Danger
</button>
</div>
<div class="mb-8"> <div class="mb-8">
<h2 class="text-xl font-bold">Quick Action</h2> <h2 class="text-xl font-bold">Quick Action</h2>
<p class="mb-4">Show appreciation to a contact:</p> <p class="mb-4">Show appreciation to a contact:</p>
@@ -104,15 +16,13 @@
:key="contact.did" :key="contact.did"
@click="openDialog(contact)" @click="openDialog(contact)"
> >
<EntityIcon <div class="mb-1">
:entityId="contact.did" <fa icon="user" class="fa-fw fa-xl text-slate-400"></fa>
:iconSize="64" </div>
class="mx-auto border border-slate-300 rounded-md mb-1"
></EntityIcon>
<h3 <h3
class="text-xs font-medium text-ellipsis whitespace-nowrap overflow-hidden" class="text-xs font-medium text-ellipsis whitespace-nowrap overflow-hidden"
> >
{{ contact.name || "(no name)" }} {{ contact.name || contact.did }}
</h3> </h3>
</li> </li>
</ul> </ul>
@@ -129,6 +39,7 @@
<!-- If there are no contacts, show this instead: --> <!-- If there are no contacts, show this instead: -->
<div <div
class="rounded border border-dashed border-slate-300 bg-slate-100 px-4 py-3 text-center italic text-slate-500" 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.) (No contacts to show.)
</div> </div>
@@ -185,10 +96,9 @@ import { createAndSubmitGive, didInfo } from "@/libs/endorserServer";
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";
import EntityIcon from "@/components/EntityIcon";
@Component({ @Component({
components: { GiftedDialog, AlertMessage, QuickNav, EntityIcon }, components: { GiftedDialog, AlertMessage, QuickNav },
}) })
export default class HomeView extends Vue { export default class HomeView extends Vue {
activeDid = ""; activeDid = "";
@@ -248,17 +158,10 @@ export default class HomeView extends Vue {
this.feedLastViewedId = settings?.lastViewedClaimId; this.feedLastViewedId = settings?.lastViewedClaimId;
this.updateAllFeed(); this.updateAllFeed();
} catch (err) { } catch (err) {
this.$notify( this.alertTitle = "Error";
{ this.alertMessage =
group: "alert", err.userMessage ||
type: "danger", "There was an error retrieving the latest sweet, sweet action.";
title: "Error",
text:
err.userMessage ||
"There was an error retrieving the latest sweet, sweet action.",
},
-1,
);
} }
} }
@@ -307,15 +210,9 @@ export default class HomeView extends Vue {
}) })
.catch((e) => { .catch((e) => {
console.log("Error with feed load:", e); console.log("Error with feed load:", e);
this.$notify( this.alertMessage =
{ e.userMessage || "There was an error retrieving feed data.";
group: "alert", this.alertTitle = "Error";
type: "danger",
title: "Export Error",
text: e.userMessage || "There was an error retrieving feed data.",
},
-1,
);
}); });
this.isHiddenSpinner = true; this.isHiddenSpinner = true;
@@ -351,7 +248,7 @@ export default class HomeView extends Vue {
} }
// 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; claim.agent?.identifier || claim.agent?.did || giveRecord.issuer;
const giverInfo = didInfo( const giverInfo = didInfo(
giverDid, giverDid,
this.activeDid, this.activeDid,
@@ -390,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 {
@@ -406,27 +303,17 @@ export default class HomeView extends Vue {
*/ */
public async recordGive(giverDid, description, hours) { public async recordGive(giverDid, description, hours) {
if (!this.activeDid) { if (!this.activeDid) {
this.$notify( this.setAlert(
{ "Error",
group: "alert", "You must select an identity before you can record a give.",
type: "danger",
title: "Error",
text: "You must select an identity before you can record a give.",
},
-1,
); );
return; return;
} }
if (!description && !hours) { if (!description && !hours) {
this.$notify( this.setAlert(
{ "Error",
group: "alert", "You must enter a description or some number of hours.",
type: "danger",
title: "Error",
text: "You must enter a description or some number of hours.",
},
-1,
); );
return; return;
} }
@@ -446,38 +333,19 @@ export default class HomeView extends Vue {
if (this.isGiveCreationError(result)) { if (this.isGiveCreationError(result)) {
const errorMessage = this.getGiveCreationErrorMessage(result); const errorMessage = this.getGiveCreationErrorMessage(result);
console.log("Error with give result:", result); console.log("Error with give result:", result);
this.$notify( this.setAlert(
{ "Error",
group: "alert", errorMessage || "There was an error recording the give.",
type: "danger",
title: "Error",
text: errorMessage || "There was an error recording the give.",
},
-1,
); );
} else { } else {
this.$notify( this.setAlert("Success", "That gift was recorded.");
{
group: "alert",
type: "success",
title: "Success",
text: "That gift was recorded.",
},
-1,
);
} }
} catch (error) { } catch (error) {
console.log("Error with give caught:", error); console.log("Error with give caught:", error);
this.$notify( this.setAlert(
{ "Error",
group: "alert", this.getGiveErrorMessage(error) ||
type: "danger", "There was an error recording the give.",
title: "Error",
text:
this.getGiveErrorMessage(error) ||
"There was an error recording the give.",
},
-1,
); );
} }
} }

View File

@@ -133,19 +133,13 @@ export default class IdentitySwitcherView extends Vue {
this.limitsMessage = "No identity."; this.limitsMessage = "No identity.";
this.loadingLimits = false; this.loadingLimits = false;
} else { } else {
this.$notify( this.alertMessage =
{ "Clear your cache and start over (after data backup).";
group: "alert",
type: "danger",
title: "Error Creating Account",
text: "Clear your cache and start over (after data backup).",
},
-1,
);
console.error( console.error(
"Telling user to clear cache at page create because:", "Telling user to clear cache at page create because:",
err, err,
); );
this.alertTitle = "Error Creating Account";
} }
} }
} }

View File

@@ -218,13 +218,13 @@ export default class NewEditProjectView extends Vue {
try { try {
const resp = await this.axios.post(url, payload, { headers }); const resp = await this.axios.post(url, payload, { headers });
// handleId is new in server v release-1.6.0; remove fullIri when that // handleId is new in server v release-1.6.0; remove fullIri when that
// version shows up here: https://api.endorser.ch/api-docs/ // version shows up here: https://endorser.ch:3000/api-docs/
if (resp.data?.success?.handleId || resp.data?.success?.fullIri) { if (resp.data?.success?.handleId || resp.data?.success?.fullIri) {
this.errorMessage = ""; this.errorMessage = "";
this.alertTitle = ""; this.alertTitle = "";
this.alertMessage = ""; this.alertMessage = "";
// handleId is new in server v release-1.6.0; remove fullIri when that // handleId is new in server v release-1.6.0; remove fullIri when that
// version shows up here: https://api.endorser.ch/api-docs/ // version shows up here: https://endorser.ch:3000/api-docs/
useAppStore().setProjectId( useAppStore().setProjectId(
resp.data.success.handleId || resp.data.success.fullIri, resp.data.success.handleId || resp.data.success.fullIri,
); );
@@ -245,41 +245,20 @@ export default class NewEditProjectView extends Vue {
if (serverError) { if (serverError) {
if (Object.prototype.hasOwnProperty.call(serverError, "message")) { if (Object.prototype.hasOwnProperty.call(serverError, "message")) {
console.log(serverError); console.log(serverError);
this.alertTitle = "User Message";
userMessage = serverError.response.data.error.message; // This is info for the user. userMessage = serverError.response.data.error.message; // This is info for the user.
this.$notify( this.alertMessage = userMessage;
{
group: "alert",
type: "danger",
title: "User Message",
text: userMessage,
},
-1,
);
} else { } else {
this.$notify( this.alertTitle = "Server Message";
{ this.alertMessage = JSON.stringify(serverError.toJSON());
group: "alert",
type: "danger",
title: "Server Message",
text: JSON.stringify(serverError.toJSON()),
},
-1,
);
} }
} else { } else {
console.error( console.error(
"Here's the full error trying to save the claim:", "Here's the full error trying to save the claim:",
error, error,
); );
this.$notify( this.alertTitle = "Claim Error";
{ this.alertMessage = error as string;
group: "alert",
type: "danger",
title: "Claim Error",
text: error as string,
},
-1,
);
} }
// Now set that error for the user to see. // Now set that error for the user to see.
this.errorMessage = userMessage; this.errorMessage = userMessage;

View File

@@ -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>
@@ -26,28 +19,16 @@
<!-- Project Details --> <!-- Project 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">
<div> <div>
<div class="block pb-4 flex gap-4"> <h2 class="text-xl font-semibold">{{ name }}</h2>
<div class="flex-none w-16 pt-1"> <div class="flex justify-between gap-4 text-sm mb-3">
<EntityIcon <span
:entityId="projectId" ><fa icon="user" class="fa-fw text-slate-400"></fa>
:iconSize="64" {{ issuer }}</span
class="block border border-slate-300 rounded-md" >
></EntityIcon> <span
</div> ><fa icon="calendar" class="fa-fw text-slate-400"></fa
>{{ timeSince }}
<div class="overflow-hidden"> </span>
<h2 class="text-xl font-semibold">{{ name }}</h2>
<div class="text-sm mb-3">
<div class="truncate"
><fa icon="user" class="fa-fw text-slate-400"></fa>
{{ issuer }}</div
>
<div
><fa icon="calendar" class="fa-fw text-slate-400"></fa
>{{ timeSince }}
</div>
</div>
</div>
</div> </div>
<div class="text-sm text-slate-500"> <div class="text-sm text-slate-500">
@@ -68,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()"
@@ -77,115 +59,83 @@
</div> </div>
<div> <div>
<div v-if="activeDid" class="text-center"> <div v-if="activeDid">
<button <button
@click="openDialog({ name: 'you', did: activeDid })" @click="openDialog({ name: 'you', did: activeDid })"
class="block w-full text-lg font-bold uppercase bg-blue-600 text-white px-2 py-3 rounded-md" class="text-center text-lg font-bold uppercase bg-blue-600 text-white px-2 py-3 rounded-md"
> >
I gave&hellip; I gave...
</button> </button>
<p class="mt-2 mb-4 text-center">Or, record a gift from:</p> &horbar; or:
</div> </div>
<p v-if="!activeDid" class="mt-2 mb-4">Record a gift from:</p> <!-- similar contact selection code is in multiple places -->
Record a gift from
<ul class="grid grid-cols-4 gap-x-3 gap-y-5 text-center mb-5"> <span v-for="contact in allContacts" :key="contact.did">
<li @click="openDialog()"> <button @click="openDialog(contact)" class="text-blue-500">
<div class="mb-1"> &nbsp;{{ contact.name }}</button
<fa icon="question-circle" class="fa-fw fa-xl text-slate-400"></fa> >,
</div> </span>
<h3 <span v-if="allContacts.length > 0">&nbsp;or&nbsp;</span>
class="text-xs italic font-medium text-ellipsis whitespace-nowrap overflow-hidden" <button @click="openDialog()" class="text-blue-500">
> someone not specified
Anonymous </button>
</h3>
</li>
<li
v-for="contact in allContacts"
:key="contact.did"
@click="openDialog(contact)"
>
<div class="mb-1">
<fa icon="user" class="fa-fw fa-xl text-slate-400"></fa>
</div>
<h3
class="text-xs font-medium text-ellipsis whitespace-nowrap overflow-hidden"
>
{{ 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>
</div> </div>
<!-- Gifts to & from this --> <!-- Gifts to & from this -->
<div class="grid items-start grid-cols-1 sm:grid-cols-2 gap-4"> <div class="mt-8 flex justify-around">
<div class="bg-slate-100 px-4 py-3 rounded-md"> <div>
<h3 class="text-sm uppercase font-semibold mb-3"> <h1 class="text-xl">Given to this Project</h1>
Given to this Project
</h3>
<ul class="text-sm border-t border-slate-300">
<li
v-for="give in givesToThis"
:key="give.id"
class="py-1.5 border-b border-slate-300"
>
<div class="flex justify-between gap-4">
<span
><fa icon="user" class="fa-fw text-slate-400"></fa>
{{ didInfo(give.agentDid, activeDid, allMyDids, allContacts) }}
</span>
<span v-if="give.amount"
><fa icon="coins" class="fa-fw text-slate-400"></fa>
{{ give.amount }}
</span>
</div>
<div v-if="give.description" class="text-slate-500">
<fa icon="comment" class="fa-fw text-slate-400"></fa>
{{ give.description }}
</div>
</li>
</ul>
</div> </div>
<div>
<div class="bg-slate-100 px-4 py-3 rounded-md"> <h1 class="text-xl">... and from this Project</h1>
<h3 class="text-sm uppercase font-semibold mb-3"> </div>
&hellip;and from this Project </div>
</h3> <div class="flex justify-around">
<div class="w-1/2">
<ul class="text-sm border-t border-slate-300"> <div v-for="give in givesToThis" :key="give.id">
<li <div class="flex justify-between">
v-for="give in givesByThis" <div class="flex gap-3">
:key="give.id" <div class="flex gap-2">
class="py-1.5 border-b border-slate-300" <fa icon="user" class="fa-fw text-slate-400"></fa>
> <span>{{
<div class="flex justify-between gap-4"> didInfo(give.agentDid, activeDid, allMyDids, allContacts)
<span }}</span>
><fa icon="user" class="fa-fw text-slate-400"></fa> </div>
{{ didInfo(give.agentDid, activeDid, allMyDids, allContacts) }} <div class="flex gap-2" v-if="give.amount">
</span> <fa icon="coins" class="fa-fw text-slate-400"></fa>
<span v-if="give.amount" <span>{{ give.amount }}</span>
><fa icon="coins" class="fa-fw text-slate-400"></fa> </div>
{{ give.amount }} <div class="flex gap-2" v-if="give.description">
</span> <fa icon="comment" class="fa-fw text-slate-400"></fa>
</div> <span>{{ give.description }}</span>
<div v-if="give.description" class="text-slate-500"> </div>
<fa icon="comment" class="fa-fw text-slate-400"></fa> </div>
{{ give.description }} </div>
</div> </div>
</li> </div>
</ul> <div class="w-1/2">
<div v-for="give in givesByThis" :key="give.id">
<div class="flex justify-between">
<div class="flex gap-3">
<div class="flex gap-2">
<fa icon="user" class="fa-fw text-slate-400"></fa>
<span>{{
didInfo(give.agentDid, activeDid, allMyDids, allContacts)
}}</span>
</div>
<div class="flex gap-2" v-if="give.amount">
<fa icon="coins" class="fa-fw text-slate-400"></fa>
<span>{{ give.amount }}</span>
</div>
<div class="flex gap-2">
<fa icon="comment" class="fa-fw text-slate-400"></fa>
<span>{{ give.description }}</span>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
<GiftedDialog <GiftedDialog
ref="customDialog" ref="customDialog"
@dialog-result="handleDialogResult" @dialog-result="handleDialogResult"
@@ -217,10 +167,9 @@ import {
} from "@/libs/endorserServer"; } from "@/libs/endorserServer";
import AlertMessage from "@/components/AlertMessage"; import AlertMessage from "@/components/AlertMessage";
import QuickNav from "@/components/QuickNav"; import QuickNav from "@/components/QuickNav";
import EntityIcon from "@/components/EntityIcon";
@Component({ @Component({
components: { GiftedDialog, AlertMessage, QuickNav, EntityIcon }, components: { GiftedDialog, AlertMessage, QuickNav },
}) })
export default class ProjectViewView extends Vue { export default class ProjectViewView extends Vue {
activeDid = ""; activeDid = "";
@@ -330,38 +279,16 @@ export default class ProjectViewView extends Vue {
this.truncatedDesc = this.description.slice(0, this.truncateLength); this.truncatedDesc = this.description.slice(0, this.truncateLength);
} else if (resp.status === 404) { } else if (resp.status === 404) {
// actually, axios throws an error so we never get here // actually, axios throws an error so we never get here
this.$notify( this.alertMessage = "That project does not exist.";
{
group: "alert",
type: "danger",
title: "Error",
text: "That project does not exist.",
},
-1,
);
} }
} catch (error: unknown) { } catch (error: unknown) {
const serverError = error as AxiosError; const serverError = error as AxiosError;
if (serverError.response?.status === 404) { if (serverError.response?.status === 404) {
this.$notify( this.alertMessage = "That project does not exist.";
{
group: "alert",
type: "danger",
title: "Error",
text: "That project does not exist.",
},
-1,
);
} else { } else {
this.$notify( this.alertMessage =
{ "Something went wrong retrieving that project." +
group: "alert", " See logs for more info.";
type: "danger",
title: "Error",
text: "Something went wrong retrieving that project. See logs for more info.",
},
-1,
);
console.error("Error retrieving project:", serverError.message); console.error("Error retrieving project:", serverError.message);
} }
} }
@@ -375,27 +302,12 @@ export default class ProjectViewView extends Vue {
if (resp.status === 200 && resp.data.data) { if (resp.status === 200 && resp.data.data) {
this.givesToThis = resp.data.data; this.givesToThis = resp.data.data;
} else { } else {
this.$notify( this.alertMessage = "Failed to retrieve gives to this project.";
{
group: "alert",
type: "danger",
title: "Error",
text: "Failed to retrieve gives to this project.",
},
-1,
);
} }
} catch (error: unknown) { } catch (error: unknown) {
const serverError = error as AxiosError; const serverError = error as AxiosError;
this.$notify( this.alertMessage =
{ "Something went wrong retrieving gives to this project.";
group: "alert",
type: "danger",
title: "Error",
text: "Something went wrong retrieving gives to this project.",
},
-1,
);
console.error( console.error(
"Error retrieving gives to this project:", "Error retrieving gives to this project:",
serverError.message, serverError.message,
@@ -411,27 +323,11 @@ export default class ProjectViewView extends Vue {
if (resp.status === 200 && resp.data.data) { if (resp.status === 200 && resp.data.data) {
this.givesByThis = resp.data.data; this.givesByThis = resp.data.data;
} else { } else {
this.$notify( this.alertMessage = "Failed to retrieve gives by this project.";
{
group: "alert",
type: "danger",
title: "Error",
text: "Failed to retrieve gives by this project.",
},
-1,
);
} }
} catch (error: unknown) { } catch (error: unknown) {
const serverError = error as AxiosError; const serverError = error as AxiosError;
this.$notify( this.alertMessage = "Something went wrong retrieving gives by project.";
{
group: "alert",
type: "danger",
title: "Error",
text: "Something went wrong retrieving gives by project.",
},
-1,
);
console.error( console.error(
"Error retrieving gives by this project:", "Error retrieving gives by this project:",
serverError.message, serverError.message,
@@ -462,28 +358,16 @@ export default class ProjectViewView extends Vue {
*/ */
async recordGive(giverDid, description, hours) { async recordGive(giverDid, description, hours) {
if (!this.activeDid) { if (!this.activeDid) {
this.$notify( this.alertTitle = "Error";
{ this.alertMessage =
group: "alert", "You must select an identity before you can record a give.";
type: "danger",
title: "Error",
text: "You must select an identity before you can record a give.",
},
-1,
);
return; return;
} }
if (!description && !hours) { if (!description && !hours) {
this.$notify( this.alertTitle = "Error";
{ this.alertMessage =
group: "alert", "You must enter a description or some number of hours.";
type: "danger",
title: "Error",
text: "You must enter a description or some number of hours.",
},
-1,
);
return; return;
} }
@@ -502,42 +386,21 @@ export default class ProjectViewView extends Vue {
if (result.status !== 201 || result.data?.error) { if (result.status !== 201 || result.data?.error) {
console.log("Error with give result:", result); console.log("Error with give result:", result);
this.$notify( this.alertTitle = "Error";
{ this.alertMessage =
group: "alert", result.data?.error?.message ||
type: "danger", "There was an error recording the give.";
title: "Error",
text:
result.data?.error?.message ||
"There was an error recording the give.",
},
-1,
);
} else { } else {
this.$notify( this.alertTitle = "Success";
{ this.alertMessage = "That gift was recorded.";
group: "alert",
type: "success",
title: "Success",
text: "That gift was recorded.",
},
-1,
);
} }
} catch (e) { } catch (e) {
console.log("Error with give caught:", e); console.log("Error with give caught:", e);
this.$notify( this.alertTitle = "Error";
{ this.alertMessage =
group: "alert", e.userMessage ||
type: "danger", e.response?.data?.error?.message ||
title: "Error", "There was an error recording the give.";
text:
e.userMessage ||
e.response?.data?.error?.message ||
"There was an error recording the give.",
},
-1,
);
} }
} }
} }

View File

@@ -8,18 +8,16 @@
<!-- Quick Search --> <!-- Quick Search -->
<div 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>
</div>
<!-- New Project --> <!-- New Project -->
<button <button
@@ -39,7 +37,7 @@
<!-- Results List --> <!-- Results List -->
<InfiniteScroll @reached-bottom="loadMoreData"> <InfiniteScroll @reached-bottom="loadMoreData">
<ul class="border-t border-slate-300"> <ul>
<li <li
class="border-b border-slate-300" class="border-b border-slate-300"
v-for="project in projects" v-for="project in projects"
@@ -50,11 +48,10 @@
class="block py-4 flex gap-4" class="block py-4 flex gap-4"
> >
<div class="flex-none w-12"> <div class="flex-none w-12">
<EntityIcon <img
:entityId="project.handleId" src="https://picsum.photos/200/200?random=1"
:iconSize="48" class="w-full rounded"
class="inline-block align-middle border border-slate-300 rounded-md" />
></EntityIcon>
</div> </div>
<div class="grow overflow-hidden"> <div class="grow overflow-hidden">
@@ -83,10 +80,9 @@ import { IIdentifier } from "@veramo/core";
import InfiniteScroll from "@/components/InfiniteScroll"; import InfiniteScroll from "@/components/InfiniteScroll";
import AlertMessage from "@/components/AlertMessage"; import AlertMessage from "@/components/AlertMessage";
import QuickNav from "@/components/QuickNav"; import QuickNav from "@/components/QuickNav";
import EntityIcon from "@/components/EntityIcon";
@Component({ @Component({
components: { InfiniteScroll, AlertMessage, QuickNav, EntityIcon }, components: { InfiniteScroll, AlertMessage, QuickNav },
}) })
export default class ProjectsView extends Vue { export default class ProjectsView extends Vue {
apiServer = ""; apiServer = "";
@@ -128,15 +124,8 @@ export default class ProjectsView extends Vue {
} }
} catch (error) { } catch (error) {
console.error("Got error loading projects:", error.message); console.error("Got error loading projects:", error.message);
this.$notify( this.alertTitle = "Error";
{ this.alertMessage = "Got an error loading projects:" + error.message;
group: "alert",
type: "danger",
title: "Error",
text: "Got an error loading projects: " + error.message,
},
-1,
);
} finally { } finally {
this.isLoading = false; this.isLoading = false;
} }
@@ -205,15 +194,8 @@ export default class ProjectsView extends Vue {
if (this.numAccounts === 0) { if (this.numAccounts === 0) {
console.error("No accounts found."); console.error("No accounts found.");
this.$notify( this.alertTitle = "Error";
{ this.alertMessage = "You need an identity to load your projects.";
group: "alert",
type: "danger",
title: "Error",
text: "You need an identity to load your projects.",
},
-1,
);
} else { } else {
const identity = await this.getIdentity(activeDid); const identity = await this.getIdentity(activeDid);
this.current = identity; this.current = identity;
@@ -221,15 +203,8 @@ export default class ProjectsView extends Vue {
} }
} catch (err) { } catch (err) {
console.log("Error initializing:", err); console.log("Error initializing:", err);
this.$notify( this.alertTitle = "Error";
{ this.alertMessage = "Something went wrong loading your projects.";
group: "alert",
type: "danger",
title: "Error",
text: "Something went wrong loading your projects.",
},
-1,
);
} }
} }

View File

@@ -20,26 +20,22 @@
</div> </div>
<div v-if="activeAccount"> <div v-if="activeAccount">
<p class="text-center mb-4"> <p>
<b class="text-red-600">BEWARE!</b> Anyone who has this seed phrase will BEWARE: Anyone who gets hold of this mnemonic seed phrase will be able
be able impersonate you and take over any digital holdings based on it. impersonate you and take over any digital holdings based on it. So only
Reveal it when you are somewhere only you can see your screen, and reveal it when you are in a private place out of sight of other eyes,
record it somewhere only you have access. and only record it in something private -- don't take a screenshot or
<i>Don't take a screenshot or send it to any online service.</i> send it to any online service.
</p> </p>
<div class="bg-slate-100 rounded-md overflow-hidden p-4 mb-4"> <button
<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="showSeedPhrase"
@click="showSeedPhrase" >
> Click here when you're ready to see it.
Reveal my Seed Phrase </button>
</button>
<p v-if="showSeed" class="text-center text-slate-700 mt-2"> <p v-if="showSeed">{{ activeAccount.mnemonic }}</p>
{{ activeAccount.mnemonic }}
</p>
</div>
</div> </div>
<div v-else>You do not have an active identity.</div> <div v-else>You do not have an active identity.</div>
<AlertMessage <AlertMessage
@@ -76,15 +72,8 @@ export default class SeedBackupView extends Vue {
this.activeAccount = R.find((acc) => acc.did === activeDid, accounts); this.activeAccount = R.find((acc) => acc.did === activeDid, accounts);
} catch (err) { } catch (err) {
console.error("Got an error loading an identity:", err); console.error("Got an error loading an identity:", err);
this.$notify( this.alertTitle = "Error Loading Account";
{ this.alertMessage = "Got an error loading your seed data.";
group: "alert",
type: "danger",
title: "Error Loading Account",
text: "Got an error loading your seed data.",
},
-1,
);
} }
} }

View File

@@ -62,15 +62,8 @@ export default class StatisticsView extends Vue {
this.world = newWorld; this.world = newWorld;
} catch (err) { } catch (err) {
console.log(err); console.log(err);
this.$notify( this.alertTitle = "Mounting error";
{ this.alertMessage = err.message;
group: "alert",
type: "danger",
title: "Mounting Error",
text: err.message,
},
-1,
);
} }
} }