Compare commits

...

55 Commits

Author SHA1 Message Date
Jose Olarte III
2eb0354b2c WIP: converted notification permission dialog into toggle 2023-08-28 20:46:14 +08:00
0af05b4b0d Merge branch 'master' into notification-request-permission-dialog 2023-08-21 03:02:03 -04:00
b9d59eb642 Merge pull request 'allow use of custom derivation path, and add way to increment derivation for existing' (#56) from increment-derived into master
Reviewed-on: trent_larson/kick-starter-for-time-pwa#56
2023-08-21 02:59:35 -04:00
0c05505c46 allow use of custom derivation path, and add way to increment derivation for existing 2023-08-20 19:46:12 -06:00
98c093f655 add potential tasks for multiple derivation paths 2023-08-20 06:29:29 -06:00
88112e0629 add note for deployment 2023-08-19 19:10:32 -06:00
Jose Olarte III
6ab92a83bd Added modal dialog for notification permission setting 2023-08-18 21:38:26 +08:00
Jose Olarte III
bfc52151c0 Restored anonymous item in home share section 2023-08-10 18:44:27 +08:00
Jose Olarte III
868b5413de Added jdenticon to project view 2023-08-10 18:41:17 +08:00
Jose Olarte III
50005a0dc3 Removed duplicate item heading 2023-08-10 18:21:15 +08:00
Jose Olarte III
9247b6ed1f Changed ID to name 2023-08-10 18:20:38 +08:00
Jose Olarte III
75f26ccf2d eslint fixes 2023-08-10 18:16:20 +08:00
Matthew Aaron Raymer
bfd2498906 Merge branch 'master' of ssh://173.199.124.46:222/trent_larson/kick-starter-for-time-pwa 2023-08-07 16:33:47 +08:00
Matthew Aaron Raymer
4933017e9c Merge remote-tracking branch 'origin/cleanup-and-qr-code' 2023-08-07 16:33:37 +08:00
Matthew Aaron Raymer
18c23451bb Merge remote-tracking branch 'origin/contact-amounts-ui-improvements' 2023-08-07 16:10:34 +08:00
Matthew Aaron Raymer
304985f88d Merge remote-tracking branch 'origin/polish-ui-project-view' 2023-08-07 16:08:48 +08:00
Matthew Aaron Raymer
9a41aff8f0 Merge remote-tracking branch 'origin/jdenticon-entity-photos' 2023-08-07 15:59:47 +08:00
Matthew Aaron Raymer
e19cd980b4 Merging with corrections 2023-08-07 15:54:06 +08:00
6d1756b4a5 Merge pull request 'update URL for API server' (#55) from update-api-server into master
Reviewed-on: trent_larson/kick-starter-for-time-pwa#55
2023-08-07 03:13:40 -04:00
ac4c92d8e8 Merge branch 'master' into update-api-server 2023-08-07 03:13:26 -04:00
937a3cb6c6 Merge pull request 'Minor cleanup' (#54) from seed-backup-view-improvements into master
Reviewed-on: trent_larson/kick-starter-for-time-pwa#54
2023-08-07 03:11:23 -04:00
194f741984 Merge branch 'master' into seed-backup-view-improvements 2023-08-07 03:11:07 -04:00
b31c0d975c Merge pull request 'fix display of gives on contact screen; adjust give UI for project' (#49) from ui-fix into master
Reviewed-on: trent_larson/kick-starter-for-time-pwa#49
2023-08-07 02:42:30 -04:00
f09684d7cd Merge branch 'master' into ui-fix 2023-08-07 02:41:30 -04:00
1767a48a7f Merge pull request 'New notification system + test' (#48) from notiwind-alert into master
Reviewed-on: trent_larson/kick-starter-for-time-pwa#48
2023-08-07 02:41:05 -04:00
bf6830a1a8 update URL for API server 2023-08-05 18:20:03 -06:00
Jose Olarte III
fe09f5180d Minor cleanup 2023-08-03 20:12:47 +08:00
Jose Olarte III
5addc3c206 Visual fixes 2023-07-31 21:36:05 +08:00
Jose Olarte III
69f2f3cfd2 Converted to tabular structure
For more adaptive widths
2023-07-31 21:32:19 +08:00
Jose Olarte III
4de66b1609 Cleaned up Project view UI 2023-07-28 20:22:03 +08:00
Jose Olarte III
4b87692231 Added jdenticon to more views 2023-07-25 18:41:23 +08:00
Jose Olarte III
503bb1bd93 Added jdenticon to home and contact gives views 2023-07-24 19:32:30 +08:00
Jose Olarte III
9fa3b8be0b Built jdenticon component 2023-07-24 19:32:11 +08:00
3b1a9b9c5b change description since issuer isn't necessarily giver, and add a task 2023-07-23 11:35:03 -06:00
7f48149d6f add some detail for the map pin, plus other refactors 2023-07-20 20:59:53 -06:00
c5b4921583 fix failure to retrieve all totals for gives from me 2023-07-20 20:33:55 -06:00
b28689ad06 walk back edits that were forward-looking but broken 2023-07-20 20:15:17 -06:00
0444b5be32 fix so that contact recipient is also recorded 2023-07-20 20:12:13 -06:00
4866416aae fix didInfo logic, and add to project lists 2023-07-20 19:02:18 -06:00
e48a4ed05b fix to use the real project name, and add creator 2023-07-20 18:35:27 -06:00
87cfead094 fix display of gives on contact screen; adjust give UI for project 2023-07-20 18:22:45 -06:00
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
26 changed files with 808 additions and 259 deletions

View File

@@ -11,6 +11,9 @@ npm run serve
```
### Compiles and minifies for production
If you are deploying in a subdirectory, add it to `publicPath` in vue.config.js, eg: `publicPath: "/app/time-tracker/",`
```
npm run build
```

23
package-lock.json generated
View File

@@ -33,6 +33,7 @@
"ethereum-cryptography": "^2.0.0",
"ethereumjs-util": "^7.1.5",
"ethr-did-resolver": "^8.0.0",
"jdenticon": "^3.2.0",
"js-generate-password": "^0.1.9",
"localstorage-slim": "^2.4.0",
"luxon": "^3.3.0",
@@ -12049,6 +12050,14 @@
"resolved": "https://registry.npmjs.org/canonicalize/-/canonicalize-2.0.0.tgz",
"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": {
"version": "2.4.0",
"resolved": "https://registry.npmmirror.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz",
@@ -17798,6 +17807,20 @@
"integrity": "sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==",
"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": {
"version": "29.5.0",
"resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.5.0.tgz",

View File

@@ -33,6 +33,7 @@
"ethereum-cryptography": "^2.0.0",
"ethereumjs-util": "^7.1.5",
"ethr-did-resolver": "^8.0.0",
"jdenticon": "^3.2.0",
"js-generate-password": "^0.1.9",
"localstorage-slim": "^2.4.0",
"luxon": "^3.3.0",

View File

@@ -1,40 +1,54 @@
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 } }
- 40 notifications :
- push, where we trigger a ServiceWorker(?) in the app to reach out and check for new data assignee:matthew
- 01 add a location for a project via map pin
- 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.
- 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
- 08 Scan QR code to import into contacts.
- 08 Scan QR code to import into contacts assignee:matthew
- SEE: https://github.com/gruhn/vue-qrcode-reader
- 40 notifications :
- push, where we trigger a ServiceWorker(?) in the app to reach out and check for new data
- 01 Show pop-up or some message confirming that settings & contacts download has been initiated/finished assignee:matthew
- refactor UI :
- .5 Alerts show at the top and can be missed if you've scrolled down on the page, eg. account data download
- .2 Make alerts at the top more visible (because they're currently a similar color and sometimes aren't seen)
- Show pop-up or some message confirming that settings & contacts download has been initiated/finished
- Ensure each action sent to the server has a confirmation - eg registration
- 01 Ensure each action sent to the server has a confirmation - eg registration (ie a toast something that dismisses after 5-10s)
- SEE: https://github.com/emmanuelsw/notiwind assignee:jose
- Home Feed & Quick Give screen :
- 01 save the feed-viewed status in settings storage ("afterQuery")
- 01 quick action - send action, maybe choose via canvas tool 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 include the hash of the latest commit, and maybe a version
- .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
- .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 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 move 'switch identity' to the advanced section
- .1 remove the logic to exclude beforeId in list of plans after server has commit 26b25af605e715600d4f12b6416ed9fd7142d164
- .2 in SeedBackupView, don't load the mnemonic and keep it in memory; only load it when they click "show"
- Discuss whether the remaining tasks are worthwhile before MVP release.
- 04 allow user to download claims, mine + ones I can see about me from others
- .5 change the derivation path, and regenerate test IDs
- 02 allow user to create new DIDs from the same seed phrase (ie. increment derivation path)
- .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
- .5 Do we want to combine first name & last name?
- .2 Show a warning if both giver and recipient are the same (but still allow?)
- contacts v+ :
- 01 Import all the non-sensitive data (ie. contacts & settings).
- .2 show error to user when adding a duplicate contact
@@ -47,12 +61,6 @@ tasks:
- 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)
- .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 :
- 08 thorough testing for errors & edge cases
- Turn off stats-world or ensure it's usable (eg. cannot zoom out too far and lose world, cannot screenshot).

View File

@@ -1,6 +1,7 @@
<template>
<router-view />
<!-- https://github.com/emmanuelsw/notiwind -->
<NotificationGroup group="alert">
<div
class="fixed top-4 right-4 w-full max-w-sm flex flex-col items-start justify-end"
@@ -127,6 +128,89 @@
</Notification>
</div>
</NotificationGroup>
<NotificationGroup group="modal">
<div class="fixed z-[100] top-0 inset-x-0 w-full">
<Notification
v-slot="{ notifications, close }"
enter="transform ease-out duration-300 transition"
enter-from="translate-y-2 opacity-0 sm:translate-y-4"
enter-to="translate-y-0 opacity-100 sm:translate-y-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 === 'notification-permission'"
class="absolute inset-0 h-screen flex flex-col items-center justify-center bg-slate-900/50"
>
<div
class="flex w-11/12 max-w-sm mx-auto mb-3 overflow-hidden bg-white rounded-lg shadow-lg"
>
<div class="w-full px-6 py-6 text-slate-900 text-center">
<p class="text-lg mb-4">
Would you like to <b>turn on</b> notifications for this app?
</p>
<button
class="block w-full text-center text-md font-bold uppercase bg-blue-600 text-white px-2 py-2 rounded-md mb-2"
>
Turn on Notifications
</button>
<div class="grid grid-cols-2 gap-2">
<button
@click="close(notification.id)"
class="block w-full text-center text-md font-bold uppercase bg-slate-600 text-white px-2 py-2 rounded-md"
>
Maybe Later
</button>
<button
class="block w-full text-center text-md font-bold uppercase bg-rose-600 text-white px-2 py-2 rounded-md"
>
Never
</button>
</div>
</div>
</div>
</div>
<div
v-if="notification.type === 'notification-mute'"
class="absolute inset-0 h-screen flex flex-col items-center justify-center bg-slate-900/50"
>
<div
class="flex w-11/12 max-w-sm mx-auto mb-3 overflow-hidden bg-white rounded-lg shadow-lg"
>
<div class="w-full px-6 py-6 text-slate-900 text-center">
<p class="text-lg mb-4">
Would you like to <b>turn off</b> notifications for this app?
</p>
<button
class="block w-full text-center text-md font-bold uppercase bg-rose-600 text-white px-2 py-2 rounded-md mb-2"
>
Turn off Notifications
</button>
<button
@click="close(notification.id)"
class="block w-full text-center text-md font-bold uppercase bg-slate-600 text-white px-2 py-2 rounded-md"
>
Keep it On
</button>
</div>
</div>
</div>
</div>
</Notification>
</div>
</NotificationGroup>
</template>
<style></style>

View File

@@ -0,0 +1,19 @@
<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

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

View File

@@ -28,12 +28,12 @@ type NonsensitiveTables = {
* https://9to5answer.com/how-to-bypass-warning-unexpected-any-specify-a-different-type-typescript-eslint-no-explicit-any
*/
export type SensitiveDexie<T extends unknown = SensitiveTables> = BaseDexie & T;
export const accountsDB = new BaseDexie("KickStartAccounts") as SensitiveDexie;
export const accountsDB = new BaseDexie("TimeSafariAccounts") as SensitiveDexie;
const SensitiveSchemas = Object.assign({}, AccountsSchema);
export type NonsensitiveDexie<T extends unknown = NonsensitiveTables> =
BaseDexie & T;
export const db = new BaseDexie("KickStart") as NonsensitiveDexie;
export const db = new BaseDexie("TimeSafari") as NonsensitiveDexie;
const NonsensitiveSchemas = Object.assign({}, ContactsSchema, SettingsSchema);
/**

View File

@@ -7,6 +7,8 @@ import { HDNode } from "@ethersproject/hdnode";
import * as didJwt from "did-jwt";
import * as u8a from "uint8arrays";
export const DEFAULT_ROOT_DERIVATION_PATH = "m/76798669'/0'/0'/0'";
/**
*
*
@@ -47,17 +49,17 @@ export const newIdentifier = (
*/
export const deriveAddress = (
mnemonic: string,
derivationPath: string = DEFAULT_ROOT_DERIVATION_PATH,
): [string, string, string, string] => {
const UPORT_ROOT_DERIVATION_PATH = "m/7696500'/0'/0'/0'";
mnemonic = mnemonic.trim().toLowerCase();
const hdnode: HDNode = HDNode.fromMnemonic(mnemonic);
const rootNode: HDNode = hdnode.derivePath(UPORT_ROOT_DERIVATION_PATH);
const rootNode: HDNode = hdnode.derivePath(derivationPath);
const privateHex = rootNode.privateKey.substring(2); // original starts with '0x'
const publicHex = rootNode.publicKey.substring(2); // original starts with '0x'
const address = rootNode.address;
return [address, privateHex, publicHex, UPORT_ROOT_DERIVATION_PATH];
return [address, privateHex, publicHex, derivationPath];
};
/**

View File

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

View File

@@ -11,6 +11,8 @@ import "./assets/styles/tailwind.css";
import { library } from "@fortawesome/fontawesome-svg-core";
import {
faArrowLeft,
faArrowRight,
faBurst,
faCalendar,
faChevronLeft,
@@ -54,6 +56,8 @@ import {
} from "@fortawesome/free-solid-svg-icons";
library.add(
faArrowLeft,
faArrowRight,
faBurst,
faCalendar,
faChevronLeft,

View File

@@ -91,6 +91,14 @@ const routes: Array<RouteRecordRaw> = [
/* webpackChunkName: "import-account" */ "../views/ImportAccountView.vue"
),
},
{
path: "/import-derive",
name: "import-derive",
component: () =>
import(
/* webpackChunkName: "import-derive" */ "../views/ImportDerivedAccountView.vue"
),
},
{
path: "/new-edit-account",
name: "new-edit-account",

View File

@@ -119,10 +119,37 @@
</router-link>
<router-link
:to="{ name: 'identity-switcher' }"
class="block text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-8"
class="block text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-2"
>
Switch Identity / No Identity
</router-link>
<label
for="toggleNotifications"
class="flex items-center cursor-pointer mt-4 mb-8"
@click="
this.$notify(
{
group: 'modal',
type: 'notification-permission',
},
-1,
)
"
>
<!-- toggle -->
<div class="relative">
<!-- input -->
<input type="checkbox" name="toggleNotifications" class="sr-only" />
<!-- line -->
<div class="block bg-slate-500 w-14 h-8 rounded-full"></div>
<!-- dot -->
<div
class="dot absolute left-1 top-1 bg-slate-400 w-6 h-6 rounded-full transition"
></div>
</div>
<!-- label -->
<div class="ml-2">App Notifications</div>
</label>
<h3 class="text-sm uppercase font-semibold mb-3">Data</h3>

View File

@@ -1,71 +1,95 @@
<template>
<QuickNav selected="Contacts"></QuickNav>
<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">
Given with {{ contact?.name }}
</h1>
<div class="flex justify-around">
<span />
<span class="justify-around">(Only 50 most recent)</span>
<span />
</div>
<!-- Results List -->
<div>
<div class="border-b border-slate-300 flex">
<div class="w-1/4"></div>
<div class="w-1/4">from them</div>
<div class="w-1/4"></div>
<div class="w-1/4">to them</div>
</div>
<div
class="border-b border-slate-300 flex"
v-for="record in giveRecords"
:key="record.id"
>
<div class="w-1/4">
{{ new Date(record.issuedAt).toLocaleString() }}
</div>
<div class="w-1/4">
<span v-if="record.agentDid == contact.did">
<div class="font-bold">
{{ record.amount }} {{ record.unit }}
<span v-if="record.amountConfirmed" class="tooltip">
<fa icon="circle-check" class="text-green-600 fa-fw ml-1" />
<span class="tooltiptext">Confirmed</span>
</span>
<button v-else class="tooltip" @click="confirm(record)">
<fa icon="circle" class="text-blue-600 fa-fw ml-1" />
<span class="tooltiptext">Unconfirmed</span>
</button>
</div>
<br />
{{ record.description }}
</span>
</div>
<div class="w-1/8">
<span v-if="record.agentDid == contact.did">
<fa icon="long-arrow-alt-left" class="text-slate-900 fa-fw ml-1" />
</span>
<span v-else>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<fa icon="long-arrow-alt-right" class="text-slate-900 fa-fw ml-1" />
</span>
</div>
<div class="w-1/4">
<span v-if="record.agentDid != contact.did">
<div class="font-bold">
{{ record.amount }} {{ record.unit }}
<span v-if="record.amountConfirmed" class="tooltip">
<fa icon="circle-check" class="text-green-600 fa-fw ml-1" />
<span class="tooltiptext">Confirmed</span>
</span>
<button v-else class="tooltip" @click="cannotConfirmMessage()">
<fa icon="circle" class="text-slate-600 fa-fw ml-1" />
<span class="tooltiptext">Unconfirmed</span>
</button>
</div>
<br />
{{ record.description }}
</span>
</div>
</div>
</div>
<table
class="table-auto w-full border-t border-slate-300 text-sm sm:text-base text-center"
>
<thead class="bg-slate-100">
<tr class="border-b border-slate-300">
<th></th>
<th class="px-1 py-2">From Them</th>
<th></th>
<th class="px-1 py-2">To Them</th>
</tr>
</thead>
<tbody>
<tr
v-for="record in giveRecords"
:key="record.id"
class="border-b border-slate-300"
>
<td class="p-1 text-xs sm:text-sm text-left text-slate-500">
{{ new Date(record.issuedAt).toLocaleString() }}
</td>
<td class="p-1">
<span v-if="record.agentDid == contact.did">
<div class="font-bold">
{{ record.amount }} {{ record.unit }}
<span v-if="record.amountConfirmed" title="Confirmed">
<fa icon="circle-check" class="text-green-600 fa-fw" />
</span>
<button v-else @click="confirm(record)" title="Unconfirmed">
<fa icon="circle" class="text-blue-600 fa-fw" />
</button>
</div>
<div class="italic text-xs sm:text-sm text-slate-500">
{{ record.description }}
</div>
</span>
</td>
<td class="p-1">
<span v-if="record.agentDid == contact.did">
<fa icon="arrow-left" class="text-slate-400 fa-fw" />
</span>
<span v-else>
<fa icon="arrow-right" class="text-slate-400 fa-fw" />
</span>
</td>
<td class="p-1">
<span v-if="record.agentDid != contact.did">
<div class="font-bold">
{{ record.amount }} {{ record.unit }}
<span v-if="record.amountConfirmed" title="Confirmed">
<fa icon="circle-check" class="text-green-600 fa-fw" />
</span>
<button
v-else
@click="cannotConfirmMessage()"
title="Unconfirmed"
>
<fa icon="circle" class="text-slate-600 fa-fw" />
</button>
</div>
<div class="italic text-xs sm:text-sm text-slate-500">
{{ record.description }}
</div>
</span>
</td>
</tr>
</tbody>
</table>
<AlertMessage
:alertTitle="alertTitle"
:alertMessage="alertMessage"
@@ -170,7 +194,7 @@ export default class ContactsView extends Vue {
encodeURIComponent(identity.did) +
"&recipientDid=" +
encodeURIComponent(contact.did);
const headers = this.getHeaders(identity);
const headers = await this.getHeaders(identity);
const resp = await this.axios.get(url, { headers });
if (resp.status === 200) {
result = resp.data.data;

View File

@@ -24,8 +24,12 @@
<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>
<span class="grow italic text-slate-500"
><EntityIcon
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
</span>
<span class="text-right">
@@ -46,7 +50,11 @@
>
<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>
><EntityIcon
:entityId="contact.did"
:iconSize="32"
class="inline-block align-middle border border-slate-300 rounded-md mr-1"
></EntityIcon>
{{ contact.name || "(no name)" }}
</span>
<span class="text-right">
@@ -87,9 +95,10 @@ import { Account } from "@/db/tables/accounts";
import { Contact } from "@/db/tables/contacts";
import AlertMessage from "@/components/AlertMessage";
import QuickNav from "@/components/QuickNav";
import EntityIcon from "@/components/EntityIcon";
@Component({
components: { GiftedDialog, AlertMessage, QuickNav },
components: { GiftedDialog, AlertMessage, QuickNav, EntityIcon },
})
export default class HomeView extends Vue {
activeDid = "";

View File

@@ -66,18 +66,27 @@
: "Unconfirmed"
}}
</button>
<br />
(Only hours shown)
<br />
(Only recent shown)
</div>
</div>
<!-- Results List -->
<ul v-if="contacts.length > 0" class="border-t border-slate-300">
<li
class="border-b border-slate-300 py-4"
class="border-b border-slate-300 pt-2.5 pb-4"
v-for="contact in contacts"
:key="contact.did"
>
<div class="grow overflow-hidden">
<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)" }}
</h2>
<div class="text-sm truncate">{{ contact.did }}</div>
@@ -135,9 +144,12 @@
<fa icon="trash-can" class="fa-fw" />
</button>
<div v-if="!showGiveNumbers" class="ml-auto flex gap-1.5">
<div
v-if="showGiveNumbers && contact.did != activeDid"
class="ml-auto flex gap-1.5"
>
<button
class="text-sm uppercase bg-blue-600 text-white px-2 py-1.5 rounded-md"
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]"
>
@@ -152,11 +164,11 @@
: (givenByMeUnconfirmed[contact.did] || 0)
/* eslint-enable prettier/prettier */
}}
<fa icon="plus" class="fa-fw" />
<fa icon="plus" />
</button>
<button
class="text-sm uppercase bg-blue-600 text-white px-2 py-1.5 rounded-md"
class="text-sm bg-blue-600 text-white px-2 py-1.5 rounded-r-md -ml-1.5 border-l border-blue-400"
@click="onClickAddGive(contact.did, activeDid)"
title="givenToMeDescriptions[contact.did]"
>
@@ -171,7 +183,7 @@
: (givenToMeUnconfirmed[contact.did] || 0)
/* eslint-enable prettier/prettier */
}}
<fa icon="plus" class="fa-fw" />
<fa icon="plus" />
</button>
<router-link
@@ -214,12 +226,13 @@ import {
import { Component, Vue } from "vue-facing-decorator";
import AlertMessage from "@/components/AlertMessage";
import QuickNav from "@/components/QuickNav";
import EntityIcon from "@/components/EntityIcon";
// eslint-disable-next-line @typescript-eslint/no-var-requires
const Buffer = require("buffer/").Buffer;
@Component({
components: { AlertMessage, QuickNav },
components: { AlertMessage, QuickNav, EntityIcon },
})
export default class ContactsView extends Vue {
activeDid = "";
@@ -294,20 +307,27 @@ export default class ContactsView extends Vue {
}
async loadGives() {
const handleResponse = (resp, descriptions, confirmed, unconfirmed) => {
const handleResponse = (
resp,
descriptions,
confirmed,
unconfirmed,
useRecipient,
) => {
if (resp.status === 200) {
const allData = resp.data.data;
for (const give of allData) {
const otherDid = useRecipient ? give.recipientDid : give.agentDid;
if (give.unit === "HUR") {
if (give.amountConfirmed) {
const prevAmount = confirmed[give.agentDid] || 0;
confirmed[give.agentDid] = prevAmount + give.amount;
const prevAmount = confirmed[otherDid] || 0;
confirmed[otherDid] = prevAmount + give.amount;
} else {
const prevAmount = unconfirmed[give.agentDid] || 0;
unconfirmed[give.agentDid] = prevAmount + give.amount;
const prevAmount = unconfirmed[otherDid] || 0;
unconfirmed[otherDid] = prevAmount + give.amount;
}
if (!descriptions[give.agentDid] && give.description) {
descriptions[give.agentDid] = give.description;
if (!descriptions[otherDid] && give.description) {
descriptions[otherDid] = give.description;
}
}
}
@@ -334,17 +354,15 @@ export default class ContactsView extends Vue {
};
try {
const { headers, identity } = await this.getHeadersAndIdentity(
this.activeDid,
);
const { headers } = await this.getHeadersAndIdentity(this.activeDid);
const givenByUrl =
this.apiServer +
"/api/v2/report/gives?agentDid=" +
encodeURIComponent(identity.did);
encodeURIComponent(this.activeDid);
const givenToUrl =
this.apiServer +
"/api/v2/report/gives?recipientDid=" +
encodeURIComponent(identity.did);
encodeURIComponent(this.activeDid);
const [givenByMeResp, givenToMeResp] = await Promise.all([
this.axios.get(givenByUrl, { headers }),
@@ -359,6 +377,7 @@ export default class ContactsView extends Vue {
givenByMeDescriptions,
givenByMeConfirmed,
givenByMeUnconfirmed,
true,
);
this.givenByMeDescriptions = givenByMeDescriptions;
this.givenByMeConfirmed = givenByMeConfirmed;
@@ -372,6 +391,7 @@ export default class ContactsView extends Vue {
givenToMeDescriptions,
givenToMeConfirmed,
givenToMeUnconfirmed,
false,
);
this.givenToMeDescriptions = givenToMeDescriptions;
this.givenToMeConfirmed = givenToMeConfirmed;

View File

@@ -83,17 +83,20 @@
class="block py-4 flex gap-4"
>
<div class="w-12">
<img
src="https://picsum.photos/200/200?random=1"
class="w-full rounded"
/>
<EntityIcon
:entityId="project.handleId"
:iconSize="48"
class="block border border-slate-300 rounded-md"
></EntityIcon>
</div>
<div class="grow">
<h2 class="text-base font-semibold">Canyon cleanup</h2>
<h2 class="text-base font-semibold">{{ project.name }}</h2>
<div class="text-sm">
<fa icon="user" class="fa-fw text-slate-400"></fa>
{{ project.name }}
{{
didInfo(project.issuerDid, activeDid, allMyDids, allContacts)
}}
</div>
</div>
</a>
@@ -111,17 +114,22 @@
import { Component, Vue } from "vue-facing-decorator";
import { accountsDB, db } from "@/db";
import { Contact } from "@/db/tables/contacts";
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
import { accessToken } from "@/libs/crypto";
import { didInfo } from "@/libs/endorserServer";
import AlertMessage from "@/components/AlertMessage";
import QuickNav from "@/components/QuickNav";
import InfiniteScroll from "@/components/InfiniteScroll";
import EntityIcon from "@/components/EntityIcon";
@Component({
components: { AlertMessage, QuickNav, InfiniteScroll },
components: { AlertMessage, QuickNav, InfiniteScroll, EntityIcon },
})
export default class DiscoverView extends Vue {
activeDid = "";
allContacts: Array<Contact> = [];
allMyDids: Array<string> = [];
apiServer = "";
searchTerms = "";
alertMessage = "";
@@ -133,11 +141,20 @@ export default class DiscoverView extends Vue {
remoteCount = 0;
isLoading = false;
// make this function available to the Vue template
didInfo = didInfo;
async mounted() {
await db.open();
const settings = await db.settings.get(MASTER_SETTINGS_KEY);
this.activeDid = settings?.activeDid || "";
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();
}
@@ -166,7 +183,6 @@ export default class DiscoverView extends Vue {
public async search(beforeId?: string) {
let queryParams = "claimContents=" + encodeURIComponent(this.searchTerms);
console.log(beforeId);
if (beforeId) {
queryParams = queryParams + `&beforeId=${beforeId}`;
}
@@ -204,9 +220,8 @@ export default class DiscoverView extends Vue {
const plans: ProjectData[] = results.data;
if (plans) {
for (const plan of plans) {
const { name, description, handleId = plan.handleId, rowid } = plan;
console.log("here");
this.projects.push({ name, description, handleId, rowid });
const { name, description, handleId, rowid, issuerDid } = plan;
this.projects.push({ name, description, handleId, rowid, issuerDid });
}
this.remoteCount = this.projects.length;
} else {
@@ -310,8 +325,6 @@ export default class DiscoverView extends Vue {
async loadMoreData(payload: boolean) {
if (this.projects.length > 0 && payload) {
const latestProject = this.projects[this.projects.length - 1];
console.log("rowid", latestProject, payload);
console.log(Object.keys(latestProject));
if (this.isLocalActive) {
this.searchLocal(latestProject["rowid"]);
} else if (this.isRemoteActive) {

View File

@@ -128,6 +128,14 @@
key.
</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">
I know there is a record from someone, so why can't I see that info?
</h2>
@@ -146,14 +154,6 @@
page.
</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>
<p>
See

View File

@@ -92,6 +92,21 @@
>
Danger
</button>
<button
@click="
this.$notify(
{
group: 'modal',
type: 'notification-permission',
},
-1,
)
"
class="font-bold uppercase bg-slate-600 text-white px-3 py-2 rounded-md mr-2"
>
Notification Permission
</button>
</div>
<div class="mb-8">
@@ -99,18 +114,32 @@
<p class="mb-4">Show appreciation to a contact:</p>
<ul class="grid grid-cols-4 gap-x-3 gap-y-5 text-center mb-5">
<li @click="openDialog()">
<EntityIcon
:entityId="Anonymous"
:iconSize="64"
class="mx-auto border border-slate-300 rounded-md mb-1"
></EntityIcon>
<h3
class="text-xs italic font-medium text-ellipsis whitespace-nowrap overflow-hidden"
>
Anonymous
</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>
<EntityIcon
:entityId="contact.did"
:iconSize="64"
class="mx-auto border border-slate-300 rounded-md mb-1"
></EntityIcon>
<h3
class="text-xs font-medium text-ellipsis whitespace-nowrap overflow-hidden"
>
{{ contact.name || "(no name)" }}
{{ contact.name || contact.did }}
</h3>
</li>
</ul>
@@ -127,6 +156,7 @@
<!-- If there are no contacts, show this instead: -->
<div
class="rounded border border-dashed border-slate-300 bg-slate-100 px-4 py-3 text-center italic text-slate-500"
v-if="allContacts.length === 0"
>
(No contacts to show.)
</div>
@@ -183,9 +213,10 @@ import { createAndSubmitGive, didInfo } from "@/libs/endorserServer";
import { Contact } from "@/db/tables/contacts";
import AlertMessage from "@/components/AlertMessage";
import QuickNav from "@/components/QuickNav";
import EntityIcon from "@/components/EntityIcon";
@Component({
components: { GiftedDialog, AlertMessage, QuickNav },
components: { GiftedDialog, AlertMessage, QuickNav, EntityIcon },
})
export default class HomeView extends Vue {
activeDid = "";
@@ -347,8 +378,7 @@ export default class HomeView extends Vue {
claim = claim.claim;
}
// agent.did is for legacy data, before March 2023
const giverDid =
claim.agent?.identifier || claim.agent?.did || giveRecord.issuer;
const giverDid = claim.agent?.identifier || claim.agent?.did;
const giverInfo = didInfo(
giverDid,
this.activeDid,
@@ -387,7 +417,7 @@ export default class HomeView extends Vue {
handleDialogResult(result) {
if (result.action === "confirm") {
return new Promise((resolve) => {
this.recordGive(result.contact?.did, result.description, result.hours);
this.recordGive(result.giver?.did, result.description, result.hours);
resolve();
});
} else {

View File

@@ -24,6 +24,24 @@
v-model="mnemonic"
/>
{{ mnemonic }}
<h3
class="text-sm uppercase font-semibold mb-3"
@click="showAdvanced = !showAdvanced"
>
Advanced
</h3>
<div v-if="showAdvanced">
Enter a custom derivation path
<input
type="text"
class="block w-full rounded border border-slate-400 mb-4 px-3 py-2"
v-model="derivationPath"
/>
For previous uPort or Endorser users,
<a @click="derivationPath = UPORT_DERIVATION_PATH" class="text-blue-500">
click here to use that value.
</a>
</div>
<div class="mt-8">
<button
@click="from_mnemonic()"
@@ -44,7 +62,11 @@
<script lang="ts">
import { Component, Vue } from "vue-facing-decorator";
import { deriveAddress, newIdentifier } from "../libs/crypto";
import {
DEFAULT_ROOT_DERIVATION_PATH,
deriveAddress,
newIdentifier,
} from "../libs/crypto";
import { accountsDB, db } from "@/db";
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
@@ -52,11 +74,14 @@ import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
components: {},
})
export default class ImportAccountView extends Vue {
UPORT_DERIVATION_PATH = "m/7696500'/0'/0'/0'";
mnemonic = "";
address = "";
privateHex = "";
publicHex = "";
derivationPath = "";
derivationPath = DEFAULT_ROOT_DERIVATION_PATH;
showAdvanced = false;
public onCancelClick() {
this.$router.back();
@@ -65,8 +90,10 @@ export default class ImportAccountView extends Vue {
public async from_mnemonic() {
const mne: string = this.mnemonic.trim().toLowerCase();
if (this.mnemonic.trim().length > 0) {
[this.address, this.privateHex, this.publicHex, this.derivationPath] =
deriveAddress(mne);
[this.address, this.privateHex, this.publicHex] = deriveAddress(
mne,
this.derivationPath,
);
const newId = newIdentifier(
this.address,

View File

@@ -0,0 +1,163 @@
<template>
<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">
<!-- Cancel -->
<button
@click="$router.go(-1)"
class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
>
<fa icon="chevron-left"></fa>
</button>
Derive from Existing Identity
</h1>
</div>
<!-- Import Account Form -->
<div>
<p class="text-center text-xl mb-4 font-light">
Will increment the maximum derivation path from the existing seed.
</p>
<p v-if="didArrays.length > 1">
Choose existing DIDs from same seed phrase to compute derivation.
</p>
<ul class="mb-4">
<li
class="block bg-slate-100 rounded-md flex items-center px-4 py-3 mb-2"
v-for="dids in didArrays"
:key="dids[0]"
@click="switchAccount(dids[0])"
>
<fa
v-if="dids[0] == selectedArrayFirstDid"
icon="circle"
class="fa-fw text-blue-400 text-xl mr-3"
></fa>
<fa
v-else
icon="circle"
class="fa-fw text-slate-400 text-xl mr-3"
></fa>
<span class="overflow-hidden">
<div class="text-sm text-slate-500 truncate">
<code>{{ dids.join(",") }}</code>
</div>
</span>
</li>
</ul>
</div>
<div class="mt-8">
<button
@click="incrementDerivation()"
class="block w-full text-center text-lg font-bold uppercase bg-blue-600 text-white px-2 py-3 rounded-md mb-2"
>
Increment and Import
</button>
<button
@click="onCancelClick()"
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>
</section>
</template>
<script lang="ts">
import { Component, Vue } from "vue-facing-decorator";
import {
DEFAULT_ROOT_DERIVATION_PATH,
deriveAddress,
newIdentifier,
} from "../libs/crypto";
import { accountsDB, db } from "@/db";
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
@Component({
components: {},
})
export default class ImportAccountView extends Vue {
derivationPath = DEFAULT_ROOT_DERIVATION_PATH;
didArrays: Array<Array<string>> = [];
selectedArrayFirstDid = "";
async mounted() {
await accountsDB.open();
const accounts = await accountsDB.accounts.toArray();
const seedDids = {};
accounts.forEach((account) => {
const prevDids = seedDids[account.mnemonic] || [];
seedDids[account.mnemonic] = prevDids.concat([account.did]);
});
this.didArrays = Object.values(seedDids);
this.selectedArrayFirstDid = this.didArrays[0][0];
}
public onCancelClick() {
this.$router.back();
}
public switchAccount(did: string) {
this.selectedArrayFirstDid = did;
}
public async incrementDerivation() {
await accountsDB.open();
// find the maximum derivation path for the selected DIDs
const selectedArray: Array<string> = this.didArrays.find(
(dids) => dids[0] === this.selectedArrayFirstDid,
);
const allMatchingAccounts = await accountsDB.accounts
.where("did")
.anyOf(...selectedArray)
.toArray();
const accountWithMaxDeriv = allMatchingAccounts[0];
allMatchingAccounts.slice(1).forEach((account) => {
if (account.derivationPath > accountWithMaxDeriv.derivationPath) {
accountWithMaxDeriv.derivationPath = account.derivationPath;
}
});
// increment the last number in that max derivation path
let lastStr = accountWithMaxDeriv.derivationPath.split("/").slice(-1)[0];
if (lastStr.endsWith("'")) {
lastStr = lastStr.slice(0, -1);
}
const lastNum = parseInt(lastStr, 10);
const newLastNum = lastNum + 1;
const newDerivPath = accountWithMaxDeriv.derivationPath
.split("/")
.slice(0, -1)
.concat([newLastNum.toString() + "'"])
.join("/");
const mne: string = accountWithMaxDeriv.mnemonic;
const [address, privateHex, publicHex] = deriveAddress(mne, newDerivPath);
const newId = newIdentifier(address, publicHex, privateHex, newDerivPath);
try {
await accountsDB.accounts.add({
dateCreated: new Date().toISOString(),
derivationPath: newDerivPath,
did: newId.did,
identity: JSON.stringify(newId),
mnemonic: mne,
publicKeyHex: newId.keys[0].publicKeyHex,
});
// record that as the active DID
await db.open();
db.settings.update(MASTER_SETTINGS_KEY, {
activeDid: newId.did,
});
this.$router.push({ name: "account" });
} catch (err) {
console.error("Error saving mnemonic & updating settings:", err);
}
}
}
</script>

View File

@@ -218,13 +218,13 @@ export default class NewEditProjectView extends Vue {
try {
const resp = await this.axios.post(url, payload, { headers });
// handleId is new in server v release-1.6.0; remove fullIri when that
// version shows up here: https://endorser.ch:3000/api-docs/
// version shows up here: https://api.endorser.ch/api-docs/
if (resp.data?.success?.handleId || resp.data?.success?.fullIri) {
this.errorMessage = "";
this.alertTitle = "";
this.alertMessage = "";
// handleId is new in server v release-1.6.0; remove fullIri when that
// version shows up here: https://endorser.ch:3000/api-docs/
// version shows up here: https://api.endorser.ch/api-docs/
useAppStore().setProjectId(
resp.data.success.handleId || resp.data.success.fullIri,
);

View File

@@ -12,13 +12,6 @@
>
<fa icon="chevron-left" class="fa-fw"></fa>
</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
</h1>
</div>
@@ -26,16 +19,28 @@
<!-- Project Details -->
<div class="bg-slate-100 rounded-md overflow-hidden px-4 py-3 mb-4">
<div>
<h2 class="text-xl font-semibold">{{ name }}</h2>
<div class="flex justify-between gap-4 text-sm mb-3">
<span
><fa icon="user" class="fa-fw text-slate-400"></fa>
{{ issuer }}</span
>
<span
><fa icon="calendar" class="fa-fw text-slate-400"></fa
>{{ timeSince }}
</span>
<div class="block pb-4 flex gap-4">
<div class="flex-none w-16 pt-1">
<EntityIcon
:entityId="projectId"
:iconSize="64"
class="block border border-slate-300 rounded-md"
></EntityIcon>
</div>
<div class="overflow-hidden">
<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 class="text-sm text-slate-500">
@@ -56,6 +61,7 @@
</div>
</div>
<button
v-if="issuer == activeDid"
type="button"
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md"
@click="onEditClick()"
@@ -65,83 +71,119 @@
</div>
<div>
<div v-if="activeDid">
<div v-if="activeDid" class="text-center">
<button
@click="openDialog({ name: 'you', did: activeDid })"
class="text-center text-lg font-bold uppercase bg-blue-600 text-white px-2 py-3 rounded-md"
class="block w-full text-lg font-bold uppercase bg-blue-600 text-white px-2 py-3 rounded-md"
>
I gave...
I gave&hellip;
</button>
&horbar; or:
<p class="mt-2 mb-4 text-center">Or, record a gift from:</p>
</div>
<!-- similar contact selection code is in multiple places -->
Record a gift from
<span v-for="contact in allContacts" :key="contact.did">
<button @click="openDialog(contact)" class="text-blue-500">
&nbsp;{{ contact.name }}</button
>,
</span>
<span v-if="allContacts.length > 0">&nbsp;or&nbsp;</span>
<button @click="openDialog()" class="text-blue-500">
someone not specified
</button>
<p v-if="!activeDid" class="mt-2 mb-4">Record a gift from:</p>
<ul class="grid grid-cols-4 gap-x-3 gap-y-5 text-center mb-5">
<li @click="openDialog()">
<EntityIcon
:entityId="Anonymous"
:iconSize="64"
class="mx-auto border border-slate-300 rounded-md mb-1"
></EntityIcon>
<h3
class="text-xs italic font-medium text-ellipsis whitespace-nowrap overflow-hidden"
>
Anonymous
</h3>
</li>
<li
v-for="contact in allContacts"
:key="contact.did"
@click="openDialog(contact)"
>
<EntityIcon
:entityId="contact.did"
:iconSize="64"
class="mx-auto border border-slate-300 rounded-md mb-1"
></EntityIcon>
<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>
<!-- Gifts to & from this -->
<div class="mt-8 flex justify-around">
<div>
<h1 class="text-xl">Given to this Project</h1>
<div class="grid items-start grid-cols-1 sm:grid-cols-2 gap-4">
<div class="bg-slate-100 px-4 py-3 rounded-md">
<h3 class="text-sm uppercase font-semibold mb-3">
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>
<h1 class="text-xl">... and from this Project</h1>
</div>
</div>
<div class="flex justify-around">
<div class="w-1/2">
<div v-for="give in givesToThis" :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" v-if="give.description">
<fa icon="comment" class="fa-fw text-slate-400"></fa>
<span>{{ give.description }}</span>
</div>
</div>
</div>
</div>
</div>
<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 class="bg-slate-100 px-4 py-3 rounded-md">
<h3 class="text-sm uppercase font-semibold mb-3">
&hellip;and from this Project
</h3>
<ul class="text-sm border-t border-slate-300">
<li
v-for="give in givesByThis"
: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>
<GiftedDialog
ref="customDialog"
@dialog-result="handleDialogResult"
@@ -173,9 +215,10 @@ import {
} from "@/libs/endorserServer";
import AlertMessage from "@/components/AlertMessage";
import QuickNav from "@/components/QuickNav";
import EntityIcon from "@/components/EntityIcon";
@Component({
components: { GiftedDialog, AlertMessage, QuickNav },
components: { GiftedDialog, AlertMessage, QuickNav, EntityIcon },
})
export default class ProjectViewView extends Vue {
activeDid = "";

View File

@@ -50,10 +50,11 @@
class="block py-4 flex gap-4"
>
<div class="flex-none w-12">
<img
src="https://picsum.photos/200/200?random=1"
class="w-full rounded"
/>
<EntityIcon
:entityId="project.handleId"
:iconSize="48"
class="inline-block align-middle border border-slate-300 rounded-md"
></EntityIcon>
</div>
<div class="grow overflow-hidden">
@@ -82,9 +83,10 @@ import { IIdentifier } from "@veramo/core";
import InfiniteScroll from "@/components/InfiniteScroll";
import AlertMessage from "@/components/AlertMessage";
import QuickNav from "@/components/QuickNav";
import EntityIcon from "@/components/EntityIcon";
@Component({
components: { InfiniteScroll, AlertMessage, QuickNav },
components: { InfiniteScroll, AlertMessage, QuickNav, EntityIcon },
})
export default class ProjectsView extends Vue {
apiServer = "";

View File

@@ -20,22 +20,34 @@
</div>
<div v-if="activeAccount">
<p>
BEWARE: Anyone who gets hold of this mnemonic seed phrase will be able
impersonate you and take over any digital holdings based on it. So only
reveal it when you are in a private place out of sight of other eyes,
and only record it in something private -- don't take a screenshot or
send it to any online service.
<p class="text-center mb-4">
<b class="text-red-600">BEWARE!</b> Anyone who has this seed phrase will
be able impersonate you and take over any digital holdings based on it.
Reveal it when you are somewhere only you can see your screen, and
record it somewhere only you have access.
<i>Don't take a screenshot or send it to any online service.</i>
</p>
<button
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md"
@click="showSeedPhrase"
>
Click here when you're ready to see it.
</button>
<p v-if="numAccounts > 1">
<b class="text-orange-600">Note:</b> You have more than one identity
stored in this browser. If they are all based on the same seed as the
current identity, this one backup is sufficient; however, if you have
different seeds for other identities, you will have to back them up
separately.
</p>
<p v-if="showSeed">{{ activeAccount.mnemonic }}</p>
<div class="bg-slate-100 rounded-md overflow-hidden p-4 mb-4">
<button
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md"
@click="showSeedPhrase"
>
Reveal my Seed Phrase
</button>
<p v-if="showSeed" class="text-center text-slate-700 mt-2">
{{ activeAccount.mnemonic }}
</p>
</div>
</div>
<div v-else>You do not have an active identity.</div>
<AlertMessage
@@ -56,6 +68,7 @@ import QuickNav from "@/components/QuickNav";
@Component({ components: { AlertMessage, QuickNav } })
export default class SeedBackupView extends Vue {
activeAccount = null;
numAccounts = 0;
showSeed = false;
alertMessage = "";
alertTitle = "";
@@ -69,6 +82,7 @@ export default class SeedBackupView extends Vue {
await accountsDB.open();
const accounts = await accountsDB.accounts.toArray();
this.numAccounts = accounts.length;
this.activeAccount = R.find((acc) => acc.did === activeDid, accounts);
} catch (err) {
console.error("Got an error loading an identity:", err);

View File

@@ -10,30 +10,46 @@
<div class="mt-8">
<p class="text-center text-xl mb-4 font-light">
Do you already have an identity to import?
Do you have an identity to import?
</p>
<a
@click="onClickYes()"
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 uppercase bg-blue-600 text-white px-2 py-3 rounded-md"
>
No
</a>
<a
@click="onClickNo()"
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md"
>Yes</a
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mt-2"
>
Yes
</a>
<a
v-if="numAccounts > 0"
@click="onClickDerive()"
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mt-2"
>
Derive from Existing Account
</a>
</div>
</section>
</template>
<script lang="ts">
import { Component, Vue } from "vue-facing-decorator";
import { accountsDB } from "@/db";
@Component({
components: {},
})
export default class StartView extends Vue {
numAccounts = 0;
async mounted() {
await accountsDB.open();
this.numAccounts = await accountsDB.accounts.count();
}
public onClickYes() {
this.$router.push({ name: "new-identifier" });
}
@@ -41,5 +57,9 @@ export default class StartView extends Vue {
public onClickNo() {
this.$router.push({ name: "import-account" });
}
public onClickDerive() {
this.$router.push({ name: "import-derive" });
}
}
</script>