Compare commits
22 Commits
android-sa
...
electron-b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a92d088432 | ||
| aa346a9abd | |||
| 9ea2f96106 | |||
| 623bf12ecd | |||
|
|
427660d686 | ||
|
|
643f31c43a | ||
| 2c6b787fa2 | |||
|
|
ec53452220 | ||
|
|
ec326495b2 | ||
|
|
cc50c38d13 | ||
| 3969167d92 | |||
|
|
e3cc22245c | ||
|
|
eb44e7b51e | ||
|
|
ca8d72e1c9 | ||
|
|
a4528c5703 | ||
|
|
6acebb66ef | ||
| b3f7026afe | |||
|
|
ec1a725832 | ||
|
|
6d316c2b3f | ||
|
|
24f6730572 | ||
|
|
0fc44b31bf | ||
|
|
bed2c7106a |
29
BUILDING.md
29
BUILDING.md
@@ -251,7 +251,7 @@ npm run build:web:dev # Start development server with hot reload
|
||||
npm run build:web # Development build (starts dev server with hot reload)
|
||||
npm run build:web:test # Test environment build (optimized for testing)
|
||||
npm run build:web:prod # Production build (optimized for production)
|
||||
npm run build:web:serve # Build and serve locally (builds then serves)
|
||||
npm run build:web:serve # Build and serve locally for production testing
|
||||
|
||||
# Docker builds
|
||||
npm run build:web:docker # Development build with Docker containerization
|
||||
@@ -269,6 +269,12 @@ Start the development server using `npm run build:web:dev` or `npm run build:web
|
||||
2. The built files will be in the `dist` directory
|
||||
3. To test the production build locally, use `npm run build:web:serve` (builds then serves)
|
||||
|
||||
**Why Use `serve`?**
|
||||
- **Production Testing**: Test your optimized production build locally before deployment
|
||||
- **SPA Routing Validation**: Verify deep linking and navigation work correctly (handles routes like `/discover`, `/account`)
|
||||
- **Performance Testing**: Test the minified and optimized build locally
|
||||
- **Deployment Validation**: Ensure built files work correctly when served by a real HTTP server
|
||||
|
||||
You'll likely want to use test locations for the Endorser & image & partner servers; see "DEFAULT_ENDORSER_API_SERVER" & "DEFAULT_IMAGE_API_SERVER" & "DEFAULT_PARTNER_API_SERVER" below.
|
||||
|
||||
### Web Build Script Details
|
||||
@@ -288,7 +294,7 @@ All web build commands use the `./scripts/build-web.sh` script, which provides:
|
||||
- **Clean Build**: Removes previous `dist/` directory
|
||||
- **Vite Build**: Executes `npx vite build --config vite.config.web.mts`
|
||||
- **Docker Support**: Optional Docker containerization
|
||||
- **Local Serving**: Built-in HTTP server for testing builds
|
||||
- **Local Serving**: Built-in HTTP server for testing builds with SPA routing support
|
||||
|
||||
**Direct Script Usage:**
|
||||
|
||||
@@ -324,6 +330,25 @@ All web build commands use the `./scripts/build-web.sh` script, which provides:
|
||||
- `5` - Serve command failed
|
||||
- `6` - Invalid build mode
|
||||
|
||||
### Local Serving with `serve`
|
||||
|
||||
The `serve` functionality provides a local HTTP server for testing production builds:
|
||||
|
||||
**What It Does:**
|
||||
1. **Builds** the application using Vite
|
||||
2. **Serves** the built files from the `dist/` directory
|
||||
3. **Handles SPA Routing** - serves `index.html` for all routes (fixes 404s on `/discover`, `/account`, etc.)
|
||||
|
||||
**Server Options:**
|
||||
- **Primary**: `npx serve -s dist -l 8080` (recommended - full SPA support)
|
||||
- **Fallback**: Python HTTP server (limited SPA routing support)
|
||||
|
||||
**Use Cases:**
|
||||
- Testing production builds before deployment
|
||||
- Validating SPA routing behavior
|
||||
- Performance testing of optimized builds
|
||||
- Debugging production build issues locally
|
||||
|
||||
### Compile and minify for test & production
|
||||
|
||||
- If there are DB changes: before updating the test server, open browser(s) with
|
||||
|
||||
@@ -18,7 +18,7 @@ npm install
|
||||
npm run build:web:serve -- --test
|
||||
```
|
||||
|
||||
To be able to make submissions: go to "profile" (bottom left), go to the bottom and expand "Show Advanced Settings", go to the bottom and to the "Test Page", and finally "Become User 0" to see all the functionality.
|
||||
To be able to take action on the platform: go to [the test page](http://localhost:8080/test) and click "Become User 0".
|
||||
|
||||
See [BUILDING.md](BUILDING.md) for comprehensive build instructions for all platforms (Web, Electron, iOS, Android, Docker).
|
||||
|
||||
|
||||
596
package-lock.json
generated
596
package-lock.json
generated
@@ -138,6 +138,7 @@
|
||||
"postcss": "^8.4.38",
|
||||
"prettier": "^3.2.5",
|
||||
"rimraf": "^6.0.1",
|
||||
"serve": "^14.2.4",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"ts-jest": "^29.4.0",
|
||||
"tsx": "^4.20.4",
|
||||
@@ -11760,6 +11761,13 @@
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@zeit/schemas": {
|
||||
"version": "2.36.0",
|
||||
"resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.36.0.tgz",
|
||||
"integrity": "sha512-7kjMwcChYEzMKjeex9ZFXkt1AyNov9R5HZtjBKVsmVpw7pa7ZtlCGvCBC2vnnXctaYN+aRI61HjIqeetZW5ROg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@zxing/text-encoding": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz",
|
||||
@@ -11822,9 +11830,8 @@
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"mime-types": "~2.1.34",
|
||||
"negotiator": "0.6.3"
|
||||
@@ -11949,6 +11956,16 @@
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/ansi-align": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
|
||||
"integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"string-width": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-escapes": {
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
|
||||
@@ -12153,6 +12170,27 @@
|
||||
"license": "ISC",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/arch": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz",
|
||||
"integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/are-we-there-yet": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz",
|
||||
@@ -12949,6 +12987,153 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/boxen": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.0.tgz",
|
||||
"integrity": "sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-align": "^3.0.1",
|
||||
"camelcase": "^7.0.0",
|
||||
"chalk": "^5.0.1",
|
||||
"cli-boxes": "^3.0.0",
|
||||
"string-width": "^5.1.2",
|
||||
"type-fest": "^2.13.0",
|
||||
"widest-line": "^4.0.1",
|
||||
"wrap-ansi": "^8.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/boxen/node_modules/ansi-regex": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz",
|
||||
"integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/boxen/node_modules/ansi-styles": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
||||
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/boxen/node_modules/camelcase": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz",
|
||||
"integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/boxen/node_modules/chalk": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.0.tgz",
|
||||
"integrity": "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^12.17.0 || ^14.13 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/boxen/node_modules/emoji-regex": {
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/boxen/node_modules/string-width": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"eastasianwidth": "^0.2.0",
|
||||
"emoji-regex": "^9.2.2",
|
||||
"strip-ansi": "^7.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/boxen/node_modules/strip-ansi": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/boxen/node_modules/type-fest": {
|
||||
"version": "2.19.0",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
|
||||
"integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==",
|
||||
"dev": true,
|
||||
"license": "(MIT OR CC0-1.0)",
|
||||
"engines": {
|
||||
"node": ">=12.20"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/boxen/node_modules/wrap-ansi": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
|
||||
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^6.1.0",
|
||||
"string-width": "^5.0.1",
|
||||
"strip-ansi": "^7.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/bplist-creator": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.0.tgz",
|
||||
@@ -13735,6 +13920,22 @@
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk-template": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz",
|
||||
"integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chalk": "^4.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk-template?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/char-regex": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
|
||||
@@ -13959,6 +14160,19 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/cli-boxes": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz",
|
||||
"integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/cli-cursor": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
|
||||
@@ -14015,6 +14229,24 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/clipboardy": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-3.0.0.tgz",
|
||||
"integrity": "sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"arch": "^2.2.0",
|
||||
"execa": "^5.1.1",
|
||||
"is-wsl": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/cliui": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||
@@ -14181,9 +14413,8 @@
|
||||
"version": "2.0.18",
|
||||
"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
|
||||
"integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"mime-db": ">= 1.43.0 < 2"
|
||||
},
|
||||
@@ -14457,6 +14688,16 @@
|
||||
"license": "ISC",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/content-disposition": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
|
||||
"integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/conventional-changelog": {
|
||||
"version": "3.1.25",
|
||||
"resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.25.tgz",
|
||||
@@ -19249,6 +19490,19 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-port-reachable": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-port-reachable/-/is-port-reachable-4.0.0.tgz",
|
||||
"integrity": "sha512-9UoipoxYmSk6Xy7QFgRv2HDyaysmgSG75TFQs6S+3pDM7ZhKTF/bskZV+0UlABHzKjNVhPjYCLfeZUEg1wXxig==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-stream": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
|
||||
@@ -24440,9 +24694,8 @@
|
||||
"version": "1.54.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
||||
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
@@ -25906,6 +26159,13 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/path-is-inside": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
|
||||
"integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==",
|
||||
"dev": true,
|
||||
"license": "(WTFPL OR MIT)"
|
||||
},
|
||||
"node_modules/path-key": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||
@@ -25947,6 +26207,13 @@
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz",
|
||||
"integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/path-type": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||
@@ -28112,6 +28379,30 @@
|
||||
"integrity": "sha512-CiD3ZSanZqcMPRhtfct5K9f7i3OLCcBBWsJjLh1gW9RO/nS94sVzY59iS+fgYBOBqaBpf4EzfqUF3j9IG+xo8A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/registry-auth-token": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz",
|
||||
"integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"rc": "^1.1.6",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/registry-url": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz",
|
||||
"integrity": "sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"rc": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/regjsgen": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz",
|
||||
@@ -29220,6 +29511,115 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/serve": {
|
||||
"version": "14.2.4",
|
||||
"resolved": "https://registry.npmjs.org/serve/-/serve-14.2.4.tgz",
|
||||
"integrity": "sha512-qy1S34PJ/fcY8gjVGszDB3EXiPSk5FKhUa7tQe0UPRddxRidc2V6cNHPNewbE1D7MAkgLuWEt3Vw56vYy73tzQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@zeit/schemas": "2.36.0",
|
||||
"ajv": "8.12.0",
|
||||
"arg": "5.0.2",
|
||||
"boxen": "7.0.0",
|
||||
"chalk": "5.0.1",
|
||||
"chalk-template": "0.4.0",
|
||||
"clipboardy": "3.0.0",
|
||||
"compression": "1.7.4",
|
||||
"is-port-reachable": "4.0.0",
|
||||
"serve-handler": "6.1.6",
|
||||
"update-check": "1.5.4"
|
||||
},
|
||||
"bin": {
|
||||
"serve": "build/main.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/serve-handler": {
|
||||
"version": "6.1.6",
|
||||
"resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.6.tgz",
|
||||
"integrity": "sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "3.0.0",
|
||||
"content-disposition": "0.5.2",
|
||||
"mime-types": "2.1.18",
|
||||
"minimatch": "3.1.2",
|
||||
"path-is-inside": "1.0.2",
|
||||
"path-to-regexp": "3.3.0",
|
||||
"range-parser": "1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/serve-handler/node_modules/brace-expansion": {
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/serve-handler/node_modules/bytes": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
|
||||
"integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/serve-handler/node_modules/mime-db": {
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
|
||||
"integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/serve-handler/node_modules/mime-types": {
|
||||
"version": "2.1.18",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
|
||||
"integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "~1.33.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/serve-handler/node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/serve-handler/node_modules/range-parser": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
|
||||
"integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/serve-static": {
|
||||
"version": "1.16.2",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
|
||||
@@ -29351,6 +29751,106 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/serve/node_modules/ajv": {
|
||||
"version": "8.12.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
|
||||
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/serve/node_modules/bytes": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
|
||||
"integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/serve/node_modules/chalk": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz",
|
||||
"integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^12.17.0 || ^14.13 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/serve/node_modules/compression": {
|
||||
"version": "1.7.4",
|
||||
"resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz",
|
||||
"integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.5",
|
||||
"bytes": "3.0.0",
|
||||
"compressible": "~2.0.16",
|
||||
"debug": "2.6.9",
|
||||
"on-headers": "~1.0.2",
|
||||
"safe-buffer": "5.1.2",
|
||||
"vary": "~1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/serve/node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/serve/node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/serve/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/serve/node_modules/on-headers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
|
||||
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/serve/node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/set-blocking": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||
@@ -31680,6 +32180,17 @@
|
||||
"browserslist": ">= 4.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/update-check": {
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/update-check/-/update-check-1.5.4.tgz",
|
||||
"integrity": "sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"registry-auth-token": "3.3.2",
|
||||
"registry-url": "3.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uri-js": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
||||
@@ -31788,9 +32299,8 @@
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
@@ -32642,6 +33152,76 @@
|
||||
"string-width": "^1.0.2 || 2 || 3 || 4"
|
||||
}
|
||||
},
|
||||
"node_modules/widest-line": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz",
|
||||
"integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"string-width": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/widest-line/node_modules/ansi-regex": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz",
|
||||
"integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/widest-line/node_modules/emoji-regex": {
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/widest-line/node_modules/string-width": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"eastasianwidth": "^0.2.0",
|
||||
"emoji-regex": "^9.2.2",
|
||||
"strip-ansi": "^7.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/widest-line/node_modules/strip-ansi": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wonka": {
|
||||
"version": "6.3.5",
|
||||
"resolved": "https://registry.npmjs.org/wonka/-/wonka-6.3.5.tgz",
|
||||
|
||||
@@ -268,6 +268,7 @@
|
||||
"postcss": "^8.4.38",
|
||||
"prettier": "^3.2.5",
|
||||
"rimraf": "^6.0.1",
|
||||
"serve": "^14.2.4",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"ts-jest": "^29.4.0",
|
||||
"tsx": "^4.20.4",
|
||||
|
||||
@@ -22,7 +22,7 @@ projects, and special entities with selection. * * @author Matthew Raymer */
|
||||
<!-- "Unnamed" entity -->
|
||||
<SpecialEntityCard
|
||||
entity-type="unnamed"
|
||||
label="Unnamed"
|
||||
:label="unnamedEntityName"
|
||||
icon="circle-question"
|
||||
:entity-data="unnamedEntityData"
|
||||
:notify="notify"
|
||||
@@ -83,6 +83,7 @@ import ShowAllCard from "./ShowAllCard.vue";
|
||||
import { Contact } from "../db/tables/contacts";
|
||||
import { PlanData } from "../interfaces/records";
|
||||
import { NotificationIface } from "../constants/app";
|
||||
import { UNNAMED_ENTITY_NAME } from "@/constants/entities";
|
||||
|
||||
/**
|
||||
* EntityGrid - Unified grid layout for displaying people or projects
|
||||
@@ -159,6 +160,10 @@ export default class EntityGrid extends Vue {
|
||||
@Prop({ default: "other party" })
|
||||
conflictContext!: string;
|
||||
|
||||
/** Whether to hide the "Show All" navigation */
|
||||
@Prop({ default: false })
|
||||
hideShowAll!: boolean;
|
||||
|
||||
/**
|
||||
* Function to determine which entities to display (allows parent control)
|
||||
*
|
||||
@@ -245,7 +250,9 @@ export default class EntityGrid extends Vue {
|
||||
* Whether to show the "Show All" navigation
|
||||
*/
|
||||
get shouldShowAll(): boolean {
|
||||
return this.entities.length > 0 && this.showAllRoute !== "";
|
||||
return (
|
||||
!this.hideShowAll && this.entities.length > 0 && this.showAllRoute !== ""
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -271,10 +278,17 @@ export default class EntityGrid extends Vue {
|
||||
get unnamedEntityData(): { did: string; name: string } {
|
||||
return {
|
||||
did: "",
|
||||
name: "Unnamed",
|
||||
name: UNNAMED_ENTITY_NAME,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the unnamed entity name constant
|
||||
*/
|
||||
get unnamedEntityName(): string {
|
||||
return UNNAMED_ENTITY_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a person DID is conflicted
|
||||
*/
|
||||
@@ -304,16 +318,13 @@ export default class EntityGrid extends Vue {
|
||||
|
||||
/**
|
||||
* Handle special entity selection from SpecialEntityCard
|
||||
* Treat "You" and "Unnamed" as person entities
|
||||
*/
|
||||
handleEntitySelected(event: {
|
||||
type: string;
|
||||
entityType: string;
|
||||
data: { did?: string; name: string };
|
||||
}): void {
|
||||
handleEntitySelected(event: { data: { did?: string; name: string } }): void {
|
||||
// Convert special entities to person entities since they represent people
|
||||
this.emitEntitySelected({
|
||||
type: "special",
|
||||
entityType: event.entityType,
|
||||
data: event.data,
|
||||
type: "person",
|
||||
data: event.data as Contact,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -321,13 +332,11 @@ export default class EntityGrid extends Vue {
|
||||
|
||||
@Emit("entity-selected")
|
||||
emitEntitySelected(data: {
|
||||
type: "person" | "project" | "special";
|
||||
entityType?: string;
|
||||
data: Contact | PlanData | { did?: string; name: string };
|
||||
type: "person" | "project";
|
||||
data: Contact | PlanData;
|
||||
}): {
|
||||
type: "person" | "project" | "special";
|
||||
entityType?: string;
|
||||
data: Contact | PlanData | { did?: string; name: string };
|
||||
type: "person" | "project";
|
||||
data: Contact | PlanData;
|
||||
} {
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ Matthew Raymer */
|
||||
:show-all-query-params="showAllQueryParams"
|
||||
:notify="notify"
|
||||
:conflict-context="conflictContext"
|
||||
:hide-show-all="hideShowAll"
|
||||
@entity-selected="handleEntitySelected"
|
||||
/>
|
||||
|
||||
@@ -55,9 +56,8 @@ interface EntityData {
|
||||
* Entity selection event data structure
|
||||
*/
|
||||
interface EntitySelectionEvent {
|
||||
type: "person" | "project" | "special";
|
||||
entityType?: string;
|
||||
data: Contact | PlanData | EntityData;
|
||||
type: "person" | "project";
|
||||
data: Contact | PlanData;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,6 +154,10 @@ export default class EntitySelectionStep extends Vue {
|
||||
@Prop()
|
||||
notify?: (notification: NotificationIface, timeout?: number) => void;
|
||||
|
||||
/** Whether to hide the "Show All" navigation */
|
||||
@Prop({ default: false })
|
||||
hideShowAll!: boolean;
|
||||
|
||||
/**
|
||||
* CSS classes for the cancel button
|
||||
*/
|
||||
|
||||
@@ -42,8 +42,8 @@ computed CSS properties * * @author Matthew Raymer */
|
||||
<p class="text-xs text-slate-500 leading-1 -mb-1 uppercase">
|
||||
{{ label }}
|
||||
</p>
|
||||
<h3 class="font-semibold truncate">
|
||||
{{ entity?.name || "Unnamed" }}
|
||||
<h3 :class="nameClasses">
|
||||
{{ displayName }}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
@@ -62,6 +62,7 @@ import { Component, Prop, Vue } from "vue-facing-decorator";
|
||||
import EntityIcon from "./EntityIcon.vue";
|
||||
import ProjectIcon from "./ProjectIcon.vue";
|
||||
import { Contact } from "../db/tables/contacts";
|
||||
import { UNNAMED_ENTITY_NAME } from "@/constants/entities";
|
||||
|
||||
/**
|
||||
* Entity interface for both person and project entities
|
||||
@@ -138,6 +139,38 @@ export default class EntitySummaryButton extends Vue {
|
||||
return this.editable ? "text-blue-500" : "text-slate-400";
|
||||
}
|
||||
|
||||
/**
|
||||
* Computed CSS classes for the entity name
|
||||
*/
|
||||
get nameClasses(): string {
|
||||
const baseClasses = "font-semibold truncate";
|
||||
|
||||
// Add italic styling for special "Unnamed" or entities without set names
|
||||
if (!this.entity?.name || this.entity?.did === "") {
|
||||
return `${baseClasses} italic text-slate-500`;
|
||||
}
|
||||
|
||||
return baseClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computed display name for the entity
|
||||
*/
|
||||
get displayName(): string {
|
||||
// If the entity has a set name, use that name
|
||||
if (this.entity?.name) {
|
||||
return this.entity.name;
|
||||
}
|
||||
|
||||
// If the entity is the special "Unnamed", use "Unnamed"
|
||||
if (this.entity?.did === "") {
|
||||
return UNNAMED_ENTITY_NAME;
|
||||
}
|
||||
|
||||
// If the entity does not have a set name, but is not the special "Unnamed", use their DID
|
||||
return this.entity?.did;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle click event - only call function prop if editable
|
||||
* Allows parent to control edit behavior and validation
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
:unit-code="unitCode"
|
||||
:offer-id="offerId"
|
||||
:notify="$notify"
|
||||
:hide-show-all="hideShowAll"
|
||||
@entity-selected="handleEntitySelected"
|
||||
@cancel="cancel"
|
||||
/>
|
||||
@@ -87,6 +88,7 @@ import {
|
||||
NOTIFY_GIFTED_DETAILS_NO_IDENTIFIER,
|
||||
NOTIFY_GIFTED_DETAILS_RECORDING_GIVE,
|
||||
} from "@/constants/notifications";
|
||||
import { UNNAMED_ENTITY_NAME } from "@/constants/entities";
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
@@ -114,6 +116,7 @@ export default class GiftedDialog extends Vue {
|
||||
@Prop() fromProjectId = "";
|
||||
@Prop() toProjectId = "";
|
||||
@Prop() isFromProjectView = false;
|
||||
@Prop() hideShowAll = false;
|
||||
@Prop({ default: "person" }) giverEntityType = "person" as
|
||||
| "person"
|
||||
| "project";
|
||||
@@ -224,15 +227,6 @@ export default class GiftedDialog extends Vue {
|
||||
|
||||
this.allMyDids = await retrieveAccountDids();
|
||||
|
||||
if (this.giver && !this.giver.name) {
|
||||
this.giver.name = didInfo(
|
||||
this.giver.did,
|
||||
this.activeDid,
|
||||
this.allMyDids,
|
||||
this.allContacts,
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
this.giverEntityType === "project" ||
|
||||
this.recipientEntityType === "project"
|
||||
@@ -455,14 +449,14 @@ export default class GiftedDialog extends Vue {
|
||||
if (contact) {
|
||||
this.giver = {
|
||||
did: contact.did,
|
||||
name: contact.name || contact.did,
|
||||
name: contact.name,
|
||||
};
|
||||
} else {
|
||||
// Only set to "Unnamed" if no giver is currently set
|
||||
if (!this.giver || !this.giver.did) {
|
||||
this.giver = {
|
||||
did: "",
|
||||
name: "Unnamed",
|
||||
name: UNNAMED_ENTITY_NAME,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -517,14 +511,14 @@ export default class GiftedDialog extends Vue {
|
||||
if (contact) {
|
||||
this.receiver = {
|
||||
did: contact.did,
|
||||
name: contact.name || contact.did,
|
||||
name: contact.name,
|
||||
};
|
||||
} else {
|
||||
// Only set to "Unnamed" if no receiver is currently set
|
||||
if (!this.receiver || !this.receiver.did) {
|
||||
this.receiver = {
|
||||
did: "",
|
||||
name: "Unnamed",
|
||||
name: UNNAMED_ENTITY_NAME,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -566,20 +560,21 @@ export default class GiftedDialog extends Vue {
|
||||
|
||||
/**
|
||||
* Handle entity selection from EntitySelectionStep
|
||||
* @param entity - The selected entity (person, project, or special) with stepType
|
||||
* @param entity - The selected entity (person or project) with stepType
|
||||
*/
|
||||
handleEntitySelected(entity: {
|
||||
type: "person" | "project" | "special";
|
||||
entityType?: string;
|
||||
data: Contact | PlanData | { did?: string; name: string };
|
||||
type: "person" | "project";
|
||||
data: Contact | PlanData;
|
||||
stepType: string;
|
||||
}) {
|
||||
if (entity.type === "person") {
|
||||
const contact = entity.data as Contact;
|
||||
// Apply DID-based logic for person entities
|
||||
const processedContact = this.processPersonEntity(contact);
|
||||
if (entity.stepType === "giver") {
|
||||
this.selectGiver(contact);
|
||||
this.selectGiver(processedContact);
|
||||
} else {
|
||||
this.selectRecipient(contact);
|
||||
this.selectRecipient(processedContact);
|
||||
}
|
||||
} else if (entity.type === "project") {
|
||||
const project = entity.data as PlanData;
|
||||
@@ -588,33 +583,22 @@ export default class GiftedDialog extends Vue {
|
||||
} else {
|
||||
this.selectRecipientProject(project);
|
||||
}
|
||||
} else if (entity.type === "special") {
|
||||
// Handle special entities like "You" and "Unnamed"
|
||||
if (entity.entityType === "you") {
|
||||
// "You" entity selected
|
||||
const youEntity = {
|
||||
did: this.activeDid,
|
||||
name: "You",
|
||||
};
|
||||
if (entity.stepType === "giver") {
|
||||
this.giver = youEntity;
|
||||
} else {
|
||||
this.receiver = youEntity;
|
||||
}
|
||||
this.firstStep = false;
|
||||
} else if (entity.entityType === "unnamed") {
|
||||
// "Unnamed" entity selected
|
||||
const unnamedEntity = {
|
||||
did: "",
|
||||
name: "Unnamed",
|
||||
};
|
||||
if (entity.stepType === "giver") {
|
||||
this.giver = unnamedEntity;
|
||||
} else {
|
||||
this.receiver = unnamedEntity;
|
||||
}
|
||||
this.firstStep = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes person entities using DID-based logic for "You" and "Unnamed"
|
||||
*/
|
||||
private processPersonEntity(contact: Contact): Contact {
|
||||
if (contact.did === this.activeDid) {
|
||||
// If DID matches active DID, create "You" entity
|
||||
return { ...contact, name: "You" };
|
||||
} else if (!contact.did || contact.did === "") {
|
||||
// If DID is empty/null, create "Unnamed" entity
|
||||
return { ...contact, name: UNNAMED_ENTITY_NAME };
|
||||
} else {
|
||||
// Return the contact as-is
|
||||
return contact;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center">
|
||||
<h3 class="text-lg font-medium">
|
||||
{{ member.name || "Unnamed Member" }}
|
||||
{{ member.name || unnamedMember }}
|
||||
</h3>
|
||||
<div
|
||||
v-if="!getContactFor(member.did) && member.did !== activeDid"
|
||||
@@ -177,6 +177,7 @@ import {
|
||||
NOTIFY_ADD_CONTACT_FIRST,
|
||||
NOTIFY_CONTINUE_WITHOUT_ADDING,
|
||||
} from "@/constants/notifications";
|
||||
import { SOMEONE_UNNAMED } from "@/constants/entities";
|
||||
|
||||
interface Member {
|
||||
admitted: boolean;
|
||||
@@ -220,6 +221,13 @@ export default class MembersList extends Vue {
|
||||
apiServer = "";
|
||||
contacts: Array<Contact> = [];
|
||||
|
||||
/**
|
||||
* Get the unnamed member constant
|
||||
*/
|
||||
get unnamedMember(): string {
|
||||
return SOMEONE_UNNAMED;
|
||||
}
|
||||
|
||||
async created() {
|
||||
this.notify = createNotifyHelpers(this.$notify);
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ conflict detection. * * @author Matthew Raymer */
|
||||
</div>
|
||||
|
||||
<h3 :class="nameClasses">
|
||||
{{ person.name || person.did || "Unnamed" }}
|
||||
{{ displayName }}
|
||||
</h3>
|
||||
</li>
|
||||
</template>
|
||||
@@ -98,9 +98,27 @@ export default class PersonCard extends Vue {
|
||||
return `${baseClasses} text-slate-400`;
|
||||
}
|
||||
|
||||
// Add italic styling for entities without set names
|
||||
if (!this.person.name) {
|
||||
return `${baseClasses} italic text-slate-500`;
|
||||
}
|
||||
|
||||
return baseClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computed display name for the person
|
||||
*/
|
||||
get displayName(): string {
|
||||
// If the entity has a set name, use that name
|
||||
if (this.person.name) {
|
||||
return this.person.name;
|
||||
}
|
||||
|
||||
// If the entity does not have a set name
|
||||
return this.person.did;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle card click - emit if selectable and not conflicted, show warning if conflicted
|
||||
*/
|
||||
@@ -114,7 +132,7 @@ export default class PersonCard extends Vue {
|
||||
group: "alert",
|
||||
type: "warning",
|
||||
title: "Cannot Select",
|
||||
text: `You cannot select "${this.person.name || this.person.did || "Unnamed"}" because they are already selected as the ${this.conflictContext}.`,
|
||||
text: `You cannot select "${this.displayName}" because they are already selected as the ${this.conflictContext}.`,
|
||||
},
|
||||
3000,
|
||||
);
|
||||
|
||||
@@ -15,7 +15,7 @@ issuer information. * * @author Matthew Raymer */
|
||||
<h3
|
||||
class="text-xs font-medium text-ellipsis whitespace-nowrap overflow-hidden"
|
||||
>
|
||||
{{ project.name || "Unnamed Project" }}
|
||||
{{ project.name || unnamedProject }}
|
||||
</h3>
|
||||
|
||||
<div class="text-xs text-slate-500 truncate">
|
||||
@@ -31,6 +31,7 @@ import ProjectIcon from "./ProjectIcon.vue";
|
||||
import { PlanData } from "../interfaces/records";
|
||||
import { Contact } from "../db/tables/contacts";
|
||||
import { didInfo } from "../libs/endorserServer";
|
||||
import { UNNAMED_PROJECT } from "@/constants/entities";
|
||||
|
||||
/**
|
||||
* ProjectCard - Displays a project entity with selection capability
|
||||
@@ -63,6 +64,13 @@ export default class ProjectCard extends Vue {
|
||||
@Prop({ required: true })
|
||||
allContacts!: Contact[];
|
||||
|
||||
/**
|
||||
* Get the unnamed project constant
|
||||
*/
|
||||
get unnamedProject(): string {
|
||||
return UNNAMED_PROJECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computed display name for the project issuer
|
||||
*/
|
||||
|
||||
@@ -115,6 +115,7 @@ import { urlBase64ToUint8Array } from "../libs/crypto/vc/util";
|
||||
import * as libsUtil from "../libs/util";
|
||||
import { logger } from "../utils/logger";
|
||||
import { PlatformServiceMixin } from "../utils/PlatformServiceMixin";
|
||||
import { UNNAMED_ENTITY_NAME } from "@/constants/entities";
|
||||
|
||||
// Example interface for error
|
||||
interface ErrorResponse {
|
||||
@@ -602,7 +603,7 @@ export default class PushNotificationPermission extends Vue {
|
||||
* Returns the default message for direct push
|
||||
*/
|
||||
get notificationMessagePlaceholder(): string {
|
||||
return "Click to share some gratitude with the world -- even if they're unnamed.";
|
||||
return `Click to share some gratitude with the world -- even if they're ${UNNAMED_ENTITY_NAME.toLowerCase()}.`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -124,8 +124,6 @@ export default class SpecialEntityCard extends Vue {
|
||||
handleClick(): void {
|
||||
if (this.selectable && !this.conflicted) {
|
||||
this.emitEntitySelected({
|
||||
type: "special",
|
||||
entityType: this.entityType,
|
||||
data: this.entityData,
|
||||
});
|
||||
} else if (this.conflicted && this.notify) {
|
||||
@@ -145,13 +143,7 @@ export default class SpecialEntityCard extends Vue {
|
||||
// Emit methods using @Emit decorator
|
||||
|
||||
@Emit("entity-selected")
|
||||
emitEntitySelected(data: {
|
||||
type: string;
|
||||
entityType: string;
|
||||
data: { did?: string; name: string };
|
||||
}): {
|
||||
type: string;
|
||||
entityType: string;
|
||||
emitEntitySelected(data: { data: { did?: string; name: string } }): {
|
||||
data: { did?: string; name: string };
|
||||
} {
|
||||
return data;
|
||||
|
||||
14
src/constants/entities.ts
Normal file
14
src/constants/entities.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Constants for entity-related strings, particularly for unnamed/unknown person entities
|
||||
*/
|
||||
|
||||
// Core unnamed entity names
|
||||
export const UNNAMED_ENTITY_NAME = "Unnamed";
|
||||
|
||||
// Descriptive phrases for unnamed entities
|
||||
export const SOMEONE_UNNAMED = "Someone Unnamed";
|
||||
export const THAT_UNNAMED_PERSON = "That unnamed person";
|
||||
export const UNNAMED_PERSON = "unnamed person";
|
||||
|
||||
// Project-related unnamed entities
|
||||
export const UNNAMED_PROJECT = "Unnamed Project";
|
||||
@@ -1,4 +1,5 @@
|
||||
import axios from "axios";
|
||||
import { THAT_UNNAMED_PERSON } from "./entities";
|
||||
|
||||
// Notification message constants for user-facing notifications
|
||||
// Add new notification messages here as needed
|
||||
@@ -873,7 +874,7 @@ export const NOTIFY_CONTACT_LINK_COPIED = {
|
||||
// Template for registration success message
|
||||
// Used in: ContactsView.vue (register method - registration success with contact name)
|
||||
export const getRegisterPersonSuccessMessage = (name?: string): string =>
|
||||
`${name || "That unnamed person"} ${NOTIFY_REGISTER_PERSON_SUCCESS.message}`;
|
||||
`${name || THAT_UNNAMED_PERSON} ${NOTIFY_REGISTER_PERSON_SUCCESS.message}`;
|
||||
|
||||
// Template for visibility success message
|
||||
// Used in: ContactsView.vue (setVisibility method - visibility success with contact name)
|
||||
@@ -1378,7 +1379,7 @@ export function createQRContactAddedMessage(hasVisibility: boolean): string {
|
||||
export function createQRRegistrationSuccessMessage(
|
||||
contactName: string,
|
||||
): string {
|
||||
return `${contactName || "That unnamed person"}${NOTIFY_QR_REGISTRATION_SUCCESS.message}`;
|
||||
return `${contactName || THAT_UNNAMED_PERSON}${NOTIFY_QR_REGISTRATION_SUCCESS.message}`;
|
||||
}
|
||||
|
||||
// ContactQRScanShowView.vue timeout constants
|
||||
|
||||
@@ -60,6 +60,7 @@ import { PlanSummaryRecord } from "../interfaces/records";
|
||||
import { logger } from "../utils/logger";
|
||||
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
|
||||
import { APP_SERVER } from "@/constants/app";
|
||||
import { SOMEONE_UNNAMED } from "@/constants/entities";
|
||||
|
||||
/**
|
||||
* Standard context for schema.org data
|
||||
@@ -309,7 +310,7 @@ export function didInfoForContact(
|
||||
showDidForVisible: boolean = false,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
): { known: boolean; displayName: string; profileImageUrl?: string } {
|
||||
if (!did) return { displayName: "Someone Not Named", known: false };
|
||||
if (!did) return { displayName: SOMEONE_UNNAMED, known: false };
|
||||
if (did === activeDid) {
|
||||
return { displayName: "You", known: true };
|
||||
} else if (contact) {
|
||||
|
||||
@@ -33,6 +33,7 @@ import { logger } from "../utils/logger";
|
||||
import { PlatformServiceFactory } from "../services/PlatformServiceFactory";
|
||||
import { IIdentifier } from "@veramo/core";
|
||||
import { DEFAULT_ROOT_DERIVATION_PATH } from "./crypto";
|
||||
import { UNNAMED_PERSON } from "@/constants/entities";
|
||||
|
||||
// Consolidate this with src/utils/PlatformServiceMixin.mapQueryResultToValues
|
||||
function mapQueryResultToValues(
|
||||
@@ -192,7 +193,7 @@ export const nameForContact = (
|
||||
): string => {
|
||||
return (
|
||||
(contact?.name as string) ||
|
||||
(capitalize ? "This" : "this") + " unnamed user"
|
||||
(capitalize ? "This" : "this") + " " + UNNAMED_PERSON
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -74,13 +74,13 @@ export class DeepLinkHandler {
|
||||
* @throws {DeepLinkError} If validation fails or route is invalid
|
||||
*/
|
||||
async handleDeepLink(url: string): Promise<void> {
|
||||
logger.info(`[DeepLink] 🚀 Starting deeplink processing for URL: ${url}`);
|
||||
logger.debug(`[DeepLink] 🚀 Starting deeplink processing for URL: ${url}`);
|
||||
|
||||
try {
|
||||
logger.info(`[DeepLink] 📍 Parsing URL: ${url}`);
|
||||
logger.debug(`[DeepLink] 📍 Parsing URL: ${url}`);
|
||||
const { path, params, query } = this.parseDeepLink(url);
|
||||
|
||||
logger.info(`[DeepLink] ✅ URL parsed successfully:`, {
|
||||
logger.debug(`[DeepLink] ✅ URL parsed successfully:`, {
|
||||
path,
|
||||
params: Object.keys(params),
|
||||
query: Object.keys(query),
|
||||
@@ -93,10 +93,10 @@ export class DeepLinkHandler {
|
||||
Object.entries(params).map(([key, value]) => [key, value ?? ""]),
|
||||
);
|
||||
|
||||
logger.info(`[DeepLink] 🧹 Parameters sanitized:`, sanitizedParams);
|
||||
logger.debug(`[DeepLink] 🧹 Parameters sanitized:`, sanitizedParams);
|
||||
|
||||
await this.validateAndRoute(path, sanitizedParams, query);
|
||||
logger.info(`[DeepLink] 🎯 Deeplink processing completed successfully`);
|
||||
logger.debug(`[DeepLink] 🎯 Deeplink processing completed successfully`);
|
||||
} catch (error) {
|
||||
logger.error(`[DeepLink] ❌ Deeplink processing failed:`, {
|
||||
url,
|
||||
@@ -159,7 +159,7 @@ export class DeepLinkHandler {
|
||||
logger.debug(`[DeepLink] 🔗 Query parameters extracted:`, query);
|
||||
}
|
||||
|
||||
logger.info(`[DeepLink] ✅ Parse completed:`, {
|
||||
logger.debug(`[DeepLink] ✅ Parse completed:`, {
|
||||
routePath,
|
||||
pathParams: pathParams.length,
|
||||
queryParams: Object.keys(query).length,
|
||||
@@ -186,7 +186,7 @@ export class DeepLinkHandler {
|
||||
params: Record<string, string>,
|
||||
query: Record<string, string>,
|
||||
): Promise<void> {
|
||||
logger.info(
|
||||
logger.debug(
|
||||
`[DeepLink] 🎯 Starting validation and routing for path: ${path}`,
|
||||
);
|
||||
|
||||
@@ -197,11 +197,11 @@ export class DeepLinkHandler {
|
||||
logger.debug(`[DeepLink] 🔍 Validating route path: ${path}`);
|
||||
// Validate route exists
|
||||
const validRoute = routeSchema.parse(path) as DeepLinkRoute;
|
||||
logger.info(`[DeepLink] ✅ Route validation passed: ${validRoute}`);
|
||||
logger.debug(`[DeepLink] ✅ Route validation passed: ${validRoute}`);
|
||||
|
||||
// Get route configuration
|
||||
const routeConfig = ROUTE_MAP[validRoute];
|
||||
logger.info(`[DeepLink] 📋 Route config retrieved:`, routeConfig);
|
||||
logger.debug(`[DeepLink] 📋 Route config retrieved:`, routeConfig);
|
||||
|
||||
if (!routeConfig) {
|
||||
logger.error(`[DeepLink] ❌ No route config found for: ${validRoute}`);
|
||||
@@ -209,7 +209,7 @@ export class DeepLinkHandler {
|
||||
}
|
||||
|
||||
routeName = routeConfig.name;
|
||||
logger.info(`[DeepLink] 🎯 Route name resolved: ${routeName}`);
|
||||
logger.debug(`[DeepLink] 🎯 Route name resolved: ${routeName}`);
|
||||
} catch (error) {
|
||||
logger.error(`[DeepLink] ❌ Route validation failed:`, {
|
||||
path,
|
||||
@@ -228,14 +228,14 @@ export class DeepLinkHandler {
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(
|
||||
logger.debug(
|
||||
`[DeepLink] 🔄 Redirected to error page for invalid route: ${path}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Continue with parameter validation
|
||||
logger.info(
|
||||
logger.debug(
|
||||
`[DeepLink] 🔍 Starting parameter validation for route: ${routeName}`,
|
||||
);
|
||||
|
||||
@@ -258,7 +258,7 @@ export class DeepLinkHandler {
|
||||
if (pathSchema) {
|
||||
logger.debug(`[DeepLink] 🔍 Validating path parameters:`, params);
|
||||
validatedPathParams = await pathSchema.parseAsync(params);
|
||||
logger.info(
|
||||
logger.debug(
|
||||
`[DeepLink] ✅ Path parameters validated:`,
|
||||
validatedPathParams,
|
||||
);
|
||||
@@ -270,7 +270,7 @@ export class DeepLinkHandler {
|
||||
if (querySchema) {
|
||||
logger.debug(`[DeepLink] 🔍 Validating query parameters:`, query);
|
||||
validatedQueryParams = await querySchema.parseAsync(query);
|
||||
logger.info(
|
||||
logger.debug(
|
||||
`[DeepLink] ✅ Query parameters validated:`,
|
||||
validatedQueryParams,
|
||||
);
|
||||
@@ -299,7 +299,7 @@ export class DeepLinkHandler {
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(
|
||||
logger.debug(
|
||||
`[DeepLink] 🔄 Redirected to error page for invalid parameters`,
|
||||
);
|
||||
return;
|
||||
@@ -307,7 +307,7 @@ export class DeepLinkHandler {
|
||||
|
||||
// Attempt navigation
|
||||
try {
|
||||
logger.info(`[DeepLink] 🚀 Attempting navigation:`, {
|
||||
logger.debug(`[DeepLink] 🚀 Attempting navigation:`, {
|
||||
routeName,
|
||||
pathParams: validatedPathParams,
|
||||
queryParams: validatedQueryParams,
|
||||
@@ -319,7 +319,7 @@ export class DeepLinkHandler {
|
||||
query: validatedQueryParams,
|
||||
});
|
||||
|
||||
logger.info(`[DeepLink] ✅ Navigation successful to: ${routeName}`);
|
||||
logger.debug(`[DeepLink] ✅ Navigation successful to: ${routeName}`);
|
||||
} catch (error) {
|
||||
logger.error(`[DeepLink] ❌ Navigation failed:`, {
|
||||
routeName,
|
||||
@@ -342,7 +342,7 @@ export class DeepLinkHandler {
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(
|
||||
logger.debug(
|
||||
`[DeepLink] 🔄 Redirected to error page for navigation failure`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,20 +17,40 @@
|
||||
|
||||
<!-- Results List -->
|
||||
<ul class="border-t border-slate-300">
|
||||
<li class="border-b border-slate-300 py-3">
|
||||
<!-- "You" entity -->
|
||||
<li v-if="shouldShowYouEntity" class="border-b border-slate-300 py-3">
|
||||
<h2 class="text-base flex gap-4 items-center">
|
||||
<span class="grow flex gap-2 items-center font-medium">
|
||||
<font-awesome
|
||||
icon="circle-question"
|
||||
class="text-slate-400 text-4xl"
|
||||
/>
|
||||
<span class="italic text-slate-400">(Not Named)</span>
|
||||
<font-awesome icon="hand" class="text-blue-500 text-4xl shrink-0" />
|
||||
<span class="text-ellipsis overflow-hidden text-blue-500">You</span>
|
||||
</span>
|
||||
<span class="text-right">
|
||||
<button
|
||||
type="button"
|
||||
class="block w-full text-center text-sm 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-3 py-1.5 rounded-md"
|
||||
@click="openDialog('Unnamed')"
|
||||
@click="openDialog({ did: activeDid, name: 'You' })"
|
||||
>
|
||||
<font-awesome icon="gift" class="fa-fw"></font-awesome>
|
||||
</button>
|
||||
</span>
|
||||
</h2>
|
||||
</li>
|
||||
<li class="border-b border-slate-300 py-3">
|
||||
<h2 class="text-base flex gap-4 items-center">
|
||||
<span class="grow flex gap-2 items-center font-medium">
|
||||
<font-awesome
|
||||
icon="circle-question"
|
||||
class="text-slate-400 text-4xl shrink-0"
|
||||
/>
|
||||
<span class="text-ellipsis overflow-hidden italic text-slate-500">{{
|
||||
unnamedEntityName
|
||||
}}</span>
|
||||
</span>
|
||||
<span class="text-right">
|
||||
<button
|
||||
type="button"
|
||||
class="block w-full text-center text-sm 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-3 py-1.5 rounded-md"
|
||||
@click="openDialog({ did: '', name: unnamedEntityName })"
|
||||
>
|
||||
<font-awesome icon="gift" class="fa-fw"></font-awesome>
|
||||
</button>
|
||||
@@ -43,14 +63,22 @@
|
||||
class="border-b border-slate-300 py-3"
|
||||
>
|
||||
<h2 class="text-base flex gap-4 items-center">
|
||||
<span class="grow flex gap-2 items-center font-medium">
|
||||
<span
|
||||
class="grow flex gap-2 items-center font-medium overflow-hidden"
|
||||
>
|
||||
<EntityIcon
|
||||
:contact="contact"
|
||||
:icon-size="34"
|
||||
class="inline-block align-middle border border-slate-300 rounded-full overflow-hidden"
|
||||
class="inline-block align-middle border border-slate-300 rounded-full overflow-hidden shrink-0"
|
||||
/>
|
||||
<span v-if="contact.name">{{ contact.name }}</span>
|
||||
<span v-else class="italic text-slate-400">(No name)</span>
|
||||
<span v-if="contact.name" class="text-ellipsis overflow-hidden">{{
|
||||
contact.name
|
||||
}}</span>
|
||||
<span
|
||||
v-else
|
||||
class="text-ellipsis overflow-hidden italic text-slate-500"
|
||||
>{{ contact.did }}</span
|
||||
>
|
||||
</span>
|
||||
<span class="text-right">
|
||||
<button
|
||||
@@ -72,6 +100,7 @@
|
||||
:from-project-id="fromProjectId"
|
||||
:to-project-id="toProjectId"
|
||||
:is-from-project-view="isFromProjectView"
|
||||
:hide-show-all="true"
|
||||
/>
|
||||
</section>
|
||||
</template>
|
||||
@@ -89,6 +118,7 @@ import { GiverReceiverInputInfo } from "../libs/util";
|
||||
import { logger } from "../utils/logger";
|
||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
||||
import { UNNAMED_ENTITY_NAME } from "@/constants/entities";
|
||||
@Component({
|
||||
components: { GiftedDialog, QuickNav, EntityIcon },
|
||||
mixins: [PlatformServiceMixin],
|
||||
@@ -188,147 +218,151 @@ export default class ContactGiftingView extends Vue {
|
||||
}
|
||||
}
|
||||
|
||||
openDialog(contact?: GiverReceiverInputInfo | "Unnamed") {
|
||||
if (contact === "Unnamed") {
|
||||
// Special case: Handle "Unnamed" contacts for both givers and recipients
|
||||
let recipient: GiverReceiverInputInfo;
|
||||
let giver: GiverReceiverInputInfo | undefined;
|
||||
openDialog(contact?: GiverReceiverInputInfo) {
|
||||
// Determine the selected entity based on contact type
|
||||
const selectedEntity = this.createEntityFromContact(contact);
|
||||
|
||||
if (this.stepType === "giver") {
|
||||
// We're selecting a giver, so preserve the existing recipient from context
|
||||
if (this.recipientEntityType === "project") {
|
||||
recipient = {
|
||||
did: this.recipientProjectHandleId,
|
||||
name: this.recipientProjectName,
|
||||
image: this.recipientProjectImage,
|
||||
handleId: this.recipientProjectHandleId,
|
||||
};
|
||||
} else {
|
||||
// Preserve the existing recipient from context
|
||||
if (this.recipientDid === this.activeDid) {
|
||||
// Recipient was "You"
|
||||
recipient = { did: this.activeDid, name: "You" };
|
||||
} else if (this.recipientDid) {
|
||||
// Recipient was a regular contact
|
||||
recipient = {
|
||||
did: this.recipientDid,
|
||||
name: this.recipientProjectName || "Someone",
|
||||
};
|
||||
} else {
|
||||
// Fallback to "You" if no recipient was previously selected
|
||||
recipient = { did: this.activeDid, name: "You" };
|
||||
}
|
||||
}
|
||||
giver = undefined; // Will be set to "Unnamed" in GiftedDialog
|
||||
} else {
|
||||
// We're selecting a recipient, so recipient is "Unnamed" and giver is preserved from context
|
||||
recipient = { did: "", name: "Unnamed" };
|
||||
// Create giver and recipient based on step type and selected entity
|
||||
const { giver, recipient } = this.createGiverAndRecipient(selectedEntity);
|
||||
|
||||
// Preserve the existing giver from the context
|
||||
if (this.giverEntityType === "project") {
|
||||
giver = {
|
||||
did: this.giverProjectHandleId,
|
||||
name: this.giverProjectName,
|
||||
image: this.giverProjectImage,
|
||||
handleId: this.giverProjectHandleId,
|
||||
};
|
||||
} else if (this.giverDid) {
|
||||
giver = {
|
||||
did: this.giverDid,
|
||||
name: this.giverProjectName || "Someone",
|
||||
};
|
||||
} else {
|
||||
giver = { did: this.activeDid, name: "You" };
|
||||
}
|
||||
}
|
||||
// Open the dialog
|
||||
(this.$refs.giftedDialog as GiftedDialog).open(
|
||||
giver,
|
||||
recipient,
|
||||
this.offerId,
|
||||
this.prompt,
|
||||
this.description,
|
||||
this.amountInput,
|
||||
this.unitCode,
|
||||
);
|
||||
|
||||
(this.$refs.giftedDialog as GiftedDialog).open(
|
||||
giver,
|
||||
recipient,
|
||||
this.offerId,
|
||||
this.prompt,
|
||||
this.description,
|
||||
this.amountInput,
|
||||
this.unitCode,
|
||||
);
|
||||
// Move to Step 2 - entities are already set by the open() call
|
||||
(this.$refs.giftedDialog as GiftedDialog).moveToStep2();
|
||||
}
|
||||
|
||||
// Move to Step 2 - entities are already set by the open() call
|
||||
(this.$refs.giftedDialog as GiftedDialog).moveToStep2();
|
||||
/**
|
||||
* Creates an entity object from the contact parameter
|
||||
* Uses DID-based logic to determine "You" and "Unnamed" entities
|
||||
*/
|
||||
private createEntityFromContact(
|
||||
contact?: GiverReceiverInputInfo,
|
||||
): GiverReceiverInputInfo | undefined {
|
||||
if (!contact) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Handle GiverReceiverInputInfo object
|
||||
if (contact.did === this.activeDid) {
|
||||
// If DID matches active DID, create "You" entity
|
||||
return { did: this.activeDid, name: "You" };
|
||||
} else if (!contact.did || contact.did === "") {
|
||||
// If DID is empty/null, create "Unnamed" entity
|
||||
return { did: "", name: UNNAMED_ENTITY_NAME };
|
||||
} else {
|
||||
// Regular case: contact is a GiverReceiverInputInfo
|
||||
let giver: GiverReceiverInputInfo;
|
||||
let recipient: GiverReceiverInputInfo;
|
||||
// Create a copy of the contact to avoid modifying the original
|
||||
return { ...contact };
|
||||
}
|
||||
}
|
||||
|
||||
if (this.stepType === "giver") {
|
||||
// We're selecting a giver, so the contact becomes the giver
|
||||
giver = contact as GiverReceiverInputInfo; // Safe because we know contact is not "Unnamed" or undefined
|
||||
/**
|
||||
* Creates giver and recipient objects based on step type and selected entity
|
||||
*/
|
||||
private createGiverAndRecipient(selectedEntity?: GiverReceiverInputInfo): {
|
||||
giver: GiverReceiverInputInfo | undefined;
|
||||
recipient: GiverReceiverInputInfo;
|
||||
} {
|
||||
if (this.stepType === "giver") {
|
||||
// We're selecting a giver, so the selected entity becomes the giver
|
||||
const giver = selectedEntity;
|
||||
const recipient = this.createRecipientFromContext();
|
||||
return { giver, recipient };
|
||||
} else {
|
||||
// We're selecting a recipient, so the selected entity becomes the recipient
|
||||
const recipient = selectedEntity || {
|
||||
did: "",
|
||||
name: UNNAMED_ENTITY_NAME,
|
||||
};
|
||||
const giver = this.createGiverFromContext();
|
||||
return { giver, recipient };
|
||||
}
|
||||
}
|
||||
|
||||
// Preserve the existing recipient from the context
|
||||
if (this.recipientEntityType === "project") {
|
||||
recipient = {
|
||||
did: this.recipientProjectHandleId,
|
||||
name: this.recipientProjectName,
|
||||
image: this.recipientProjectImage,
|
||||
handleId: this.recipientProjectHandleId,
|
||||
};
|
||||
} else {
|
||||
// Check if the preserved recipient was "You" or a regular contact
|
||||
if (this.recipientDid === this.activeDid) {
|
||||
// Recipient was "You"
|
||||
recipient = { did: this.activeDid, name: "You" };
|
||||
} else if (this.recipientDid) {
|
||||
// Recipient was a regular contact
|
||||
recipient = {
|
||||
did: this.recipientDid,
|
||||
name: this.recipientProjectName || "Someone",
|
||||
};
|
||||
} else {
|
||||
// Fallback to "Unnamed"
|
||||
recipient = { did: "", name: "Unnamed" };
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Creates recipient object from context (preserves existing recipient)
|
||||
*/
|
||||
private createRecipientFromContext(): GiverReceiverInputInfo {
|
||||
if (this.recipientEntityType === "project") {
|
||||
return {
|
||||
name: this.recipientProjectName,
|
||||
image: this.recipientProjectImage,
|
||||
handleId: this.recipientProjectHandleId,
|
||||
};
|
||||
} else {
|
||||
if (this.recipientDid === this.activeDid) {
|
||||
return { did: this.activeDid, name: "You" };
|
||||
} else if (this.recipientDid) {
|
||||
return {
|
||||
did: this.recipientDid,
|
||||
name: this.recipientProjectName,
|
||||
};
|
||||
} else {
|
||||
// We're selecting a recipient, so the contact becomes the recipient
|
||||
recipient = contact as GiverReceiverInputInfo; // Safe because we know contact is not "Unnamed" or undefined
|
||||
|
||||
// Preserve the existing giver from the context
|
||||
if (this.giverEntityType === "project") {
|
||||
giver = {
|
||||
did: this.giverProjectHandleId,
|
||||
name: this.giverProjectName,
|
||||
image: this.giverProjectImage,
|
||||
handleId: this.giverProjectHandleId,
|
||||
};
|
||||
} else {
|
||||
// Check if the preserved giver was "You" or a regular contact
|
||||
if (this.giverDid === this.activeDid) {
|
||||
// Giver was "You"
|
||||
giver = { did: this.activeDid, name: "You" };
|
||||
} else if (this.giverDid) {
|
||||
// Giver was a regular contact
|
||||
giver = {
|
||||
did: this.giverDid,
|
||||
name: this.giverProjectName || "Someone",
|
||||
};
|
||||
} else {
|
||||
// Fallback to "Unnamed"
|
||||
giver = { did: "", name: "Unnamed" };
|
||||
}
|
||||
}
|
||||
return { did: "", name: UNNAMED_ENTITY_NAME };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(this.$refs.giftedDialog as GiftedDialog).open(
|
||||
giver,
|
||||
recipient,
|
||||
this.offerId,
|
||||
this.prompt,
|
||||
this.description,
|
||||
this.amountInput,
|
||||
this.unitCode,
|
||||
);
|
||||
/**
|
||||
* Creates giver object from context (preserves existing giver)
|
||||
*/
|
||||
private createGiverFromContext(): GiverReceiverInputInfo {
|
||||
if (this.giverEntityType === "project") {
|
||||
return {
|
||||
name: this.giverProjectName,
|
||||
image: this.giverProjectImage,
|
||||
handleId: this.giverProjectHandleId,
|
||||
};
|
||||
} else {
|
||||
if (this.giverDid === this.activeDid) {
|
||||
return { did: this.activeDid, name: "You" };
|
||||
} else if (this.giverDid) {
|
||||
return {
|
||||
did: this.giverDid,
|
||||
name: this.giverProjectName,
|
||||
};
|
||||
} else {
|
||||
return { did: "", name: UNNAMED_ENTITY_NAME };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move to Step 2 - entities are already set by the open() call
|
||||
(this.$refs.giftedDialog as GiftedDialog).moveToStep2();
|
||||
/**
|
||||
* Get the unnamed entity name constant
|
||||
*/
|
||||
get unnamedEntityName(): string {
|
||||
return UNNAMED_ENTITY_NAME;
|
||||
}
|
||||
|
||||
get shouldShowYouEntity(): boolean {
|
||||
if (this.stepType === "giver") {
|
||||
// When selecting a giver, show "You" if the current recipient is not "You"
|
||||
// This prevents selecting yourself as both giver and recipient
|
||||
if (this.recipientEntityType === "project") {
|
||||
// If recipient is a project, we can select "You" as giver
|
||||
return true;
|
||||
} else {
|
||||
// If recipient is a person, check if it's not "You"
|
||||
return this.recipientDid !== this.activeDid;
|
||||
}
|
||||
} else {
|
||||
// When selecting a recipient, show "You" if the current giver is not "You"
|
||||
// This prevents selecting yourself as both giver and recipient
|
||||
if (this.giverEntityType === "project") {
|
||||
// If giver is a project, we can select "You" as recipient
|
||||
return true;
|
||||
} else {
|
||||
// If giver is a person, check if it's not "You"
|
||||
return this.giverDid !== this.activeDid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,6 +290,7 @@ import {
|
||||
NOTIFY_SERVER_ACCESS_ERROR,
|
||||
NOTIFY_NO_IDENTITY_ERROR,
|
||||
} from "@/constants/notifications";
|
||||
import { THAT_UNNAMED_PERSON } from "@/constants/entities";
|
||||
|
||||
/**
|
||||
* DIDView Component
|
||||
@@ -551,7 +552,7 @@ export default class DIDView extends Vue {
|
||||
contact.registered = true;
|
||||
await this.$updateContact(contact.did, { registered: true });
|
||||
|
||||
const name = contact.name || "That unnamed person";
|
||||
const name = contact.name || THAT_UNNAMED_PERSON;
|
||||
this.notify.success(
|
||||
`${name} ${NOTIFY_REGISTRATION_SUCCESS.message}`,
|
||||
TIMEOUTS.LONG,
|
||||
|
||||
@@ -233,7 +233,7 @@
|
||||
|
||||
<div class="grow">
|
||||
<h2 class="text-base font-semibold">
|
||||
{{ project.name || "Unnamed Project" }}
|
||||
{{ project.name || unnamedProject }}
|
||||
</h2>
|
||||
<div class="text-sm">
|
||||
<font-awesome
|
||||
@@ -340,6 +340,7 @@ import {
|
||||
NOTIFY_DISCOVER_LOCAL_SEARCH_ERROR,
|
||||
NOTIFY_DISCOVER_MAP_SEARCH_ERROR,
|
||||
} from "@/constants/notifications";
|
||||
import { UNNAMED_PROJECT } from "@/constants/entities";
|
||||
interface Tile {
|
||||
indexLat: number;
|
||||
indexLon: number;
|
||||
@@ -370,6 +371,13 @@ export default class DiscoverView extends Vue {
|
||||
|
||||
notify!: ReturnType<typeof createNotifyHelpers>;
|
||||
|
||||
/**
|
||||
* Get the unnamed project constant
|
||||
*/
|
||||
get unnamedProject(): string {
|
||||
return UNNAMED_PROJECT;
|
||||
}
|
||||
|
||||
activeDid = "";
|
||||
allContacts: Array<Contact> = [];
|
||||
allMyDids: Array<string> = [];
|
||||
|
||||
@@ -200,7 +200,7 @@
|
||||
</p>
|
||||
<p>
|
||||
Then you can record your appreciation for... whatever: select any contact on the home page
|
||||
(or "Unnamed") and send it. The main goal is to record what people
|
||||
(or "{{ unnamedEntityName }}") and send it. The main goal is to record what people
|
||||
have given you, to grow giving economies. You can also record your own
|
||||
ideas for projects. Each claim is recorded on a
|
||||
custom ledger.
|
||||
@@ -600,6 +600,7 @@ import QuickNav from "../components/QuickNav.vue";
|
||||
import { APP_SERVER } from "../constants/app";
|
||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||
import { QRNavigationService } from "@/services/QRNavigationService";
|
||||
import { UNNAMED_ENTITY_NAME } from "@/constants/entities";
|
||||
|
||||
/**
|
||||
* HelpView.vue - Comprehensive Help System Component
|
||||
@@ -647,6 +648,13 @@ export default class HelpView extends Vue {
|
||||
APP_SERVER = APP_SERVER;
|
||||
// Capacitor reference removed - using QRNavigationService instead
|
||||
|
||||
/**
|
||||
* Get the unnamed entity name constant
|
||||
*/
|
||||
get unnamedEntityName(): string {
|
||||
return UNNAMED_ENTITY_NAME;
|
||||
}
|
||||
|
||||
// Ideally, we put no functionality in here, especially in the setup,
|
||||
// because we never want this page to have a chance of throwing an error.
|
||||
|
||||
|
||||
@@ -282,6 +282,7 @@ import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
||||
import { NOTIFY_CONTACT_LOADING_ISSUE } from "@/constants/notifications";
|
||||
import * as Package from "../../package.json";
|
||||
import { UNNAMED_ENTITY_NAME } from "@/constants/entities";
|
||||
|
||||
// consolidate this with GiveActionClaim in src/interfaces/claims.ts
|
||||
interface Claim {
|
||||
@@ -1546,30 +1547,41 @@ export default class HomeView extends Vue {
|
||||
* @param giver Optional contact info for giver
|
||||
* @param description Optional gift description
|
||||
*/
|
||||
openDialog(giver?: GiverReceiverInputInfo | "Unnamed", prompt?: string) {
|
||||
if (giver === "Unnamed") {
|
||||
// Special case: Pass undefined to trigger Step 1, but with "Unnamed" pre-selected
|
||||
(this.$refs.giftedDialog as GiftedDialog).open(
|
||||
undefined,
|
||||
{
|
||||
did: this.activeDid,
|
||||
name: "You",
|
||||
} as GiverReceiverInputInfo,
|
||||
undefined,
|
||||
prompt,
|
||||
);
|
||||
// Immediately select "Unnamed" and move to Step 2
|
||||
(this.$refs.giftedDialog as GiftedDialog).selectGiver();
|
||||
openDialog(giver?: GiverReceiverInputInfo, prompt?: string) {
|
||||
// Determine the giver entity based on DID logic
|
||||
const giverEntity = this.createGiverEntity(giver);
|
||||
|
||||
(this.$refs.giftedDialog as GiftedDialog).open(
|
||||
giverEntity,
|
||||
{
|
||||
did: this.activeDid,
|
||||
name: "You", // In HomeView, we always use "You" as the giver
|
||||
} as GiverReceiverInputInfo,
|
||||
undefined,
|
||||
prompt,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates giver entity using DID-based logic
|
||||
*/
|
||||
private createGiverEntity(
|
||||
giver?: GiverReceiverInputInfo,
|
||||
): GiverReceiverInputInfo | undefined {
|
||||
if (!giver) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Handle GiverReceiverInputInfo object
|
||||
if (giver.did === this.activeDid) {
|
||||
// If DID matches active DID, create "You" entity
|
||||
return { did: this.activeDid, name: "You" };
|
||||
} else if (!giver.did || giver.did === "") {
|
||||
// If DID is empty/null, create "Unnamed" entity
|
||||
return { did: "", name: UNNAMED_ENTITY_NAME };
|
||||
} else {
|
||||
(this.$refs.giftedDialog as GiftedDialog).open(
|
||||
giver,
|
||||
{
|
||||
did: this.activeDid,
|
||||
name: "You",
|
||||
} as GiverReceiverInputInfo,
|
||||
undefined,
|
||||
prompt,
|
||||
);
|
||||
// Return the giver as-is
|
||||
return giver;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1632,10 +1644,15 @@ export default class HomeView extends Vue {
|
||||
this.isImageViewerOpen = true;
|
||||
}
|
||||
|
||||
openPersonDialog(
|
||||
giver?: GiverReceiverInputInfo | "Unnamed",
|
||||
prompt?: string,
|
||||
) {
|
||||
private handleQRCodeClick() {
|
||||
if (Capacitor.isNativePlatform()) {
|
||||
this.$router.push({ name: "contact-qr-scan-full" });
|
||||
} else {
|
||||
this.$router.push({ name: "contact-qr" });
|
||||
}
|
||||
}
|
||||
|
||||
openPersonDialog(giver?: GiverReceiverInputInfo, prompt?: string) {
|
||||
this.showProjectsDialog = false;
|
||||
this.openDialog(giver, prompt);
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@
|
||||
class="text-blue-500"
|
||||
@click="onClickLoadProject(plan.handleId)"
|
||||
>
|
||||
{{ plan.name || "Unnamed Project" }}
|
||||
{{ plan.name || unnamedProject }}
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="fulfillersToHitLimit" class="text-center">
|
||||
@@ -207,7 +207,7 @@
|
||||
class="text-blue-500"
|
||||
@click="onClickLoadProject(fulfilledByThis.handleId)"
|
||||
>
|
||||
{{ fulfilledByThis.name || "Unnamed Project" }}
|
||||
{{ fulfilledByThis.name || unnamedProject }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -611,6 +611,7 @@ import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
||||
import { NOTIFY_CONFIRM_CLAIM } from "@/constants/notifications";
|
||||
import { APP_SERVER } from "@/constants/app";
|
||||
import { UNNAMED_PROJECT } from "@/constants/entities";
|
||||
/**
|
||||
* Project View Component
|
||||
* @author Matthew Raymer
|
||||
@@ -664,6 +665,13 @@ export default class ProjectViewView extends Vue {
|
||||
/** Notification helpers instance */
|
||||
notify!: ReturnType<typeof createNotifyHelpers>;
|
||||
|
||||
/**
|
||||
* Get the unnamed project constant
|
||||
*/
|
||||
get unnamedProject(): string {
|
||||
return UNNAMED_PROJECT;
|
||||
}
|
||||
|
||||
// Account and Settings State
|
||||
/** Currently active DID */
|
||||
activeDid = "";
|
||||
|
||||
@@ -244,7 +244,7 @@
|
||||
|
||||
<div class="grow overflow-hidden">
|
||||
<h2 class="text-base font-semibold">
|
||||
{{ project.name || "Unnamed Project" }}
|
||||
{{ project.name || unnamedProject }}
|
||||
</h2>
|
||||
<div class="text-sm truncate">
|
||||
{{ project.description }}
|
||||
@@ -286,6 +286,7 @@ import {
|
||||
NOTIFY_OFFERS_LOAD_ERROR,
|
||||
NOTIFY_OFFERS_FETCH_ERROR,
|
||||
} from "@/constants/notifications";
|
||||
import { UNNAMED_PROJECT } from "@/constants/entities";
|
||||
|
||||
/**
|
||||
* Projects View Component
|
||||
@@ -324,6 +325,13 @@ export default class ProjectsView extends Vue {
|
||||
|
||||
notify!: ReturnType<typeof createNotifyHelpers>;
|
||||
|
||||
/**
|
||||
* Get the unnamed project constant
|
||||
*/
|
||||
get unnamedProject(): string {
|
||||
return UNNAMED_PROJECT;
|
||||
}
|
||||
|
||||
// User account state
|
||||
activeDid = "";
|
||||
allContacts: Array<Contact> = [];
|
||||
|
||||
@@ -239,7 +239,8 @@ export default class QuickActionBvcEndView extends Vue {
|
||||
}
|
||||
const eventStartDateObj = currentOrPreviousSat
|
||||
.set({ weekday: 6 })
|
||||
.set({ hour: 9 })
|
||||
.set({ hour: 8 })
|
||||
.set({ minute: 30 }) // to catch if people put their claims 30 minutes early
|
||||
.startOf("hour");
|
||||
|
||||
// Hack, but full ISO pushes the length to 340 which crashes verifyJWT!
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
*/
|
||||
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { UNNAMED_ENTITY_NAME } from '../src/constants/entities';
|
||||
import { deleteContact, generateAndRegisterEthrUser, importUser } from './testUtils';
|
||||
|
||||
test('Check activity feed - check that server is running', async ({ page }) => {
|
||||
@@ -177,7 +178,7 @@ test('Check User 0 can register a random person', async ({ page }) => {
|
||||
await page.goto('./');
|
||||
await page.getByTestId('closeOnboardingAndFinish').click();
|
||||
await page.getByRole('button', { name: 'Person' }).click();
|
||||
await page.getByRole('listitem').filter({ hasText: 'Unnamed' }).locator('svg').click();
|
||||
await page.getByRole('listitem').filter({ hasText: UNNAMED_ENTITY_NAME }).locator('svg').click();
|
||||
await page.getByPlaceholder('What was given').fill('Gave me access!');
|
||||
await page.getByRole('button', { name: 'Sign & Send' }).click();
|
||||
await expect(page.getByText('That gift was recorded.')).toBeVisible();
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
* ```
|
||||
*/
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { UNNAMED_ENTITY_NAME } from '../src/constants/entities';
|
||||
import { importUser } from './testUtils';
|
||||
|
||||
test('Record something given', async ({ page }) => {
|
||||
@@ -101,7 +102,7 @@ test('Record something given', async ({ page }) => {
|
||||
await page.goto('./');
|
||||
await page.getByTestId('closeOnboardingAndFinish').click();
|
||||
await page.getByRole('button', { name: 'Person' }).click();
|
||||
await page.getByRole('listitem').filter({ hasText: 'Unnamed' }).locator('svg').click();
|
||||
await page.getByRole('listitem').filter({ hasText: UNNAMED_ENTITY_NAME }).locator('svg').click();
|
||||
await page.getByPlaceholder('What was given').fill(finalTitle);
|
||||
await page.getByRole('spinbutton').fill(randomNonZeroNumber.toString());
|
||||
await page.getByRole('button', { name: 'Sign & Send' }).click();
|
||||
|
||||
@@ -85,6 +85,7 @@
|
||||
*/
|
||||
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { UNNAMED_ENTITY_NAME } from '../src/constants/entities';
|
||||
import { importUser, createUniqueStringsArray, createRandomNumbersArray } from './testUtils';
|
||||
|
||||
test('Record 9 new gifts', async ({ page }) => {
|
||||
@@ -116,7 +117,7 @@ test('Record 9 new gifts', async ({ page }) => {
|
||||
await page.getByTestId('closeOnboardingAndFinish').click();
|
||||
}
|
||||
await page.getByRole('button', { name: 'Person' }).click();
|
||||
await page.getByRole('listitem').filter({ hasText: 'Unnamed' }).locator('svg').click();
|
||||
await page.getByRole('listitem').filter({ hasText: UNNAMED_ENTITY_NAME }).locator('svg').click();
|
||||
await page.getByPlaceholder('What was given').fill(finalTitles[i]);
|
||||
await page.getByRole('spinbutton').fill(finalNumbers[i].toString());
|
||||
await page.getByRole('button', { name: 'Sign & Send' }).click();
|
||||
|
||||
@@ -23,11 +23,36 @@ test('New offers for another user', async ({ page }) => {
|
||||
await page.getByPlaceholder('URL or DID, Name, Public Key').fill(autoCreatedDid + ', A Friend');
|
||||
await expect(page.locator('button > svg.fa-plus')).toBeVisible();
|
||||
await page.locator('button > svg.fa-plus').click();
|
||||
await page.locator('div[role="alert"] button:has-text("No")').click(); // don't register
|
||||
await page.locator('div[role="alert"]:has-text("Register") button:has-text("No")').click(); // don't register
|
||||
await expect(page.locator('div[role="alert"] h4:has-text("Success")')).toBeVisible();
|
||||
await page.locator('div[role="alert"] button > svg.fa-xmark').click(); // dismiss info alert
|
||||
await expect(page.locator('div[role="alert"] button > svg.fa-xmark')).toBeHidden(); // ensure alert is gone
|
||||
|
||||
// Wait for any dialogs to appear and dismiss them systematically
|
||||
await page.waitForTimeout(1000); // Give time for any dialogs to appear
|
||||
|
||||
// Check for and dismiss export dialog
|
||||
const exportDialog = page.locator('div[role="alert"]:has-text("Export Your Data")');
|
||||
if (await exportDialog.isVisible()) {
|
||||
await page.locator('div[role="alert"]:has-text("Export Your Data") button:has-text("No, Not Now")').click();
|
||||
await expect(exportDialog).toBeHidden();
|
||||
await page.waitForTimeout(500);
|
||||
}
|
||||
|
||||
// Check for any other modal overlays that might be blocking
|
||||
const modalOverlay = page.locator('div[class*="bg-slate-900"]');
|
||||
if (await modalOverlay.isVisible()) {
|
||||
// Try to find and click any close buttons in the overlay
|
||||
const closeButtons = page.locator('div[class*="bg-slate-900"] button');
|
||||
if (await closeButtons.count() > 0) {
|
||||
await closeButtons.first().click();
|
||||
await page.waitForTimeout(500);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure no dialogs are blocking before proceeding
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// show buttons to make offers directly to people
|
||||
await page.getByRole('button').filter({ hasText: /See Actions/i }).click();
|
||||
|
||||
@@ -39,8 +64,8 @@ test('New offers for another user', async ({ page }) => {
|
||||
await page.getByTestId('inputOfferAmount').locator('input').fill('1');
|
||||
await page.getByRole('button', { name: 'Sign & Send' }).click();
|
||||
await expect(page.getByText('That offer was recorded.')).toBeVisible();
|
||||
await page.locator('div[role="alert"] button > svg.fa-xmark').click(); // dismiss info alert
|
||||
await expect(page.locator('div[role="alert"] button > svg.fa-xmark')).toBeHidden(); // ensure alert is gone
|
||||
await page.locator('div[role="alert"]:has-text("That offer was recorded") button > svg.fa-xmark').click(); // dismiss info alert
|
||||
await expect(page.locator('div[role="alert"]:has-text("That offer was recorded") button > svg.fa-xmark')).toBeHidden(); // ensure alert is gone
|
||||
|
||||
// make another offer to user 1
|
||||
const randomString2 = Math.random().toString(36).substring(2, 5);
|
||||
@@ -49,8 +74,8 @@ test('New offers for another user', async ({ page }) => {
|
||||
await page.getByTestId('inputOfferAmount').locator('input').fill('3');
|
||||
await page.getByRole('button', { name: 'Sign & Send' }).click();
|
||||
await expect(page.getByText('That offer was recorded.')).toBeVisible();
|
||||
await page.locator('div[role="alert"] button > svg.fa-xmark').click(); // dismiss info alert
|
||||
await expect(page.locator('div[role="alert"] button > svg.fa-xmark')).toBeHidden(); // ensure alert is gone
|
||||
await page.locator('div[role="alert"]:has-text("That offer was recorded") button > svg.fa-xmark').click(); // dismiss info alert
|
||||
await expect(page.locator('div[role="alert"]:has-text("That offer was recorded") button > svg.fa-xmark')).toBeHidden(); // ensure alert is gone
|
||||
|
||||
// Switch back to the auto-created DID (the "another user") to see the offers
|
||||
await switchToUser(page, autoCreatedDid);
|
||||
|
||||
@@ -157,11 +157,11 @@ export async function generateAndRegisterEthrUser(page: Page): Promise<string> {
|
||||
.getByPlaceholder("URL or DID, Name, Public Key")
|
||||
.fill(`${newDid}, ${contactName}`);
|
||||
await page.locator("button > svg.fa-plus").click();
|
||||
// register them
|
||||
await page.locator('div[role="alert"] button:has-text("Yes")').click();
|
||||
// register them - be more specific to avoid multiple button matches
|
||||
await page.locator('div[role="alert"]:has-text("Register") button:has-text("Yes")').click();
|
||||
// wait for it to disappear because the next steps may depend on alerts being gone
|
||||
await expect(
|
||||
page.locator('div[role="alert"] button:has-text("Yes")')
|
||||
page.locator('div[role="alert"]:has-text("Register") button:has-text("Yes")')
|
||||
).toBeHidden();
|
||||
await expect(page.locator("li", { hasText: contactName })).toBeVisible();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user