Compare commits

..

7 Commits

Author SHA1 Message Date
035f2a5b04 docs: adding do for updated development server run command
- `npm run dev`
2024-03-25 08:15:04 -06:00
09dccc34d6 fix: buffer typescript error in util.ts when parsing ArrayBuffer 2024-03-25 08:10:38 -06:00
ca240ab795 fix: es modules syntax for buffer deps instead of commonjs require 2024-03-24 13:05:22 -06:00
01b5ca6ec8 chore: update vitejs config to deploy on the same default port as the @vue/cli-service
This port is 8080. This is done to not break existing tooling and devops code.
2024-03-24 12:05:09 -06:00
6f49260c1e fix: AccountViewView.vue template not resolving dep for dexie-export-import/dist/import
Previous error:

Error: The following dependencies are imported but could not be resolved:

  dexie-export-import/dist/import (imported by /Users/jason/dev/src/trent/crowd-funder-for-time-pwa/src/views/AccountViewView.vue?id=0)

Are they installed?
    at file:///Users/jason/dev/src/trent/crowd-funder-for-time-pwa/node_modules/vite/dist/node/chunks/dep-DJaaTb_D.js:52506:23
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async file:///Users/jason/dev/src/trent/crowd-funder-for-time-pwa/node_modules/vite/dist/node/chunks/dep-DJaaTb_D.js:51972:38
2024-03-24 11:51:58 -06:00
38f44771e9 Initial stab at vitejs update 2024-03-24 11:18:29 -06:00
7ce00b86e8 deps: npm audit fix to resolve vulnerabilities 1 low, 3 moderate, 1 high
There are still 9 moderate severity vulnerabilities, but I will work on those independentally because they may involve updating to library version that have breaking changes.
2024-03-22 09:29:42 -06:00
44 changed files with 5310 additions and 11637 deletions

View File

@@ -1,4 +1,4 @@
# Only the variables that start with VUE_APP_ are seen in the application process.env in Vue. # Only the variables that start with VITE_ are seen in the application process.env in Vue.
VUE_APP_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01GXYPFF7FA03NXKPYY142PY4H VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01GXYPFF7FA03NXKPYY142PY4H
VUE_APP_DEFAULT_ENDORSER_API_SERVER=https://api.endorser.ch VITE_DEFAULT_ENDORSER_API_SERVER=https://api.endorser.ch
VUE_APP_DEFAULT_IMAGE_API_SERVER=https://image-api.timesafari.app VITE_DEFAULT_IMAGE_API_SERVER=https://image-api.timesafari.app

View File

@@ -2,6 +2,7 @@ module.exports = {
root: true, root: true,
env: { env: {
node: true, node: true,
es2022: true,
}, },
extends: [ extends: [
"plugin:vue/vue3-essential", "plugin:vue/vue3-essential",
@@ -9,9 +10,9 @@ module.exports = {
"@vue/typescript/recommended", "@vue/typescript/recommended",
"plugin:prettier/recommended", "plugin:prettier/recommended",
], ],
parserOptions: { // parserOptions: {
ecmaVersion: 2020, // ecmaVersion: 2020,
}, // },
rules: { rules: {
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off", "no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off", "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",

View File

@@ -10,16 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Nothing - Nothing
## [0.3.6] - 2024.03.24 - 3a07e31d6313ab95711265562d9023c42916e141 ## [0.3.4] - 2024.03.21
### Added
- Button to mirror photo during video
- More detailed onboarding help screen
- Public-data blurb
### Changed in DB or environment
- Nothing
## [0.3.5] - 2024.03.23 - 28754bdfb1e11aa221dd49a5dce4219b69cf6a9d
### Added ### Added
- Photo on gift records - Photo on gift records
### Fixed ### Fixed

View File

@@ -18,6 +18,11 @@ npm install
### Compiles and hot-reloads for development ### Compiles and hot-reloads for development
``` ```
npm run dev
```
### Builds the production app
```
npm run serve npm run serve
``` ```
@@ -42,7 +47,7 @@ npm run lint
``` ```
# (See .env.development for more details.) # (See .env.development for more details.)
# The test BVC_MEETUPS_PROJECT_CLAIM_ID does not resolve as a URL because it's only in the test DB and the prod redirect won't redirect there. # The test BVC_MEETUPS_PROJECT_CLAIM_ID does not resolve as a URL because it's only in the test DB and the prod redirect won't redirect there.
TIME_SAFARI_APP_TITLE="TimeSafari_Test" VUE_APP_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HNTZYJJXTGT0EZS3VEJGX7AK VUE_APP_DEFAULT_ENDORSER_API_SERVER=https://test-api.endorser.ch VUE_APP_DEFAULT_IMAGE_API_SERVER=https://test-image-api.timesafari.app npm run build TIME_SAFARI_APP_TITLE="TimeSafari_Test" VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HNTZYJJXTGT0EZS3VEJGX7AK VITE_DEFAULT_ENDORSER_API_SERVER=https://test-api.endorser.ch VITE_DEFAULT_IMAGE_API_SERVER=https://test-image-api.timesafari.app npm run build
``` ```
* Production * Production
@@ -55,6 +60,8 @@ npm run build
* `rsync -azvu -e "ssh -i ~/.ssh/..." dist ubuntutest@test.timesafari.app:time-safari` * `rsync -azvu -e "ssh -i ~/.ssh/..." dist ubuntutest@test.timesafari.app:time-safari`
* Revert src/constants/app.ts and package.json (if that was prod).
* Commit changes. Record the new hash in the changelog. Edit package.json to increment version & add "-beta", `npm install`, and commit. Also record what version is on production. * Commit changes. Record the new hash in the changelog. Edit package.json to increment version & add "-beta", `npm install`, and commit. Also record what version is on production.
* [Tag with the new version.](https://gitea.anomalistdesign.com/trent_larson/crowd-funder-for-time-pwa/releases) * [Tag with the new version.](https://gitea.anomalistdesign.com/trent_larson/crowd-funder-for-time-pwa/releases)
@@ -126,7 +133,7 @@ To add an icon, add to main.ts and reference with `fa` element and `icon` attrib
* Clear cache for site. (In Chrome, go to `chrome://settings/cookies` and "all site data and permissions"; in Firefox, go to `about:preferences` and search for "cache" then "Manage Data", and also manually remove the IndexedDB data if the DBs still show.) * Clear cache for site. (In Chrome, go to `chrome://settings/cookies` and "all site data and permissions"; in Firefox, go to `about:preferences` and search for "cache" then "Manage Data", and also manually remove the IndexedDB data if the DBs still show.)
* Clear notification permission. (In Chrome, go to `chrome://settings/content/notifications`; in Firefox, go to `about:preferences` and search for "notifications".) * Clear notification permission. (In Chrome, go to `chrome://settings/content/notifications`; in Firefox, go to `about:preferences` and search for "notifications".)
* Unregister service worker. (In Chrome, go to `chrome://serviceworker-internals`; in Firefox, go to `about:serviceworkers`.) * Unregister service worker. (In Chrome, go to `chrome://serviceworker-internals/`; in Firefox, go to `about:serviceworkers`.)
* Clear Cache Storage manually, possibly deleting the DB. (In Chrome, in dev tools under Application; in Firefox, in dev tools under Storage.) * Clear Cache Storage manually, possibly deleting the DB. (In Chrome, in dev tools under Application; in Firefox, in dev tools under Storage.)
(If you find more, add them to the HelpNotificationsView.vue file.) (If you find more, add them to the HelpNotificationsView.vue file.)

View File

@@ -1,3 +0,0 @@
module.exports = {
presets: ["@vue/cli-plugin-babel/preset"],
};

17
index.html Normal file
View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="/favicon.ico">
<title>TimeSafari</title>
</head>
<body>
<noscript>
<strong>We're sorry but TimeSafari doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

15669
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,95 +1,89 @@
{ {
"name": "TimeSafari", "name": "TimeSafari",
"version": "0.3.7-beta", "version": "0.3.4",
"private": true, "private": true,
"scripts": { "scripts": {
"serve": "vue-cli-service serve", "dev": "vite",
"build": "vue-cli-service build", "serve": "vite preview",
"lint": "vue-cli-service lint" "build": "vite build",
"lint": "eslint --ext .js,.ts,.vue --ignore-path .gitignore --fix src"
}, },
"dependencies": { "dependencies": {
"@dicebear/collection": "^5.3.5", "@dicebear/collection": "^5.4.1",
"@dicebear/core": "^5.3.5", "@dicebear/core": "^5.4.1",
"@ethersproject/hdnode": "^5.7.0", "@ethersproject/hdnode": "^5.7.0",
"@fortawesome/fontawesome-svg-core": "^6.4.2", "@fortawesome/fontawesome-svg-core": "^6.5.1",
"@fortawesome/free-solid-svg-icons": "^6.4.2", "@fortawesome/free-solid-svg-icons": "^6.5.1",
"@fortawesome/vue-fontawesome": "^3.0.3", "@fortawesome/vue-fontawesome": "^3.0.6",
"@pvermeer/dexie-encrypted-addon": "^3.0.0", "@pvermeer/dexie-encrypted-addon": "^3.0.0",
"@tweenjs/tween.js": "^21.0.0", "@tweenjs/tween.js": "^21.1.1",
"@types/js-yaml": "^4.0.9", "@types/js-yaml": "^4.0.9",
"@types/luxon": "^3.4.2", "@types/luxon": "^3.4.2",
"@veramo/core": "^5.4.1", "@veramo/core": "^5.6.0",
"@veramo/credential-w3c": "^5.4.1", "@veramo/credential-w3c": "^5.6.0",
"@veramo/data-store": "^5.4.1", "@veramo/data-store": "^5.6.0",
"@veramo/did-manager": "^5.4.1", "@veramo/did-manager": "^5.6.0",
"@veramo/did-provider-ethr": "^5.4.1", "@veramo/did-provider-ethr": "^5.6.0",
"@veramo/did-resolver": "^5.4.1", "@veramo/did-resolver": "^5.6.0",
"@veramo/key-manager": "^5.4.1", "@veramo/key-manager": "^5.6.0",
"@vueuse/core": "^10.4.1", "@vueuse/core": "^10.9.0",
"@zxing/text-encoding": "^0.9.0", "@zxing/text-encoding": "^0.9.0",
"axios": "^1.5.0", "axios": "^1.6.8",
"buffer": "^6.0.3",
"class-transformer": "^0.5.1", "class-transformer": "^0.5.1",
"core-js": "^3.32.1", "dexie": "^3.2.7",
"dexie": "^3.2.4", "dexie-export-import": "^4.1.1",
"dexie-export-import": "^4.0.7", "did-jwt": "^7.4.7",
"did-jwt": "^7.2.7", "ethereum-cryptography": "^2.1.3",
"ethereum-cryptography": "^2.1.2",
"ethereumjs-util": "^7.1.5", "ethereumjs-util": "^7.1.5",
"ethr-did-resolver": "^8.1.2", "ethr-did-resolver": "^8.1.2",
"git-describe": "^4.1.1", "git-describe": "^4.1.1",
"jdenticon": "^3.2.0", "jdenticon": "^3.2.0",
"js-generate-password": "^0.1.9", "js-generate-password": "^0.1.9",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"localstorage-slim": "^2.5.0", "localstorage-slim": "^2.7.0",
"luxon": "^3.4.4", "luxon": "^3.4.4",
"merkletreejs": "^0.3.11", "merkletreejs": "^0.3.11",
"moment": "^2.29.4", "moment": "^2.30.1",
"notiwind": "^2.0.2", "notiwind": "^2.0.2",
"papaparse": "^5.4.1", "papaparse": "^5.4.1",
"pina": "^0.20.2204228", "pina": "^0.20.2204228",
"pinia-plugin-persistedstate": "^3.2.0", "pinia-plugin-persistedstate": "^3.2.1",
"qr-code-generator-vue3": "^1.4.21", "qr-code-generator-vue3": "^1.4.21",
"ramda": "^0.29.0", "ramda": "^0.29.1",
"readable-stream": "^4.4.2", "readable-stream": "^4.5.2",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.14",
"register-service-worker": "^1.7.2", "register-service-worker": "^1.7.2",
"simple-vue-camera": "^1.1.3", "simple-vue-camera": "^1.1.3",
"three": "^0.156.1", "three": "^0.156.1",
"ua-parser-js": "^1.0.37", "ua-parser-js": "^1.0.37",
"util": "^0.12.5", "util": "^0.12.5",
"vue": "^3.3.4", "vue": "^3.4.21",
"vue-axios": "^3.5.2", "vue-axios": "^3.5.2",
"vue-facing-decorator": "^3.0.2", "vue-facing-decorator": "^3.0.4",
"vue-qrcode-reader": "^5.4.1", "vue-qrcode-reader": "^5.5.3",
"vue-router": "^4.2.4", "vue-router": "^4.3.0",
"web-did-resolver": "^2.0.27" "web-did-resolver": "^2.0.27"
}, },
"devDependencies": { "devDependencies": {
"@types/leaflet": "^1.9.4", "@types/leaflet": "^1.9.8",
"@types/ramda": "^0.29.3", "@types/ramda": "^0.29.11",
"@types/three": "^0.155.1", "@types/three": "^0.155.1",
"@types/ua-parser-js": "^0.7.39", "@types/ua-parser-js": "^0.7.39",
"@typescript-eslint/eslint-plugin": "^6.6.0", "@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.6.0", "@typescript-eslint/parser": "^6.21.0",
"@vitejs/plugin-vue": "^5.0.4",
"@vue-leaflet/vue-leaflet": "^0.10.1", "@vue-leaflet/vue-leaflet": "^0.10.1",
"@vue/cli-plugin-babel": "~5.0.8",
"@vue/cli-plugin-eslint": "~5.0.8",
"@vue/cli-plugin-pwa": "~5.0.8",
"@vue/cli-plugin-router": "~5.0.8",
"@vue/cli-plugin-typescript": "~5.0.8",
"@vue/cli-plugin-vuex": "~5.0.8",
"@vue/cli-service": "~5.0.8",
"@vue/eslint-config-typescript": "^11.0.3", "@vue/eslint-config-typescript": "^11.0.3",
"autoprefixer": "^10.4.15", "autoprefixer": "^10.4.19",
"eslint": "^8.53.0", "eslint": "^8.57.0",
"eslint-config-prettier": "^9.0.0", "eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.0.0", "eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-vue": "^9.17.0", "eslint-plugin-vue": "^9.23.0",
"leaflet": "^1.9.4", "leaflet": "^1.9.4",
"postcss": "^8.4.29", "postcss": "^8.4.38",
"prettier": "^3.1.0", "prettier": "^3.2.5",
"tailwindcss": "^3.3.3", "tailwindcss": "^3.4.1",
"typescript": "~5.2.2" "typescript": "~5.2.2",
"vite": "^5.2.0"
} }
} }

View File

@@ -1,17 +1,21 @@
tasks : tasks :
- 24 contextual tutorials https://docs.google.com/document/d/11C_K3RM0rgo0onih20KFhcIzukZyq_CRWqaWX5om_kM/edit#heading=h.iwiwcydou5hw - bug - landscape doesn't show full camera
- bug - got blank screen and errors on iPhone with no bottom tabs
- add to readme - check version, close tabs & restart phone if necessary
- bug maybe - a new give remembers the previous project
- alert & stop if give amount < 0
- add warning that all data (except ID) is public
- onboarding video
- .2 when adding a claim on home screen, push that claim to the top of the list
- 24 Move to Vite - 24 allow a person record with interests, including location; purpose? contact methods? enhance other connections the same? (suggestion from Philippines) assignee-group:ui
- .1 add KindSpring link to ideas
- .1 on feed, don't show "to someone anonymous" if it's to a project - .1 on feed, don't show "to someone anonymous" if it's to a project
- .1 on ideas, put an "x" to close it assignee-group:ui - .1 on ideas, put an "x" to close it assignee-group:ui
- 16 save data backups in Google - 16 save data backups in Google
- 16 generate and use passkeys for identities - 16 generate and use passkeys for identities
- .5 show "give" buttons (eg. from anonymous) even if they can't give, greyed out, and give them a warning and instructions
- .2 when adding a claim on home screen, push that claim to the top of the list
- .2 fix give dialog from "more contacts" off home page to allow giving to this user - .2 fix give dialog from "more contacts" off home page to allow giving to this user
- .2 fix bottom of project selection map, where the icons are hidden but a tap goes to the icon's page assignee-group:ui - .2 fix bottom of project selection map, where the icons are hidden but a tap goes to the icon's page assignee-group:ui
@@ -62,7 +66,6 @@ tasks :
- .1 add cursor-pointer on the icons for giving on the project page, and on the list of projects on the discover page - .1 add cursor-pointer on the icons for giving on the project page, and on the list of projects on the discover page
- .2 record when InfiniteScroll hits the end of the list and don't trigger any more loads (feed, project list, give & offer lists) - .2 record when InfiniteScroll hits the end of the list and don't trigger any more loads (feed, project list, give & offer lists)
- bug (that is hard to reproduce) - got blank screen and errors on iPhone with no bottom tabs
- bug - turning notifications on from the help screen did not stay, though account screen toggle did stay (From Jason on iPhone.) - bug - turning notifications on from the help screen did not stay, though account screen toggle did stay (From Jason on iPhone.)
- refactor - supply the projectId to the OfferDialog just like we do with the GiftedDialog offerId (in the "open" method, maybe as well as an attribute) - refactor - supply the projectId to the OfferDialog just like we do with the GiftedDialog offerId (in the "open" method, maybe as well as an attribute)
- the confirm button on each give on the ProjectViewView page doesn't have all the context of the ClaimView page, so it can show sometimes inappropriately; consider consolidation - the confirm button on each give on the ProjectViewView page doesn't have all the context of the ClaimView page, so it can show sometimes inappropriately; consider consolidation
@@ -125,10 +128,7 @@ tasks :
- .5 don't show "Offer" on project screen if they aren't registered - .5 don't show "Offer" on project screen if they aren't registered
- 01 especially for iOS, check for new version & update, eg. https://stackoverflow.com/questions/52221805/any-way-yet-to-auto-update-or-just-clear-the-cache-on-a-pwa-on-ios - 01 especially for iOS, check for new version & update, eg. https://stackoverflow.com/questions/52221805/any-way-yet-to-auto-update-or-just-clear-the-cache-on-a-pwa-on-ios
- 24 allow a person record with interests, including location; purpose? contact methods? enhance other connections the same? (suggestion from Philippines) assignee-group:ui - 24 Move to Vite
- 24 brief introduction slides https://docs.google.com/document/d/11C_K3RM0rgo0onih20KFhcIzukZyq_CRWqaWX5om_kM/edit#heading=h.iwiwcydou5hw
- 12 feedback https://docs.google.com/document/d/11C_K3RM0rgo0onih20KFhcIzukZyq_CRWqaWX5om_kM/edit#heading=h.iwiwcydou5hw
- 32 accept images for projects - 32 accept images for projects
- 32 accept images for contacts - 32 accept images for contacts
- import project interactions from GitHub/GitLab and manage signing - import project interactions from GitHub/GitLab and manage signing

View File

@@ -1,17 +0,0 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

View File

@@ -1,7 +1,7 @@
<template> <template>
<router-view /> <router-view />
<!-- Messages in the upper-right - https://github.com/emmanuelsw/notiwind --> <!-- https://github.com/emmanuelsw/notiwind -->
<NotificationGroup group="alert"> <NotificationGroup group="alert">
<div <div
class="fixed top-4 right-4 w-full max-w-sm flex flex-col items-start justify-end" class="fixed top-4 right-4 w-full max-w-sm flex flex-col items-start justify-end"
@@ -129,7 +129,6 @@
</div> </div>
</NotificationGroup> </NotificationGroup>
<!-- These are general-purpose messages - except there are some for turning app notifications on and off. -->
<NotificationGroup group="modal"> <NotificationGroup group="modal">
<div class="fixed z-[100] top-0 inset-x-0 w-full"> <div class="fixed z-[100] top-0 inset-x-0 w-full">
<Notification <Notification
@@ -149,7 +148,6 @@
class="w-full" class="w-full"
role="alert" role="alert"
> >
<!-- type "confirm" will post a message and, with onYes function, show a "Yes" button to call that function -->
<div <div
v-if="notification.type === 'confirm'" v-if="notification.type === 'confirm'"
class="absolute inset-0 h-screen flex flex-col items-center justify-center bg-slate-900/50" class="absolute inset-0 h-screen flex flex-col items-center justify-center bg-slate-900/50"
@@ -163,7 +161,6 @@
</p> </p>
<button <button
v-if="notification.onYes"
@click=" @click="
notification.onYes(); notification.onYes();
close(notification.id); close(notification.id);
@@ -177,7 +174,7 @@
@click="close(notification.id)" @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" class="block w-full text-center text-md font-bold uppercase bg-slate-600 text-white px-2 py-2 rounded-md"
> >
{{ notification.onYes ? "Cancel" : "Close" }} Cancel
</button> </button>
</div> </div>
</div> </div>

View File

@@ -1,9 +1,8 @@
@import url('https://fonts.googleapis.com/css2?family=Work+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap');
@tailwind base; @tailwind base;
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
@import url('https://fonts.googleapis.com/css2?family=Work+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap');
@layer base { @layer base {
html { html {
font-family: 'Work Sans', ui-sans-serif, system-ui, sans-serif !important; font-family: 'Work Sans', ui-sans-serif, system-ui, sans-serif !important;

View File

@@ -53,32 +53,25 @@
}" }"
class="text-blue-500" class="text-blue-500"
> >
Photo, ... More Options
</router-link> </router-link>
</span> </span>
</div> </div>
<p class="text-center mb-2 mt-6 italic"> <p class="text-center mb-2 mt-6 italic">
Sign & Send to publish to the world Sign & Send to publish to the world
<fa
icon="circle-info"
class="pl-2 text-blue-500 cursor-pointer"
@click="explainData()"
/>
</p> </p>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2"> <button
<button class="block w-full text-center text-lg font-bold uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md mb-2"
class="block w-full text-center text-lg font-bold uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md" @click="confirm"
@click="confirm" >
> Sign &amp; Send
Sign &amp; Send </button>
</button> <button
<button class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md"
class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md" @click="cancel"
@click="cancel" >
> Cancel
Cancel </button>
</button>
</div>
</div> </div>
</div> </div>
</template> </template>
@@ -202,45 +195,6 @@ export default class GiftedDialog extends Vue {
} }
async confirm() { async confirm() {
if (!this.activeDid) {
this.$notify(
{
group: "alert",
type: "danger",
title: "Error",
text: "You must select an identifier before you can record a give.",
},
3000,
);
return;
}
if (parseFloat(this.amountInput) < 0) {
this.$notify(
{
group: "alert",
type: "danger",
text: "You may not send a negative number.",
title: "",
},
2000,
);
return;
}
if (!this.description && !parseFloat(this.amountInput)) {
this.$notify(
{
group: "alert",
type: "danger",
title: "Error",
text: `You must enter a description or some number of ${
this.libsUtil.UNIT_LONG[this.unitCode]
}.`,
},
2000,
);
return;
}
this.close(); this.close();
this.$notify( this.$notify(
{ {
@@ -275,6 +229,32 @@ export default class GiftedDialog extends Vue {
amountInput: number, amountInput: number,
unitCode: string = "HUR", unitCode: string = "HUR",
) { ) {
if (!this.activeDid) {
this.$notify(
{
group: "alert",
type: "danger",
title: "Error",
text: "You must select an identifier before you can record a give.",
},
-1,
);
return;
}
if (!description && !amountInput) {
this.$notify(
{
group: "alert",
type: "danger",
title: "Error",
text: `You must enter a description or some number of ${this.libsUtil.UNIT_LONG[unitCode]}.`,
},
-1,
);
return;
}
try { try {
const identity = await libsUtil.getIdentity(this.activeDid); const identity = await libsUtil.getIdentity(this.activeDid);
const result = await createAndSubmitGive( const result = await createAndSubmitGive(
@@ -359,18 +339,6 @@ export default class GiftedDialog extends Vue {
result.response?.data?.error?.message result.response?.data?.error?.message
); );
} }
explainData() {
this.$notify(
{
group: "alert",
type: "success",
title: "Data Sharing",
text: libsUtil.PRIVACY_MESSAGE,
},
-1,
);
}
} }
</script> </script>

View File

@@ -43,45 +43,22 @@
<img :src="URL.createObjectURL(blob)" class="mt-2 rounded" /> <img :src="URL.createObjectURL(blob)" class="mt-2 rounded" />
</div> </div>
</div> </div>
<div v-else ref="cameraContainer"> <div v-else>
<!-- <!--
Camera "resolution" doesn't change how it shows on screen but rather stretches the result, eg the following which just stretches it vertically: Camera "resolution" doesn't change how it shows on screen but rather stretches the result, eg the following which just stretches it vertically:
:resolution="{ width: 375, height: 812 }" :resolution="{ width: 375, height: 812 }"
--> -->
<camera <camera facingMode="environment" autoplay ref="camera">
facingMode="environment"
autoplay
ref="camera"
@started="cameraStarted()"
>
<div <div
class="absolute portrait:bottom-0 portrait:left-0 portrait:right-0 portrait:pb-2 landscape:right-0 landscape:top-0 landscape:bottom-0 landscape:pr-4 flex landscape:flex-row justify-center items-center" class="absolute portrait:bottom-0 portrait:left-0 portrait:right-0 landscape:right-0 landscape:top-0 landscape:bottom-0 flex landscape:flex-row justify-center items-center portrait:pb-2 landscape:pr-4"
> >
<button <button
@click="takeImage()" @click="takeImage"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold p-3 rounded-full text-2xl leading-none" class="bg-blue-500 hover:bg-blue-700 text-white font-bold p-3 rounded-full text-2xl leading-none"
> >
<fa icon="camera" class="w-[1em]"></fa> <fa icon="camera" class="w-[1em]"></fa>
</button> </button>
</div> </div>
<div
class="absolute portrait:bottom-2 portrait:right-16 landscape:right-0 landscape:bottom-16 landscape:pr-4 flex justify-center items-center"
>
<button
@click="swapMirrorClass()"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold p-3 rounded-full text-2xl leading-none"
>
<fa icon="left-right" class="w-[1em]"></fa>
</button>
</div>
<div v-if="numDevices > 1" class="absolute bottom-2 right-4">
<button
@click="switchCamera()"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold p-3 rounded-full text-2xl leading-none"
>
<fa icon="rotate" class="w-[1em]"></fa>
</button>
</div>
</camera> </camera>
</div> </div>
</div> </div>
@@ -103,12 +80,12 @@ import { accessToken } from "@/libs/crypto";
export default class GiftedPhotoDialog extends Vue { export default class GiftedPhotoDialog extends Vue {
$notify!: (notification: NotificationIface, timeout?: number) => void; $notify!: (notification: NotificationIface, timeout?: number) => void;
activeDeviceNumber = 0;
activeDid = ""; activeDid = "";
blob: Blob | null = null; blob: Blob | null = null;
mirror = false;
numDevices = 0;
setImage: (arg: string) => void = () => {}; setImage: (arg: string) => void = () => {};
imageHeight?: number = window.innerHeight / 2;
imageWidth?: number = window.innerWidth / 2;
imageWarning = ".";
uploading = false; uploading = false;
visible = false; visible = false;
@@ -152,21 +129,6 @@ export default class GiftedPhotoDialog extends Vue {
this.blob = null; this.blob = null;
} }
async cameraStarted() {
const cameraComponent = this.$refs.camera as InstanceType<typeof Camera>;
if (cameraComponent) {
this.numDevices = (await cameraComponent.devices(["videoinput"])).length;
this.mirror = cameraComponent.facingMode === "user";
}
}
async switchCamera() {
const cameraComponent = this.$refs.camera as InstanceType<typeof Camera>;
this.activeDeviceNumber = (this.activeDeviceNumber + 1) % this.numDevices;
const devices = await cameraComponent?.devices(["videoinput"]);
cameraComponent?.changeCamera(devices[this.activeDeviceNumber].deviceId);
}
async takeImage(/* payload: MouseEvent */) { async takeImage(/* payload: MouseEvent */) {
const cameraComponent = this.$refs.camera as InstanceType<typeof Camera>; const cameraComponent = this.$refs.camera as InstanceType<typeof Camera>;
@@ -238,6 +200,7 @@ export default class GiftedPhotoDialog extends Vue {
<canvas id="canvas" width="320" height="240"></canvas> <canvas id="canvas" width="320" height="240"></canvas>
async cameraClicked() { async cameraClicked() {
console.log("camera_button clicked");
const video = document.querySelector("#video"); const video = document.querySelector("#video");
const stream = await navigator.mediaDevices.getUserMedia({ const stream = await navigator.mediaDevices.getUserMedia({
video: true, video: true,
@@ -248,6 +211,7 @@ export default class GiftedPhotoDialog extends Vue {
} }
} }
photoSnapped() { photoSnapped() {
console.log("snap_photo clicked");
const video = document.querySelector("#video"); const video = document.querySelector("#video");
const canvas = document.querySelector("#canvas"); const canvas = document.querySelector("#canvas");
if ( if (
@@ -268,6 +232,7 @@ export default class GiftedPhotoDialog extends Vue {
// data url of the image // data url of the image
const image_data_url = canvas?.toDataURL("image/jpeg"); const image_data_url = canvas?.toDataURL("image/jpeg");
console.log(image_data_url);
} }
} }
****/ ****/
@@ -322,17 +287,6 @@ export default class GiftedPhotoDialog extends Vue {
this.blob = null; this.blob = null;
} }
} }
swapMirrorClass() {
this.mirror = !this.mirror;
if (this.mirror) {
(this.$refs.cameraContainer as HTMLElement).classList.add("mirror-video");
} else {
(this.$refs.cameraContainer as HTMLElement).classList.remove(
"mirror-video",
);
}
}
} }
</script> </script>
@@ -357,12 +311,4 @@ export default class GiftedPhotoDialog extends Vue {
width: 100%; width: 100%;
max-width: 700px; max-width: 700px;
} }
.mirror-video {
transform: scaleX(-1);
-webkit-transform: scaleX(-1); /* For Safari */
-moz-transform: scaleX(-1); /* For Firefox */
-ms-transform: scaleX(-1); /* For IE */
-o-transform: scaleX(-1); /* For Opera */
}
</style> </style>

View File

@@ -1,15 +1,7 @@
<template> <template>
<div v-if="visible" class="dialog-overlay"> <div v-if="visible" class="dialog-overlay">
<div class="dialog"> <div class="dialog">
<h1 class="text-xl font-bold text-center mb-4 relative"> <h1 class="text-xl font-bold text-center mb-4">Here's one:</h1>
Here's one:
<div
class="text-lg text-center p-2 leading-none absolute right-0 -top-1"
@click="cancel"
>
<fa icon="xmark" class="w-[1em]"></fa>
</div>
</h1>
<span class="flex justify-between"> <span class="flex justify-between">
<span <span
class="rounded-l border border-slate-400 bg-slate-200 px-4 py-2 flex" class="rounded-l border border-slate-400 bg-slate-200 px-4 py-2 flex"

View File

@@ -50,20 +50,18 @@
<p class="text-center mt-6 mb-2 italic"> <p class="text-center mt-6 mb-2 italic">
Sign & Send to publish to the world Sign & Send to publish to the world
</p> </p>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2"> <button
<button class="block w-full text-center text-lg font-bold uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md mb-2"
class="block w-full text-center text-lg font-bold uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md" @click="confirm"
@click="confirm" >
> Sign &amp; Send
Sign &amp; Send </button>
</button> <button
<button class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md"
class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md" @click="cancel"
@click="cancel" >
> Cancel
Cancel </button>
</button>
</div>
</div> </div>
</div> </div>
</template> </template>

View File

@@ -20,11 +20,11 @@ export enum AppString {
} }
export const DEFAULT_ENDORSER_API_SERVER = export const DEFAULT_ENDORSER_API_SERVER =
process.env.VUE_APP_DEFAULT_ENDORSER_API_SERVER || import.meta.env.VITE_DEFAULT_ENDORSER_API_SERVER ||
AppString.TEST_ENDORSER_API_SERVER; AppString.TEST_ENDORSER_API_SERVER;
export const DEFAULT_IMAGE_API_SERVER = export const DEFAULT_IMAGE_API_SERVER =
process.env.VUE_APP_DEFAULT_IMAGE_API_SERVER || import.meta.env.VITE_DEFAULT_IMAGE_API_SERVER ||
AppString.TEST_IMAGE_API_SERVER; AppString.TEST_IMAGE_API_SERVER;
export const DEFAULT_PUSH_SERVER = export const DEFAULT_PUSH_SERVER =

View File

@@ -113,7 +113,7 @@ export const sign = async (privateKeyHex: string) => {
* The SimpleSigner returns a configured function for signing data. * The SimpleSigner returns a configured function for signing data.
* *
* @example * @example
* const signer = SimpleSigner(process.env.PRIVATE_KEY) * const signer = SimpleSigner(import.meta.env.PRIVATE_KEY)
* signer(data, (err, signature) => { * signer(data, (err, signature) => {
* ... * ...
* }) * })

View File

@@ -794,7 +794,7 @@ export const claimSpecialDescription = (
}; };
export const BVC_MEETUPS_PROJECT_CLAIM_ID = export const BVC_MEETUPS_PROJECT_CLAIM_ID =
process.env.VUE_APP_BVC_MEETUPS_PROJECT_CLAIM_ID || import.meta.env.VITE_BVC_MEETUPS_PROJECT_CLAIM_ID ||
"https://endorser.ch/entity/01HNTZYJJXTGT0EZS3VEJGX7AK"; // this won't resolve as a URL on production; it's a URN only found in the test system "https://endorser.ch/entity/01HNTZYJJXTGT0EZS3VEJGX7AK"; // this won't resolve as a URL on production; it's a URN only found in the test system
export const bvcMeetingJoinClaim = (did: string, startTime: string) => { export const bvcMeetingJoinClaim = (did: string, startTime: string) => {

View File

@@ -12,11 +12,10 @@ import { deriveAddress, generateSeed, newIdentifier } from "@/libs/crypto";
import { GenericServerRecord, containsHiddenDid } from "@/libs/endorserServer"; import { GenericServerRecord, containsHiddenDid } from "@/libs/endorserServer";
import * as serverUtil from "@/libs/endorserServer"; import * as serverUtil from "@/libs/endorserServer";
// eslint-disable-next-line @typescript-eslint/no-var-requires // If you edit this, check that the numbers still line up on the side in the alert (on mobile, too),
const Buffer = require("buffer/").Buffer; // and make sure they can take all actions while the notification shows.
export const ONBOARD_MESSAGE =
export const PRIVACY_MESSAGE = "1) Read through all their yellow prompts. 2) Add them to your Contacts by scanning with the QR icon that is by the input box. 3) Click the person icon to register them. 4) Show them your QR so they'll scan you. 5) Have them enable notifications.";
"The data you send be visible to the world -- except: your IDs and the IDs of anyone you tag will stay private, only visible to those you allow.";
/* eslint-disable prettier/prettier */ /* eslint-disable prettier/prettier */
export const UNIT_SHORT: Record<string, string> = { export const UNIT_SHORT: Record<string, string> = {
@@ -236,6 +235,19 @@ export const generateSaveAndActivateIdentity = async (): Promise<string> => {
return newId.did; return newId.did;
}; };
function getBase64(subscription: PushSubscription, key: PushEncryptionKeyName) {
const buffer = subscription.getKey(key);
if (!buffer) {
return null;
}
const value = Buffer.from(buffer);
return value
.toString("base64")
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
}
export const sendTestThroughPushServer = async ( export const sendTestThroughPushServer = async (
subscription: PushSubscription, subscription: PushSubscription,
skipFilter: boolean, skipFilter: boolean,
@@ -252,18 +264,8 @@ export const sendTestThroughPushServer = async (
// Use something other than "Daily Update" https://gitea.anomalistdesign.com/trent_larson/py-push-server/src/commit/3c0e196c11bc98060ec5934e99e7dbd591b5da4d/app.py#L213 // Use something other than "Daily Update" https://gitea.anomalistdesign.com/trent_larson/py-push-server/src/commit/3c0e196c11bc98060ec5934e99e7dbd591b5da4d/app.py#L213
const DIRECT_PUSH_TITLE = "DIRECT_NOTIFICATION"; const DIRECT_PUSH_TITLE = "DIRECT_NOTIFICATION";
const auth = Buffer.from(subscription.getKey("auth")); const authB64 = getBase64(subscription, "auth");
const authB64 = auth const p256dhB64 = getBase64(subscription, "p256dh");
.toString("base64")
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
const p256dh = Buffer.from(subscription.getKey("p256dh"));
const p256dhB64 = p256dh
.toString("base64")
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
const newPayload = { const newPayload = {
endpoint: subscription.endpoint, endpoint: subscription.endpoint,
keys: { keys: {

View File

@@ -45,7 +45,6 @@ import {
faHand, faHand,
faHandHoldingHeart, faHandHoldingHeart,
faHouseChimney, faHouseChimney,
faLeftRight,
faLocationDot, faLocationDot,
faLongArrowAltLeft, faLongArrowAltLeft,
faLongArrowAltRight, faLongArrowAltRight,
@@ -106,7 +105,6 @@ library.add(
faHand, faHand,
faHandHoldingHeart, faHandHoldingHeart,
faHouseChimney, faHouseChimney,
faLeftRight,
faLocationDot, faLocationDot,
faLongArrowAltLeft, faLongArrowAltLeft,
faLongArrowAltRight, faLongArrowAltRight,

View File

@@ -2,7 +2,7 @@
import { register } from "register-service-worker"; import { register } from "register-service-worker";
if (process.env.NODE_ENV === "production") { if (import.meta.env.NODE_ENV === "production") {
register("/sw_scripts-combined.js", { register("/sw_scripts-combined.js", {
ready() { ready() {
console.log( console.log(

View File

@@ -31,229 +31,154 @@ const routes: Array<RouteRecordRaw> = [
{ {
path: "/account", path: "/account",
name: "account", name: "account",
component: () => component: () => import("../views/AccountViewView.vue"),
import(/* webpackChunkName: "account" */ "../views/AccountViewView.vue"),
}, },
{ {
path: "/claim/:id?", path: "/claim/:id?",
name: "claim", name: "claim",
component: () => component: () => import("../views/ClaimView.vue"),
import(/* webpackChunkName: "claim" */ "../views/ClaimView.vue"),
}, },
{ {
path: "/confirm-contact", path: "/confirm-contact",
name: "confirm-contact", name: "confirm-contact",
component: () => component: () => import("../views/ConfirmContactView.vue"),
import(
/* webpackChunkName: "confirm-contact" */ "../views/ConfirmContactView.vue"
),
}, },
{ {
path: "/contact-amounts", path: "/contact-amounts",
name: "contact-amounts", name: "contact-amounts",
component: () => component: () => import("../views/ContactAmountsView.vue"),
import(
/* webpackChunkName: "contact-amounts" */ "../views/ContactAmountsView.vue"
),
}, },
{ {
path: "/contact-gives", path: "/contact-gives",
name: "contact-gives", name: "contact-gives",
component: () => component: () => import("../views/ContactGiftingView.vue"),
import(
/* webpackChunkName: "contact-gives" */ "../views/ContactGiftingView.vue"
),
}, },
{ {
path: "/contact-qr", path: "/contact-qr",
name: "contact-qr", name: "contact-qr",
component: () => component: () => import("../views/ContactQRScanShowView.vue"),
import(
/* webpackChunkName: "contact-qr" */ "../views/ContactQRScanShowView.vue"
),
}, },
{ {
path: "/contacts", path: "/contacts",
name: "contacts", name: "contacts",
component: () => component: () => import("../views/ContactsView.vue"),
import(/* webpackChunkName: "contacts" */ "../views/ContactsView.vue"),
}, },
{ {
path: "/discover", path: "/discover",
name: "discover", name: "discover",
component: () => component: () => import("../views/DiscoverView.vue"),
import(/* webpackChunkName: "discover" */ "../views/DiscoverView.vue"),
}, },
{ {
path: "/gifted-details", path: "/gifted-details",
name: "gifted-details", name: "gifted-details",
component: () => component: () => import("../views/GiftedDetails.vue"),
import(
/* webpackChunkName: "gifted-details" */ "../views/GiftedDetails.vue"
),
}, },
{ {
path: "/help", path: "/help",
name: "help", name: "help",
component: () => component: () => import("../views/HelpView.vue"),
import(/* webpackChunkName: "help" */ "../views/HelpView.vue"),
},
{
path: "/help-notifications",
name: "help-notifications",
component: () =>
import(
/* webpackChunkName: "help-notifications" */ "../views/HelpNotificationsView.vue"
),
},
{
path: "/help-onboarding",
name: "help-onboarding",
component: () =>
import(
/* webpackChunkName: "help-onboarding" */ "../views/HelpOnboardingView.vue"
),
}, },
{ {
path: "/", path: "/",
name: "home", name: "home",
component: () => component: () => import("../views/HomeView.vue"),
import(/* webpackChunkName: "home" */ "../views/HomeView.vue"), },
{
path: "/help-notifications",
name: "help-notifications",
component: () => import("../views/HelpNotificationsView.vue"),
}, },
{ {
path: "/identity-switcher", path: "/identity-switcher",
name: "identity-switcher", name: "identity-switcher",
component: () => component: () => import("../views/IdentitySwitcherView.vue"),
import(
/* webpackChunkName: "identity-switcher" */ "../views/IdentitySwitcherView.vue"
),
}, },
{ {
path: "/import-account", path: "/import-account",
name: "import-account", name: "import-account",
component: () => component: () => import("../views/ImportAccountView.vue"),
import(
/* webpackChunkName: "import-account" */ "../views/ImportAccountView.vue"
),
}, },
{ {
path: "/import-derive", path: "/import-derive",
name: "import-derive", name: "import-derive",
component: () => component: () => import("../views/ImportDerivedAccountView.vue"),
import(
/* webpackChunkName: "import-derive" */ "../views/ImportDerivedAccountView.vue"
),
}, },
{ {
path: "/new-edit-account", path: "/new-edit-account",
name: "new-edit-account", name: "new-edit-account",
component: () => component: () => import("../views/NewEditAccountView.vue"),
import(
/* webpackChunkName: "new-edit-account" */ "../views/NewEditAccountView.vue"
),
}, },
{ {
path: "/new-edit-project", path: "/new-edit-project",
name: "new-edit-project", name: "new-edit-project",
component: () => component: () => import("../views/NewEditProjectView.vue"),
import(
/* webpackChunkName: "new-edit-project" */ "../views/NewEditProjectView.vue"
),
}, },
{ {
path: "/new-identifier", path: "/new-identifier",
name: "new-identifier", name: "new-identifier",
component: () => component: () => import("../views/NewIdentifierView.vue"),
import(
/* webpackChunkName: "new-identifier" */ "../views/NewIdentifierView.vue"
),
}, },
{ {
path: "/project/:id?", path: "/project/:id?",
name: "project", name: "project",
component: () => component: () => import("../views/ProjectViewView.vue"),
import(/* webpackChunkName: "project" */ "../views/ProjectViewView.vue"),
}, },
{ {
path: "/projects", path: "/projects",
name: "projects", name: "projects",
component: () => component: () => import("../views/ProjectsView.vue"),
import(/* webpackChunkName: "projects" */ "../views/ProjectsView.vue"),
beforeEnter: enterOrStart, beforeEnter: enterOrStart,
}, },
{ {
path: "/quick-action-bvc", path: "/quick-action-bvc",
name: "quick-action-bvc", name: "quick-action-bvc",
component: () => component: () => import("../views/QuickActionBvcView.vue"),
import(
/* webpackChunkName: "quick-action-bvc" */ "../views/QuickActionBvcView.vue"
),
}, },
{ {
path: "/quick-action-bvc-begin", path: "/quick-action-bvc-begin",
name: "quick-action-bvc-begin", name: "quick-action-bvc-begin",
component: () => component: () => import("../views/QuickActionBvcBeginView.vue"),
import(
/* webpackChunkName: "quick-action-bvc-begin" */ "../views/QuickActionBvcBeginView.vue"
),
}, },
{ {
path: "/quick-action-bvc-end", path: "/quick-action-bvc-end",
name: "quick-action-bvc-end", name: "quick-action-bvc-end",
component: () => component: () => import("../views/QuickActionBvcEndView.vue"),
import(
/* webpackChunkName: "quick-action-bvc-end" */ "../views/QuickActionBvcEndView.vue"
),
}, },
{ {
path: "/scan-contact", path: "/scan-contact",
name: "scan-contact", name: "scan-contact",
component: () => component: () => import("../views/ContactScanView.vue"),
import(
/* webpackChunkName: "scan-contact" */ "../views/ContactScanView.vue"
),
}, },
{ {
path: "/search-area", path: "/search-area",
name: "search-area", name: "search-area",
component: () => component: () => import("../views/SearchAreaView.vue"),
import(
/* webpackChunkName: "search-area" */ "../views/SearchAreaView.vue"
),
}, },
{ {
path: "/seed-backup", path: "/seed-backup",
name: "seed-backup", name: "seed-backup",
component: () => component: () => import("../views/SeedBackupView.vue"),
import(
/* webpackChunkName: "seed-backup" */ "../views/SeedBackupView.vue"
),
}, },
{ {
path: "/start", path: "/start",
name: "start", name: "start",
component: () => component: () => import("../views/StartView.vue"),
import(/* webpackChunkName: "start" */ "../views/StartView.vue"),
}, },
{ {
path: "/statistics", path: "/statistics",
name: "statistics", name: "statistics",
component: () => component: () => import("../views/StatisticsView.vue"),
import(
/* webpackChunkName: "statistics" */ "../views/StatisticsView.vue"
),
}, },
{ {
path: "/test", path: "/test",
name: "test", name: "test",
component: () => component: () => import("../views/TestView.vue"),
import(/* webpackChunkName: "test" */ "../views/TestView.vue"),
}, },
]; ];
/** @type {*} */ /** @type {*} */
const router = createRouter({ const router = createRouter({
history: createWebHistory(process.env.BASE_URL), history: createWebHistory(import.meta.env.BASE_URL),
routes, routes,
}); });

View File

@@ -503,7 +503,7 @@
<button> <button>
<router-link <router-link
:to="{ name: 'statistics' }" :to="{ name: 'statistics' }"
class="block w-fit text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-4 py-2 rounded-md mb-2" class="block w-fit text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md mb-2"
> >
See Global Animated History of Giving See Global Animated History of Giving
</router-link> </router-link>
@@ -516,7 +516,6 @@
<script lang="ts"> <script lang="ts">
import { AxiosError, AxiosRequestConfig } from "axios"; import { AxiosError, AxiosRequestConfig } from "axios";
import Dexie from "dexie"; import Dexie from "dexie";
import "dexie-export-import";
import { ImportProgress } from "dexie-export-import/dist/import"; import { ImportProgress } from "dexie-export-import/dist/import";
import { ref } from "vue"; import { ref } from "vue";
import { Component, Vue } from "vue-facing-decorator"; import { Component, Vue } from "vue-facing-decorator";
@@ -539,9 +538,7 @@ import {
EndorserRateLimits, EndorserRateLimits,
ImageRateLimits, ImageRateLimits,
} from "@/libs/endorserServer"; } from "@/libs/endorserServer";
import { Buffer } from "buffer/";
// eslint-disable-next-line @typescript-eslint/no-var-requires
const Buffer = require("buffer/").Buffer;
interface IAccount { interface IAccount {
did: string; did: string;

View File

@@ -30,19 +30,17 @@
</div> </div>
<div class="mt-8"> <div class="mt-8">
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2"> <input
<input type="submit"
type="submit" class="block w-full text-center text-lg font-bold uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md mb-2"
class="block w-full text-center text-lg font-bold uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md" value="Add Contact"
value="Add Contact" />
/> <button
<button type="button"
type="button" class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md"
class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md" >
> Cancel
Cancel </button>
</button>
</div>
</div> </div>
</section> </section>
</template> </template>

View File

@@ -88,9 +88,7 @@ import {
CONTACT_URL_PREFIX, CONTACT_URL_PREFIX,
ENDORSER_JWT_URL_LOCATION, ENDORSER_JWT_URL_LOCATION,
} from "@/libs/endorserServer"; } from "@/libs/endorserServer";
import { Buffer } from "buffer/";
// eslint-disable-next-line @typescript-eslint/no-var-requires
const Buffer = require("buffer/").Buffer;
@Component({ @Component({
components: { components: {

View File

@@ -65,19 +65,17 @@
/> />
<div class="mt-8"> <div class="mt-8">
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2"> <input
<input type="submit"
type="submit" class="block w-full text-center text-lg font-bold uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md mb-2"
class="block w-full text-center text-lg font-bold uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md" value="Look Up Contact"
value="Look Up Contact" />
/> <button
<button type="button"
type="button" class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md"
class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md" >
> Cancel
Cancel </button>
</button>
</div>
</div> </div>
</section> </section>
</template> </template>

View File

@@ -10,8 +10,7 @@
<span /> <span />
<span> <span>
<a <a
href="/help-onboarding" @click="showHintsForOnboarding()"
target="_blank"
class="text-xs uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-1 rounded-md ml-1" class="text-xs uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-1 rounded-md ml-1"
> >
Onboarding Guide Onboarding Guide
@@ -314,8 +313,8 @@ import QuickNav from "@/components/QuickNav.vue";
import EntityIcon from "@/components/EntityIcon.vue"; import EntityIcon from "@/components/EntityIcon.vue";
import { Account } from "@/db/tables/accounts"; import { Account } from "@/db/tables/accounts";
// eslint-disable-next-line @typescript-eslint/no-var-requires import { Buffer } from "buffer/";
const Buffer = require("buffer/").Buffer;
@Component({ @Component({
components: { QuickNav, EntityIcon }, components: { QuickNav, EntityIcon },
@@ -511,6 +510,18 @@ export default class ContactsView extends Vue {
} }
} }
showHintsForOnboarding() {
this.$notify(
{
group: "alert",
type: "info",
title: "Onboard Someone",
text: libsUtil.ONBOARD_MESSAGE,
},
-1,
);
}
async onClickNewContact(): Promise<void> { async onClickNewContact(): Promise<void> {
if (!this.contactInput) { if (!this.contactInput) {
this.$notify( this.$notify(
@@ -520,7 +531,7 @@ export default class ContactsView extends Vue {
title: "No Contact", title: "No Contact",
text: "There was no contact info to add.", text: "There was no contact info to add.",
}, },
3000, -1,
); );
return; return;
} }
@@ -548,7 +559,7 @@ export default class ContactsView extends Vue {
title: "Contacts Added", title: "Contacts Added",
text: "Each contact was added. Nothing was sent to the server.", text: "Each contact was added. Nothing was sent to the server.",
}, },
3000, // keeping it up so that the "visibility" message is seen -1, // keeping it up so that the "visibility" message is seen
); );
} catch (e) { } catch (e) {
this.$notify( this.$notify(
@@ -653,7 +664,7 @@ export default class ContactsView extends Vue {
title: "No Contact Info", title: "No Contact Info",
text: "The contact info could not be parsed.", text: "The contact info could not be parsed.",
}, },
3000, -1,
); );
return; return;
} else { } else {
@@ -675,7 +686,7 @@ export default class ContactsView extends Vue {
title: "Incomplete Contact", title: "Incomplete Contact",
text: "Cannot add a contact without a DID.", text: "Cannot add a contact without a DID.",
}, },
5000, -1,
); );
return; return;
} }
@@ -687,7 +698,7 @@ export default class ContactsView extends Vue {
title: "Invalid DID", title: "Invalid DID",
text: "The DID is not valid. It must begin with 'did:'", text: "The DID is not valid. It must begin with 'did:'",
}, },
5000, -1,
); );
return; return;
} }
@@ -726,7 +737,7 @@ export default class ContactsView extends Vue {
title: "Contact Added", title: "Contact Added",
text: addedMessage, text: addedMessage,
}, },
3000, -1, // keeping it up so that the "visibility" message is seen
); );
}) })
.catch((err) => { .catch((err) => {
@@ -842,7 +853,7 @@ export default class ContactsView extends Vue {
title: "Registration Still Unknown", title: "Registration Still Unknown",
text: message, text: message,
}, },
5000, -1,
); );
} else if (resp.data?.success?.handleId) { } else if (resp.data?.success?.handleId) {
contact.registered = true; contact.registered = true;
@@ -881,7 +892,7 @@ export default class ContactsView extends Vue {
title: "Registration Error", title: "Registration Error",
text: userMessage, text: userMessage,
}, },
5000, -1,
); );
} }
} }
@@ -922,7 +933,7 @@ export default class ContactsView extends Vue {
(visibility ? "" : "not ") + (visibility ? "" : "not ") +
"see your activity.", "see your activity.",
}, },
3000, -1,
); );
} }
contact.seesMe = visibility; contact.seesMe = visibility;
@@ -942,7 +953,7 @@ export default class ContactsView extends Vue {
title: "Error Setting Visibility", title: "Error Setting Visibility",
text: message, text: message,
}, },
5000, -1,
); );
} }
} catch (err) { } catch (err) {
@@ -954,7 +965,7 @@ export default class ContactsView extends Vue {
title: "Error Setting Visibility", title: "Error Setting Visibility",
text: "Check connectivity and try again.", text: "Check connectivity and try again.",
}, },
5000, -1,
); );
} }
} }
@@ -986,7 +997,7 @@ export default class ContactsView extends Vue {
(visibility ? "" : "not ") + (visibility ? "" : "not ") +
"see your activity.", "see your activity.",
}, },
3000, -1,
); );
} else { } else {
console.error("Got bad server response checking visibility:", resp); console.error("Got bad server response checking visibility:", resp);
@@ -998,7 +1009,7 @@ export default class ContactsView extends Vue {
title: "Error Checking Visibility", title: "Error Checking Visibility",
text: message, text: message,
}, },
5000, -1,
); );
} }
} catch (err) { } catch (err) {
@@ -1010,7 +1021,7 @@ export default class ContactsView extends Vue {
title: "Error Checking Visibility", title: "Error Checking Visibility",
text: "Check connectivity and try again.", text: "Check connectivity and try again.",
}, },
3000, -1,
); );
} }
} }
@@ -1058,7 +1069,7 @@ export default class ContactsView extends Vue {
title: "Input Error", title: "Input Error",
text: "This is not a valid number of hours: " + this.hourInput, text: "This is not a valid number of hours: " + this.hourInput,
}, },
3000, -1,
); );
} else if (parseFloat(this.hourInput) == 0 && !this.hourDescriptionInput) { } else if (parseFloat(this.hourInput) == 0 && !this.hourDescriptionInput) {
this.$notify( this.$notify(
@@ -1068,7 +1079,7 @@ export default class ContactsView extends Vue {
title: "Input Error", title: "Input Error",
text: "Giving no hours or description does nothing.", text: "Giving no hours or description does nothing.",
}, },
3000, -1,
); );
} else if (!identity) { } else if (!identity) {
this.$notify( this.$notify(
@@ -1078,7 +1089,7 @@ export default class ContactsView extends Vue {
title: "Status Error", title: "Status Error",
text: "No identifier is available.", text: "No identifier is available.",
}, },
3000, -1,
); );
} else { } else {
// ask to confirm amount // ask to confirm amount
@@ -1207,7 +1218,7 @@ export default class ContactsView extends Vue {
title: "Error Sending Give", title: "Error Sending Give",
text: userMessage, text: userMessage,
}, },
5000, -1,
); );
} }
} }

View File

@@ -92,26 +92,19 @@
<p class="text-center mb-2 mt-6 italic"> <p class="text-center mb-2 mt-6 italic">
Sign & Send to publish to the world Sign & Send to publish to the world
<fa
icon="circle-info"
class="pl-2 text-blue-500 cursor-pointer"
@click="explainData()"
/>
</p> </p>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2"> <button
<button class="block w-full text-center text-lg font-bold uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md mb-2"
class="block w-full text-center text-lg font-bold uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md" @click="confirm"
@click="confirm" >
> Sign &amp; Send
Sign &amp; Send </button>
</button> <button
<button class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md"
class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md" @click="cancel"
@click="cancel" >
> Cancel
Cancel </button>
</button>
</div>
</section> </section>
</template> </template>
@@ -295,45 +288,6 @@ export default class GiftedDetails extends Vue {
} }
async confirm() { async confirm() {
if (!this.activeDid) {
this.$notify(
{
group: "alert",
type: "danger",
title: "Error",
text: "You must select an identifier before you can record a give.",
},
2000,
);
return;
}
if (parseFloat(this.amountInput) < 0) {
this.$notify(
{
group: "alert",
type: "danger",
text: "You may not send a negative number.",
title: "",
},
2000,
);
return;
}
if (!this.description && !parseFloat(this.amountInput)) {
this.$notify(
{
group: "alert",
type: "danger",
title: "Error",
text: `You must enter a description or some number of ${
this.libsUtil.UNIT_LONG[this.unitCode]
}.`,
},
2000,
);
return;
}
this.$notify( this.$notify(
{ {
group: "alert", group: "alert",
@@ -343,7 +297,6 @@ export default class GiftedDetails extends Vue {
}, },
1000, 1000,
); );
// this is asynchronous, but we don't need to wait for it to complete // this is asynchronous, but we don't need to wait for it to complete
await this.recordGive(); await this.recordGive();
} }
@@ -356,6 +309,34 @@ export default class GiftedDetails extends Vue {
* @param unitCode may be omitted, defaults to "HUR" * @param unitCode may be omitted, defaults to "HUR"
*/ */
public async recordGive() { public async recordGive() {
if (!this.activeDid) {
this.$notify(
{
group: "alert",
type: "danger",
title: "Error",
text: "You must select an identifier before you can record a give.",
},
-1,
);
return;
}
if (!this.description && !this.amountInput) {
this.$notify(
{
group: "alert",
type: "danger",
title: "Error",
text: `You must enter a description or some number of ${
this.libsUtil.UNIT_LONG[this.unitCode]
}.`,
},
-1,
);
return;
}
try { try {
const identity = await libsUtil.getIdentity(this.activeDid); const identity = await libsUtil.getIdentity(this.activeDid);
const result = await createAndSubmitGive( const result = await createAndSubmitGive(
@@ -443,17 +424,5 @@ export default class GiftedDetails extends Vue {
result.response?.data?.error?.message result.response?.data?.error?.message
); );
} }
explainData() {
this.$notify(
{
group: "alert",
type: "success",
title: "Data Sharing",
text: libsUtil.PRIVACY_MESSAGE,
},
-1,
);
}
} }
</script> </script>

View File

@@ -1,69 +0,0 @@
<template>
<!-- Don't include nav buttons since this is shown in a different window. -->
<!-- CONTENT -->
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
<!-- Breadcrumb -->
<div class="mb-8">
<!-- Don't include 'back' button since this is shown in a different window. -->
<!-- Heading -->
<h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8">
Time Safari Onboarding Instructions
</h1>
</div>
<!-- eslint-disable prettier/prettier -->
<div class="ml-4">
<h1 class="font-bold text-xl">Install</h1>
<div>
<p>
1) Have them visit TimeSafari.app in a browser, preferably Chrome or Safari.
</p>
<p>
2) Have them "Install" the site to their desktop.
</p>
</div>
<h1 class="font-bold text-xl">Add Contact & Register</h1>
<div>
<p>
3) Have them follow their yellow prompts.
</p>
<p>
4) Add them to your contacts <fa icon="users" />
</p>
<p>
5) Register them <fa icon="person-circle-question" />
</p>
<p>
6) Add yourself to their contacts <fa icon="users" />
</p>
</div>
<h1 class="font-bold text-xl">Enable Notifications</h1>
<div>
<p>
7) Enable notifications from <fa icon="circle-user" />
</p>
</div>
<h1 class="font-bold text-xl">Discuss Backups</h1>
<div>
<p>
8) Exporting backups <fa icon="circle-user" /> are important if they lose their phone --- especially for the Identifier Seed!
</p>
</div>
</div>
<!-- eslint enable -->
</section>
</template>
<script lang="ts">
import { Component, Vue } from "vue-facing-decorator";
import QuickNav from "@/components/QuickNav.vue";
@Component({ components: { QuickNav } })
export default class Help extends Vue {}
</script>

View File

@@ -39,22 +39,22 @@
and network. and network.
</p> </p>
<p> <p>
You highlight giving and also offer help to ideas -- which could be You can show giving and also offer help to ideas, based on others'
conditional on others' willingness to help, too. willingness to help out, too. You can record your own ideas and invite
You can record your own ideas and invite others to collaborate. others to collaborate.
</p> </p>
<p> <p>
This app uses the power of cryptography to build a reputation, recording This app uses the power of cryptography to build a reputation, recording
activity that you can share at your discretion. You put some activity activity that you can share at your discretion. You put some activity
public, but these services don't share your ID with others without explicit consent. public, but your sensitive information is not shared with anyone,
This is in contrast to Meta and Google, who hold including our services. This is in contrast to Meta and Google, who hold
your data and allow you use it while they manage sharing... your data and allow you use it. Those services are useful, but they have
those services are useful but they have the control, whereas this app gives you the control. the control; this app gives you the control.
</p> </p>
<h2 class="text-xl font-semibold">How do I get started?</h2> <h2 class="text-xl font-semibold">How do I get started?</h2>
<p> <p>
You need someone to register you, like the person who told you You need someone to register you -- usually the person who told you
about this app, on the Contacts about this app, on the Contacts
<fa icon="users" class="fa-fw" /> page. After they register you, you can <fa icon="users" class="fa-fw" /> page. After they register you, you can
select any contact on the home page (or "anonymous") and record your select any contact on the home page (or "anonymous") and record your
@@ -83,9 +83,9 @@
<h2 class="text-xl font-semibold">How do I add someone else?</h2> <h2 class="text-xl font-semibold">How do I add someone else?</h2>
<p> <p>
<a href="/help-onboarding" target="_blank" class="text-blue-500"> <button class="text-blue-500" @click="showOnboardInfo">
Click here to show an alert with the steps. Click here to show an alert with the steps.
</a> </button>
To start scanning, go To start scanning, go
<router-link class="text-blue-500" to="/contact-qr">here.</router-link> <router-link class="text-blue-500" to="/contact-qr">here.</router-link>
</p> </p>
@@ -198,7 +198,9 @@
<ul> <ul>
<li class="list-disc list-outside ml-4"> <li class="list-disc list-outside ml-4">
Chrome: Chrome:
Clear at chrome://settings/content/all and <a href="chrome://settings/content/all" class="text-blue-500"
>clear here</a
>
also clear under dev tools Application also clear under dev tools Application
</li> </li>
<li class="list-disc list-outside ml-4"> <li class="list-disc list-outside ml-4">
@@ -376,12 +378,25 @@ import { Component, Vue } from "vue-facing-decorator";
import * as Package from "../../package.json"; import * as Package from "../../package.json";
import QuickNav from "@/components/QuickNav.vue"; import QuickNav from "@/components/QuickNav.vue";
import { NotificationIface } from "@/constants/app"; import { NotificationIface } from "@/constants/app";
import { ONBOARD_MESSAGE } from "@/libs/util";
@Component({ components: { QuickNav } }) @Component({ components: { QuickNav } })
export default class Help extends Vue { export default class Help extends Vue {
$notify!: (notification: NotificationIface, timeout?: number) => void; $notify!: (notification: NotificationIface, timeout?: number) => void;
package = Package; package = Package;
commitHash = process.env.VUE_APP_GIT_HASH; commitHash = import.meta.env.VITE_GIT_HASH;
showOnboardInfo() {
this.$notify(
{
group: "alert",
type: "info",
title: "Onboard Someone",
text: ONBOARD_MESSAGE,
},
-1,
);
}
} }
</script> </script>

View File

@@ -118,9 +118,7 @@
<h2 class="text-xl font-bold">Record Something Given By:</h2> <h2 class="text-xl font-bold">Record Something Given By:</h2>
</div> </div>
<ul <ul class="grid grid-cols-4 gap-x-3 gap-y-5 text-center mb-5">
class="grid grid-cols-4 sm:grid-cols-5 md:grid-cols-6 gap-x-3 gap-y-5 text-center mb-5"
>
<li @click="openDialog()"> <li @click="openDialog()">
<img <img
src="../assets/blank-square.svg" src="../assets/blank-square.svg"
@@ -160,7 +158,7 @@
</router-link> </router-link>
<button <button
@click="openGiftedPrompts()" @click="openGiftedPrompts()"
class="block text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-4 py-2 rounded-md" class="block text-center text-md font-bold bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md"
> >
Ideas... Ideas...
</button> </button>
@@ -209,7 +207,7 @@
{{ giveDescription(record) }} {{ giveDescription(record) }}
<a @click="onClickLoadClaim(record.jwtId)"> <a @click="onClickLoadClaim(record.jwtId)">
<fa <fa
icon="file-lines" icon="circle-info"
class="pl-2 text-blue-500 cursor-pointer" class="pl-2 text-blue-500 cursor-pointer"
></fa> ></fa>
</a> </a>

View File

@@ -56,21 +56,19 @@
</div> </div>
<div class="mt-8"> <div class="mt-8">
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2"> <button
<button @click="fromMnemonic()"
@click="fromMnemonic()" class="block w-full text-center text-lg font-bold uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md mb-2"
class="block w-full text-center text-lg font-bold uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md" >
> Import
Import </button>
</button> <button
<button @click="onCancelClick()"
@click="onCancelClick()" type="button"
type="button" class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md"
class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md" >
> Cancel
Cancel </button>
</button>
</div>
</div> </div>
</section> </section>
</template> </template>

View File

@@ -49,21 +49,19 @@
</ul> </ul>
</div> </div>
<div class="mt-8"> <div class="mt-8">
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2"> <button
<button @click="incrementDerivation()"
@click="incrementDerivation()" class="block w-full text-center text-lg font-bold uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md mb-2"
class="block w-full text-center text-lg font-bold uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md" >
> Increment and Import
Increment and Import </button>
</button> <button
<button @click="onCancelClick()"
@click="onCancelClick()" type="button"
type="button" class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md"
class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md" >
> Cancel
Cancel </button>
</button>
</div>
</div> </div>
</section> </section>
</template> </template>

View File

@@ -22,23 +22,21 @@
/> />
<div class="mt-8"> <div class="mt-8">
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2"> <button
<button type="button"
type="button" class="block w-full text-center text-lg font-bold uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md mb-2"
class="block w-full text-center text-lg font-bold uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md mb-2" @click="onClickSaveChanges()"
@click="onClickSaveChanges()" >
> Save Changes
Save Changes </button>
</button> <!-- SHOW ME instead while processing saving changes -->
<!-- SHOW ME instead while processing saving changes --> <button
<button type="button"
type="button" class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md"
class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md" @click="onClickCancel()"
@click="onClickCancel()" >
> Cancel
Cancel </button>
</button>
</div>
</div> </div>
</section> </section>
</template> </template>

View File

@@ -73,17 +73,16 @@
/> />
<label for="includeLocation">Include Location</label> <label for="includeLocation">Include Location</label>
</div> </div>
<div v-if="includeLocation" class="mb-4 aspect-video"> <div v-if="includeLocation" style="height: 600px; width: 800px">
<p class="text-sm mb-2 text-slate-500"> <div class="px-2 py-2">
For your security, choose a location nearby but not exactly at the For your security, choose a location nearby but not exactly at the
place. place.
</p> </div>
<l-map <l-map
ref="map" ref="map"
v-model:zoom="zoom" v-model:zoom="zoom"
:center="[0, 0]" :center="[0, 0]"
class="!z-40 rounded-md"
@click=" @click="
(event) => { (event) => {
latitude = event.latlng.lat; latitude = event.latlng.lat;
@@ -105,30 +104,28 @@
</div> </div>
<div class="mt-8"> <div class="mt-8">
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2"> <button
<button :disabled="isHiddenSave"
:disabled="isHiddenSave" class="block w-full text-center text-lg font-bold uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md mb-2"
class="block w-full text-center text-lg font-bold uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md mb-2" @click="onSaveProjectClick()"
@click="onSaveProjectClick()" >
> <!-- SHOW if in idle state -->
<!-- SHOW if in idle state --> <span :class="{ hidden: isHiddenSave }">Save Project</span>
<span :class="{ hidden: isHiddenSave }">Save Project</span>
<!-- SHOW if in saving state; DISABLE button while in saving state --> <!-- SHOW if in saving state; DISABLE button while in saving state -->
<span :class="{ hidden: isHiddenSpinner }"> <span :class="{ hidden: isHiddenSpinner }">
<!-- icon no worky? --> <!-- icon no worky? -->
<i class="fa-solid fa-spinner fa-spin-pulse"></i> <i class="fa-solid fa-spinner fa-spin-pulse"></i>
Saving...</span Saving...</span
>
</button>
<button
type="button"
class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md"
@click="onCancelClick()"
> >
Cancel </button>
</button> <button
</div> type="button"
class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md"
@click="onCancelClick()"
>
Cancel
</button>
</div> </div>
</section> </section>
</template> </template>
@@ -268,8 +265,6 @@ export default class NewEditProjectView extends Vue {
vcClaim.agent = { vcClaim.agent = {
identifier: this.agentDid, identifier: this.agentDid,
}; };
} else {
delete vcClaim.agent;
} }
if (this.includeLocation) { if (this.includeLocation) {
vcClaim.location = { vcClaim.location = {
@@ -279,8 +274,6 @@ export default class NewEditProjectView extends Vue {
longitude: this.longitude, longitude: this.longitude,
}, },
}; };
} else {
delete vcClaim.location;
} }
// Make a payload for the claim // Make a payload for the claim
const vcPayload = { const vcPayload = {

View File

@@ -98,7 +98,7 @@
</div> </div>
<a @click="onClickLoadClaim(projectId)" class="cursor-pointer"> <a @click="onClickLoadClaim(projectId)" class="cursor-pointer">
<fa icon="file-lines" class="pl-2 pt-1 text-blue-500" /> <fa icon="circle-info" class="pl-2 pt-1 text-blue-500" />
</a> </a>
</div> </div>
@@ -134,9 +134,7 @@
<div class="text-center"> <div class="text-center">
<p class="mt-2 mb-4 text-center">Record a contribution from:</p> <p class="mt-2 mb-4 text-center">Record a contribution from:</p>
</div> </div>
<ul <ul class="grid grid-cols-4 gap-x-3 gap-y-5 text-center mb-5">
class="grid grid-cols-4 sm:grid-cols-5 md:grid-cols-6 gap-x-3 gap-y-5 text-center mb-5"
>
<li @click="openGiftDialog({ name: 'you', did: activeDid })"> <li @click="openGiftDialog({ name: 'you', did: activeDid })">
<fa icon="hand" class="fa-fw text-slate-400 text-5xl" /> <fa icon="hand" class="fa-fw text-slate-400 text-5xl" />
<h3 <h3
@@ -232,7 +230,7 @@
@click="onClickLoadClaim(offer.jwtId as string)" @click="onClickLoadClaim(offer.jwtId as string)"
class="cursor-pointer" class="cursor-pointer"
> >
<fa icon="file-lines" class="pl-2 pt-1 text-blue-500" /> <fa icon="circle-info" class="pl-2 pt-1 text-blue-500" />
</a> </a>
<a <a
v-if="checkIsFulfillable(offer)" v-if="checkIsFulfillable(offer)"
@@ -291,7 +289,7 @@
</div> </div>
<div class="flex justify-between"> <div class="flex justify-between">
<a @click="onClickLoadClaim(give.jwtId)"> <a @click="onClickLoadClaim(give.jwtId)">
<fa icon="file-lines" class="text-blue-500 cursor-pointer" /> <fa icon="circle-info" class="text-blue-500 cursor-pointer" />
</a> </a>
<a v-if="checkIsConfirmable(give)" @click="confirmClaim(give)"> <a v-if="checkIsConfirmable(give)" @click="confirmClaim(give)">
<fa icon="circle-check" class="text-blue-500 cursor-pointer" /> <fa icon="circle-check" class="text-blue-500 cursor-pointer" />

View File

@@ -167,7 +167,7 @@
<a @click="onClickLoadClaim(offer.jwtId)"> <a @click="onClickLoadClaim(offer.jwtId)">
<fa <fa
icon="file-lines" icon="circle-info"
class="pl-2 text-blue-500 cursor-pointer" class="pl-2 text-blue-500 cursor-pointer"
></fa> ></fa>
</a> </a>

View File

@@ -60,7 +60,7 @@
}} }}
<a @click="onClickLoadClaim(record.id)"> <a @click="onClickLoadClaim(record.id)">
<fa <fa
icon="file-lines" icon="circle-info"
class="pl-2 text-blue-500 cursor-pointer" class="pl-2 text-blue-500 cursor-pointer"
/> />
</a> </a>

View File

@@ -64,11 +64,10 @@
</div> </div>
</div> </div>
<div class="mb-4 aspect-video"> <div style="height: 600px; width: 800px">
<l-map <l-map
ref="map" ref="map"
:center="[localCenterLat, localCenterLong]" :center="[localCenterLat, localCenterLong]"
class="!z-40 rounded-md"
v-model:zoom="localZoom" v-model:zoom="localZoom"
@click="setMapPoint" @click="setMapPoint"
> >

View File

@@ -23,40 +23,36 @@
<!-- id used by puppeteer test script --> <!-- id used by puppeteer test script -->
<div id="start-question" class="mt-8"> <div id="start-question" class="mt-8">
<div class="max-w-3xl mx-auto"> <p class="text-center text-xl font-light">
<p class="text-center text-xl font-light"> Do you want a new identifier of your own?
Do you want a new identifier of your own? </p>
</p> <p class="text-center font-light">
<p class="text-center font-light"> If you haven't used this before, click "Yes" to generate a new
If you haven't used this before, click "Yes" to generate a new identifier.
identifier. </p>
</p> <p class="text-center mb-4 font-light">
<p class="text-center mb-4 font-light"> Only click "No" if you have a seed of 12 or 24 words generated
Only click "No" if you have a seed of 12 or 24 words generated elsewhere.
elsewhere. </p>
</p> <a
<a @click="onClickYes()"
@click="onClickYes()" class="block w-full text-center text-lg uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md"
class="block w-full text-center text-lg uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md mb-2" >
> Yes, generate one
Yes, generate one </a>
</a> <a
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2"> @click="onClickNo()"
<a class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md mt-2"
@click="onClickNo()" >
class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md" No, I have a seed
> </a>
No, I have a seed <a
</a> v-if="numAccounts > 0"
<a @click="onClickDerive()"
v-if="numAccounts > 0" class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md mt-2"
@click="onClickDerive()" >
class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md" Derive new address from existing seed
> </a>
Derive new address from existing seed
</a>
</div>
</div>
</div> </div>
</section> </section>
</template> </template>

View File

@@ -1,47 +1,35 @@
{ {
"compilerOptions": { "compilerOptions": {
"allowJs": true, "target": "ES2020", // Latest ECMAScript features that are widely supported by modern browsers
"resolveJsonModule": true, "module": "ESNext", // Use ES modules
"target": "esnext", "strict": true, // Enable all strict type checking options
"module": "esnext", "jsx": "preserve", // Preserves JSX to be transformed by Babel or another transpiler
"strict": true, "moduleResolution": "node", // Use Node.js style module resolution
"strictPropertyInitialization": false, "experimentalDecorators": true,
"jsx": "preserve", "esModuleInterop": true, // Enables compatibility with CommonJS modules for default imports
"moduleResolution": "node", "allowSyntheticDefaultImports": true, // Allow default imports from modules with no default export
"experimentalDecorators": true, "forceConsistentCasingInFileNames": true, // Disallow inconsistently-cased references to the same file
"skipLibCheck": true, "useDefineForClassFields": true,
"esModuleInterop": true, "sourceMap": true,
"allowSyntheticDefaultImports": true, "baseUrl": "./src", // Base directory to resolve non-relative module names
"forceConsistentCasingInFileNames": true, "paths": {
"useDefineForClassFields": true, "@/components/*": ["components/*"],
"sourceMap": true, "@/views/*": ["views/*"],
"baseUrl": "./src", "@/db/*": ["db/*"],
"types": [ "@/libs/*": ["libs/*"],
"webpack-env" "@/constants/*": ["constants/*"],
], "@/store/*": ["store/*"]
"paths": { },
"@/components/*": ["components/*"], "lib": ["ES2020", "dom", "dom.iterable"], // Include typings for ES2020 and DOM APIs
"@/views/*": ["views/*"],
"@/db/*": ["db/*"],
"@/libs/*": ["libs/*"],
"@/constants/*": ["constants/*"],
"@/store/*": ["store/*"],
}, },
"lib": [ "include": [
"esnext", "src/**/*.ts",
"dom", "src/**/*.tsx",
"dom.iterable", "src/**/*.vue",
"scripthost" "tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
] ]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
]
} }

18
vite.config.mjs Normal file
View File

@@ -0,0 +1,18 @@
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import * as path from "path";
// https://vitejs.dev/config/
export default defineConfig({
server: {
port: 8080
},
plugins: [ vue() ],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
buffer: path.resolve(__dirname, 'node_modules', 'buffer'),
'dexie-export-import/dist/import': 'dexie-export-import/dist/import/index.js',
},
},
});

View File

@@ -2,9 +2,9 @@ const { defineConfig } = require("@vue/cli-service");
const { gitDescribeSync } = require("git-describe"); const { gitDescribeSync } = require("git-describe");
const { exec } = require("child_process"); const { exec } = require("child_process");
process.env.VUE_APP_GIT_HASH = gitDescribeSync().hash; import.meta.env.VITE_GIT_HASH = gitDescribeSync().hash;
const TIME_SAFARI_APP_TITLE = const TIME_SAFARI_APP_TITLE =
process.env.TIME_SAFARI_APP_TITLE || require("./package.json").name; import.meta.env.TIME_SAFARI_APP_TITLE || require("./package.json").name;
module.exports = defineConfig({ module.exports = defineConfig({
transpileDependencies: true, transpileDependencies: true,