Matthew Raymer
5 days ago
24 changed files with 28862 additions and 27539 deletions
File diff suppressed because it is too large
@ -1,141 +1,133 @@ |
|||||
{ |
{ |
||||
"name": "TimeSafari", |
"name": "TimeSafari", |
||||
"version": "0.3.51-beta", |
"version": "0.3.54-beta", |
||||
"description": "A cross-platform app for managing time-based crowdfunding.", |
"scripts": { |
||||
"author": "Your Name <your.email@example.com>", |
"dev": "vite", |
||||
"main": "src/electron/main.js", |
"serve": "vite preview", |
||||
"scripts": { |
"build": "VITE_GIT_HASH=`git log -1 --pretty=format:%h` vite build", |
||||
"dev": "vite", |
"lint": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src", |
||||
"serve": "vite preview", |
"lint-fix": "eslint --ext .js,.ts,.vue --ignore-path .gitignore --fix src", |
||||
"build": "VITE_GIT_HASH=`git log -1 --pretty=format:%h` vite build", |
"prebuild": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src && node sw_combine.js", |
||||
"build:capacitor": "vite build --mode capacitor", |
"test-local": "npx playwright test -c playwright.config-local.ts --trace on", |
||||
"build:electron": "vite build --mode electron", |
"test-all": "npm run build && npx playwright test -c playwright.config-local.ts --trace on" |
||||
"electron:dev": "vite build --mode electron && electron .", |
}, |
||||
"electron:build": "electron-builder", |
"dependencies": { |
||||
"capacitor:sync": "npx cap copy", |
"@capacitor/android": "^6.2.0", |
||||
"lint": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src", |
"@capacitor/cli": "^6.2.0", |
||||
"lint-fix": "eslint --ext .js,.ts,.vue --ignore-path .gitignore --fix src", |
"@capacitor/core": "^6.2.0", |
||||
"prebuild": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src && node sw_combine.js", |
"@capacitor/ios": "^6.2.0", |
||||
"test-local": "npx playwright test -c playwright.config-local.ts --trace on", |
"@dicebear/collection": "^5.4.1", |
||||
"test-all": "npm run build && npx playwright test -c playwright.config-local.ts --trace on" |
"@dicebear/core": "^5.4.1", |
||||
|
"@ethersproject/hdnode": "^5.7.0", |
||||
|
"@fortawesome/fontawesome-svg-core": "^6.5.1", |
||||
|
"@fortawesome/free-solid-svg-icons": "^6.5.1", |
||||
|
"@fortawesome/vue-fontawesome": "^3.0.6", |
||||
|
"@peculiar/asn1-ecc": "^2.3.8", |
||||
|
"@peculiar/asn1-schema": "^2.3.8", |
||||
|
"@pvermeer/dexie-encrypted-addon": "^3.0.0", |
||||
|
"@simplewebauthn/browser": "^10.0.0", |
||||
|
"@simplewebauthn/server": "^10.0.0", |
||||
|
"@tweenjs/tween.js": "^21.1.1", |
||||
|
"@types/qrcode": "^1.5.5", |
||||
|
"@veramo/core": "^5.6.0", |
||||
|
"@veramo/credential-w3c": "^5.6.0", |
||||
|
"@veramo/data-store": "^5.6.0", |
||||
|
"@veramo/did-manager": "^5.6.0", |
||||
|
"@veramo/did-provider-ethr": "^5.6.0", |
||||
|
"@veramo/did-provider-peer": "^6.0.0", |
||||
|
"@veramo/did-resolver": "^5.6.0", |
||||
|
"@veramo/key-manager": "^5.6.0", |
||||
|
"@vue-leaflet/vue-leaflet": "^0.10.1", |
||||
|
"@vueuse/core": "^12.3.0", |
||||
|
"@zxing/text-encoding": "^0.9.0", |
||||
|
"asn1-ber": "^1.2.2", |
||||
|
"axios": "^1.6.8", |
||||
|
"cbor-x": "^1.5.9", |
||||
|
"class-transformer": "^0.5.1", |
||||
|
"dexie": "^3.2.7", |
||||
|
"dexie-export-import": "^4.1.1", |
||||
|
"did-jwt": "^7.4.7", |
||||
|
"did-resolver": "^4.1.0", |
||||
|
"ethereum-cryptography": "^2.1.3", |
||||
|
"ethereumjs-util": "^7.1.5", |
||||
|
"jdenticon": "^3.2.0", |
||||
|
"js-generate-password": "^0.1.9", |
||||
|
"js-yaml": "^4.1.0", |
||||
|
"leaflet": "^1.9.4", |
||||
|
"localstorage-slim": "^2.7.0", |
||||
|
"lru-cache": "^10.2.0", |
||||
|
"luxon": "^3.4.4", |
||||
|
"merkletreejs": "^0.3.11", |
||||
|
"nostr-tools": "^2.7.2", |
||||
|
"notiwind": "^2.0.2", |
||||
|
"papaparse": "^5.4.1", |
||||
|
"pina": "^0.20.2204228", |
||||
|
"pinia-plugin-persistedstate": "^3.2.1", |
||||
|
"qr-code-generator-vue3": "^1.4.21", |
||||
|
"qrcode": "^1.5.4", |
||||
|
"ramda": "^0.29.1", |
||||
|
"readable-stream": "^4.5.2", |
||||
|
"reflect-metadata": "^0.1.14", |
||||
|
"register-service-worker": "^1.7.2", |
||||
|
"simple-vue-camera": "^1.1.3", |
||||
|
"three": "^0.156.1", |
||||
|
"ua-parser-js": "^1.0.37", |
||||
|
"util": "^0.12.5", |
||||
|
"vue": "^3.5.13", |
||||
|
"vue-axios": "^3.5.2", |
||||
|
"vue-facing-decorator": "^3.0.4", |
||||
|
"vue-picture-cropper": "^0.7.0", |
||||
|
"vue-qrcode-reader": "^5.5.3", |
||||
|
"vue-router": "^4.5.0", |
||||
|
"web-did-resolver": "^2.0.27" |
||||
|
}, |
||||
|
"devDependencies": { |
||||
|
"@playwright/test": "^1.45.2", |
||||
|
"@types/js-yaml": "^4.0.9", |
||||
|
"@types/leaflet": "^1.9.8", |
||||
|
"@types/luxon": "^3.4.2", |
||||
|
"@types/node": "^20.14.11", |
||||
|
"@types/ramda": "^0.29.11", |
||||
|
"@types/three": "^0.155.1", |
||||
|
"@types/ua-parser-js": "^0.7.39", |
||||
|
"@typescript-eslint/eslint-plugin": "^6.21.0", |
||||
|
"@typescript-eslint/parser": "^6.21.0", |
||||
|
"@vitejs/plugin-vue": "^5.2.1", |
||||
|
"@vue/eslint-config-typescript": "^11.0.3", |
||||
|
"autoprefixer": "^10.4.19", |
||||
|
"electron": "^33.2.1", |
||||
|
"electron-builder": "^25.1.8", |
||||
|
"eslint": "^8.57.0", |
||||
|
"eslint-config-prettier": "^9.1.0", |
||||
|
"eslint-plugin-prettier": "^5.2.1", |
||||
|
"eslint-plugin-vue": "^9.32.0", |
||||
|
"npm-check-updates": "^17.1.13", |
||||
|
"postcss": "^8.4.38", |
||||
|
"prettier": "^3.2.5", |
||||
|
"tailwindcss": "^3.4.1", |
||||
|
"typescript": "~5.2.2", |
||||
|
"vite": "^5.2.0", |
||||
|
"vite-plugin-pwa": "^0.19.8" |
||||
|
}, |
||||
|
"build": { |
||||
|
"appId": "com.example.app", |
||||
|
"productName": "TimeSafari", |
||||
|
"directories": { |
||||
|
"output": "dist-electron-build" |
||||
}, |
}, |
||||
"dependencies": { |
"files": [ |
||||
"@capacitor/android": "^6.2.0", |
"dist-electron/**", |
||||
"@capacitor/cli": "^6.2.0", |
"src/electron/**" |
||||
"@capacitor/core": "^6.2.0", |
], |
||||
"@capacitor/ios": "^6.2.0", |
"mac": { |
||||
"@dicebear/collection": "^5.4.1", |
"target": "dmg" |
||||
"@dicebear/core": "^5.4.1", |
|
||||
"@ethersproject/hdnode": "^5.7.0", |
|
||||
"@fortawesome/fontawesome-svg-core": "^6.5.1", |
|
||||
"@fortawesome/free-solid-svg-icons": "^6.5.1", |
|
||||
"@fortawesome/vue-fontawesome": "^3.0.6", |
|
||||
"@peculiar/asn1-ecc": "^2.3.8", |
|
||||
"@peculiar/asn1-schema": "^2.3.8", |
|
||||
"@pvermeer/dexie-encrypted-addon": "^3.0.0", |
|
||||
"@simplewebauthn/browser": "^10.0.0", |
|
||||
"@simplewebauthn/server": "^10.0.0", |
|
||||
"@tweenjs/tween.js": "^21.1.1", |
|
||||
"@types/qrcode": "^1.5.5", |
|
||||
"@veramo/core": "^5.6.0", |
|
||||
"@veramo/credential-w3c": "^5.6.0", |
|
||||
"@veramo/data-store": "^5.6.0", |
|
||||
"@veramo/did-manager": "^5.6.0", |
|
||||
"@veramo/did-provider-ethr": "^5.6.0", |
|
||||
"@veramo/did-provider-peer": "^6.0.0", |
|
||||
"@veramo/did-resolver": "^5.6.0", |
|
||||
"@veramo/key-manager": "^5.6.0", |
|
||||
"@vue-leaflet/vue-leaflet": "^0.10.1", |
|
||||
"@vueuse/core": "^12.3.0", |
|
||||
"@zxing/text-encoding": "^0.9.0", |
|
||||
"asn1-ber": "^1.2.2", |
|
||||
"axios": "^1.6.8", |
|
||||
"cbor-x": "^1.5.9", |
|
||||
"class-transformer": "^0.5.1", |
|
||||
"dexie": "^3.2.7", |
|
||||
"dexie-export-import": "^4.1.1", |
|
||||
"did-jwt": "^7.4.7", |
|
||||
"did-resolver": "^4.1.0", |
|
||||
"ethereum-cryptography": "^2.1.3", |
|
||||
"ethereumjs-util": "^7.1.5", |
|
||||
"jdenticon": "^3.2.0", |
|
||||
"js-generate-password": "^0.1.9", |
|
||||
"js-yaml": "^4.1.0", |
|
||||
"leaflet": "^1.9.4", |
|
||||
"localstorage-slim": "^2.7.0", |
|
||||
"lru-cache": "^10.2.0", |
|
||||
"luxon": "^3.4.4", |
|
||||
"merkletreejs": "^0.3.11", |
|
||||
"nostr-tools": "^2.7.2", |
|
||||
"notiwind": "^2.0.2", |
|
||||
"papaparse": "^5.4.1", |
|
||||
"pina": "^0.20.2204228", |
|
||||
"pinia-plugin-persistedstate": "^3.2.1", |
|
||||
"qr-code-generator-vue3": "^1.4.21", |
|
||||
"qrcode": "^1.5.4", |
|
||||
"ramda": "^0.29.1", |
|
||||
"readable-stream": "^4.5.2", |
|
||||
"reflect-metadata": "^0.1.14", |
|
||||
"register-service-worker": "^1.7.2", |
|
||||
"simple-vue-camera": "^1.1.3", |
|
||||
"three": "^0.156.1", |
|
||||
"ua-parser-js": "^1.0.37", |
|
||||
"util": "^0.12.5", |
|
||||
"vue": "^3.5.13", |
|
||||
"vue-axios": "^3.5.2", |
|
||||
"vue-facing-decorator": "^3.0.4", |
|
||||
"vue-picture-cropper": "^0.7.0", |
|
||||
"vue-qrcode-reader": "^5.5.3", |
|
||||
"vue-router": "^4.5.0", |
|
||||
"web-did-resolver": "^2.0.27" |
|
||||
}, |
}, |
||||
"devDependencies": { |
"win": { |
||||
"@playwright/test": "^1.45.2", |
"target": "nsis" |
||||
"@types/js-yaml": "^4.0.9", |
|
||||
"@types/leaflet": "^1.9.8", |
|
||||
"@types/luxon": "^3.4.2", |
|
||||
"@types/node": "^20.14.11", |
|
||||
"@types/ramda": "^0.29.11", |
|
||||
"@types/three": "^0.155.1", |
|
||||
"@types/ua-parser-js": "^0.7.39", |
|
||||
"@typescript-eslint/eslint-plugin": "^6.21.0", |
|
||||
"@typescript-eslint/parser": "^6.21.0", |
|
||||
"@vitejs/plugin-vue": "^5.2.1", |
|
||||
"@vue/eslint-config-typescript": "^11.0.0", |
|
||||
"autoprefixer": "^10.4.19", |
|
||||
"electron": "^33.2.1", |
|
||||
"electron-builder": "^25.1.8", |
|
||||
"eslint": "^8.0.0", |
|
||||
"eslint-config-prettier": "^9.1.0", |
|
||||
"eslint-plugin-prettier": "^5.2.1", |
|
||||
"eslint-plugin-vue": "^9.32.0", |
|
||||
"npm-check-updates": "^17.1.13", |
|
||||
"postcss": "^8.4.38", |
|
||||
"prettier": "^3.2.5", |
|
||||
"tailwindcss": "^3.4.1", |
|
||||
"typescript": "~5.2.2", |
|
||||
"vite": "^6.0.7", |
|
||||
"vite-plugin-pwa": "^0.21.1" |
|
||||
}, |
}, |
||||
"build": { |
"linux": { |
||||
"appId": "com.example.app", |
"target": "AppImage" |
||||
"productName": "TimeSafari", |
}, |
||||
"directories": { |
"asar": false |
||||
"output": "dist-electron-build" |
} |
||||
}, |
} |
||||
"files": [ |
|
||||
"dist-electron/**", |
|
||||
"src/electron/**" |
|
||||
], |
|
||||
"mac": { |
|
||||
"target": "dmg" |
|
||||
}, |
|
||||
"win": { |
|
||||
"target": "nsis" |
|
||||
}, |
|
||||
"linux": { |
|
||||
"target": "AppImage" |
|
||||
}, |
|
||||
"asar": false |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,182 @@ |
|||||
|
<template> |
||||
|
<div |
||||
|
v-if="isOpen" |
||||
|
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50" |
||||
|
> |
||||
|
<div class="bg-white rounded-lg p-6 max-w-2xl w-full mx-4"> |
||||
|
<!-- Header --> |
||||
|
<div class="flex justify-between items-center mb-4"> |
||||
|
<h2 class="text-xl font-bold capitalize">{{ roleName }} Details</h2> |
||||
|
<button @click="close" class="text-gray-500 hover:text-gray-700"> |
||||
|
<fa icon="times" /> |
||||
|
</button> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Content --> |
||||
|
<!-- This is somewhat similar to ClaimView.vue and ConfirmGiftView.vue --> |
||||
|
<div class="mb-4"> |
||||
|
<p class="mb-4"> |
||||
|
<span v-if="R.isEmpty(visibleToDids)"> |
||||
|
The {{ roleName }} is not visible to you or any of your contacts. |
||||
|
</span> |
||||
|
<span v-else> The {{ roleName }} is not visible to you. </span> |
||||
|
</p> |
||||
|
|
||||
|
<div v-if="R.isEmpty(visibleToDids)"> |
||||
|
<p class="mt-2"> |
||||
|
You can ask one of your contacts to take a look and see if their |
||||
|
contacts can see more details. Someone is connected to people closer |
||||
|
to them; if you don't know who to ask, try the person who registered |
||||
|
you. |
||||
|
</p> |
||||
|
</div> |
||||
|
|
||||
|
<div v-else> |
||||
|
<p class="mb-2"> |
||||
|
They are visible to some of your contacts. If you'd like an |
||||
|
introduction, ask them if they'll tell you more. |
||||
|
</p> |
||||
|
|
||||
|
<div class="ml-4"> |
||||
|
<ul> |
||||
|
<li |
||||
|
v-for="(visDid, idx) of visibleToDids" |
||||
|
:key="idx" |
||||
|
class="list-disc ml-4 mb-2" |
||||
|
> |
||||
|
<div class="text-sm"> |
||||
|
<span> |
||||
|
{{ didInfo(visDid) }} |
||||
|
<span v-if="!serverUtil.isEmptyOrHiddenDid(visDid)"> |
||||
|
<a |
||||
|
:href="`/did/${visDid}`" |
||||
|
target="_blank" |
||||
|
class="text-blue-500" |
||||
|
> |
||||
|
<fa icon="arrow-up-right-from-square" class="fa-fw" /> |
||||
|
</a> |
||||
|
</span> |
||||
|
</span> |
||||
|
</div> |
||||
|
</li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="mt-4"> |
||||
|
<span v-if="canShare"> |
||||
|
If you'd like an introduction, |
||||
|
<a @click="onClickShareClaim()" class="text-blue-500" |
||||
|
>click here to share the information with them and ask if they'll |
||||
|
tell you more about the {{ roleName }}.</a |
||||
|
> |
||||
|
</span> |
||||
|
<span v-else> |
||||
|
If you'd like an introduction, |
||||
|
<a |
||||
|
@click="copyToClipboard('A link to this page', windowLocation)" |
||||
|
class="text-blue-500" |
||||
|
>click here to copy this page, paste it into a message, and ask if |
||||
|
they'll tell you more about the {{ roleName }}.</a |
||||
|
> |
||||
|
</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Footer --> |
||||
|
<div class="flex justify-end"> |
||||
|
<button |
||||
|
@click="close" |
||||
|
class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600" |
||||
|
> |
||||
|
Close |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts"> |
||||
|
import { Component, Vue } from "vue-facing-decorator"; |
||||
|
import * as R from "ramda"; |
||||
|
import { useClipboard } from "@vueuse/core"; |
||||
|
import { Contact } from "@/db/tables/contacts"; |
||||
|
import * as serverUtil from "@/libs/endorserServer"; |
||||
|
import { NotificationIface } from "@/constants/app"; |
||||
|
|
||||
|
@Component |
||||
|
export default class HiddenDidDialog extends Vue { |
||||
|
$notify!: (notification: NotificationIface, timeout?: number) => void; |
||||
|
|
||||
|
isOpen = false; |
||||
|
roleName = ""; |
||||
|
visibleToDids: string[] = []; |
||||
|
allContacts: Array<Contact> = []; |
||||
|
activeDid = ""; |
||||
|
allMyDids: Array<string> = []; |
||||
|
canShare = false; |
||||
|
windowLocation = window.location.href; |
||||
|
|
||||
|
R = R; |
||||
|
serverUtil = serverUtil; |
||||
|
|
||||
|
created() { |
||||
|
// When Chrome compatibility is fixed https://developer.mozilla.org/en-US/docs/Web/API/Web_Share_API#api.navigator.canshare |
||||
|
// then use this truer check: navigator.canShare && navigator.canShare() |
||||
|
this.canShare = !!navigator.share; |
||||
|
} |
||||
|
|
||||
|
open( |
||||
|
roleName: string, |
||||
|
visibleToDids: string[], |
||||
|
allContacts: Array<Contact>, |
||||
|
activeDid: string, |
||||
|
allMyDids: Array<string>, |
||||
|
) { |
||||
|
this.roleName = roleName; |
||||
|
this.visibleToDids = visibleToDids; |
||||
|
this.allContacts = allContacts; |
||||
|
this.activeDid = activeDid; |
||||
|
this.allMyDids = allMyDids; |
||||
|
this.isOpen = true; |
||||
|
} |
||||
|
|
||||
|
close() { |
||||
|
this.isOpen = false; |
||||
|
} |
||||
|
|
||||
|
didInfo(did: string) { |
||||
|
return serverUtil.didInfo( |
||||
|
did, |
||||
|
this.activeDid, |
||||
|
this.allMyDids, |
||||
|
this.allContacts, |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
copyToClipboard(name: string, text: string) { |
||||
|
useClipboard() |
||||
|
.copy(text) |
||||
|
.then(() => { |
||||
|
this.$notify( |
||||
|
{ |
||||
|
group: "alert", |
||||
|
type: "toast", |
||||
|
title: "Copied", |
||||
|
text: (name || "That") + " was copied to the clipboard.", |
||||
|
}, |
||||
|
2000, |
||||
|
); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
onClickShareClaim() { |
||||
|
this.copyToClipboard("A link to this page", this.windowLocation); |
||||
|
window.navigator.share({ |
||||
|
title: "Help Connect Me", |
||||
|
text: "I'm trying to find the people who recorded this. Can you help me?", |
||||
|
url: this.windowLocation, |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
</script> |
@ -0,0 +1,9 @@ |
|||||
|
export interface UserProfile { |
||||
|
description: string; |
||||
|
locLat?: number; |
||||
|
locLon?: number; |
||||
|
locLat2?: number; |
||||
|
locLon2?: number; |
||||
|
issuerDid: string; |
||||
|
rowId?: string; // set on profile retrieved from server
|
||||
|
} |
@ -0,0 +1,184 @@ |
|||||
|
<template> |
||||
|
<QuickNav selected="Discover" /> |
||||
|
<TopMessage /> |
||||
|
|
||||
|
<!-- CONTENT --> |
||||
|
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto"> |
||||
|
<!-- Breadcrumb --> |
||||
|
<div id="ViewBreadcrumb" class="mb-8"> |
||||
|
<h1 id="ViewHeading" class="text-lg text-center font-light relative px-7"> |
||||
|
<!-- Back --> |
||||
|
<button |
||||
|
@click="$router.go(-1)" |
||||
|
class="text-lg text-center px-2 py-1 absolute -left-2 -top-1" |
||||
|
> |
||||
|
<fa icon="chevron-left" class="fa-fw"></fa> |
||||
|
</button> |
||||
|
Individual Profile |
||||
|
</h1> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Loading Animation --> |
||||
|
<div |
||||
|
class="fixed left-6 mt-16 text-center text-4xl leading-none bg-slate-400 text-white w-14 py-2.5 rounded-full" |
||||
|
v-if="isLoading" |
||||
|
> |
||||
|
<fa icon="spinner" class="fa-spin-pulse"></fa> |
||||
|
</div> |
||||
|
|
||||
|
<div v-else-if="profile"> |
||||
|
<!-- Profile Info --> |
||||
|
<div class="mt-8"> |
||||
|
<div class="text-sm"> |
||||
|
<fa icon="user" class="fa-fw text-slate-400"></fa> |
||||
|
{{ didInfo(profile.issuerDid, activeDid, allMyDids, allContacts) }} |
||||
|
</div> |
||||
|
<p v-if="profile.description" class="mt-4 text-slate-600"> |
||||
|
{{ profile.description }} |
||||
|
</p> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Map for first coordinates --> |
||||
|
<div v-if="profile?.locLat && profile?.locLon" class="mt-4"> |
||||
|
<h2 class="text-lg font-semibold">Location</h2> |
||||
|
<div class="h-96 mt-2 w-full"> |
||||
|
<l-map |
||||
|
ref="profileMap" |
||||
|
:center="[profile.locLat, profile.locLon]" |
||||
|
:zoom="12" |
||||
|
> |
||||
|
<l-tile-layer |
||||
|
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" |
||||
|
layer-type="base" |
||||
|
name="OpenStreetMap" |
||||
|
/> |
||||
|
<l-marker :lat-lng="[profile.locLat, profile.locLon]"> |
||||
|
<l-popup>{{ |
||||
|
didInfo(profile.issuerDid, activeDid, allMyDids, allContacts) |
||||
|
}}</l-popup> |
||||
|
</l-marker> |
||||
|
</l-map> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Map for second coordinates --> |
||||
|
<div v-if="profile?.locLat2 && profile?.locLon2" class="mt-4"> |
||||
|
<h2 class="text-lg font-semibold">Second Location</h2> |
||||
|
<div class="h-96 mt-2 w-full"> |
||||
|
<l-map |
||||
|
ref="profileMap" |
||||
|
:center="[profile.locLat2, profile.locLon2]" |
||||
|
:zoom="12" |
||||
|
> |
||||
|
<l-tile-layer |
||||
|
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" |
||||
|
layer-type="base" |
||||
|
name="OpenStreetMap" |
||||
|
/> |
||||
|
<l-marker :lat-lng="[profile.locLat2, profile.locLon2]"> |
||||
|
<l-popup>{{ |
||||
|
didInfo(profile.issuerDid, activeDid, allMyDids, allContacts) |
||||
|
}}</l-popup> |
||||
|
</l-marker> |
||||
|
</l-map> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div v-else class="text-center mt-8"> |
||||
|
<p class="text-lg text-slate-500">Profile not found.</p> |
||||
|
</div> |
||||
|
</section> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts"> |
||||
|
import "leaflet/dist/leaflet.css"; |
||||
|
import { Component, Vue } from "vue-facing-decorator"; |
||||
|
import { LMap, LTileLayer, LMarker, LPopup } from "@vue-leaflet/vue-leaflet"; |
||||
|
import { Router, RouteLocationNormalizedLoaded } from "vue-router"; |
||||
|
|
||||
|
import QuickNav from "@/components/QuickNav.vue"; |
||||
|
import TopMessage from "@/components/TopMessage.vue"; |
||||
|
import { DEFAULT_PARTNER_API_SERVER, NotificationIface } from "@/constants/app"; |
||||
|
import { db } from "@/db/index"; |
||||
|
import { Contact } from "@/db/tables/contacts"; |
||||
|
import { didInfo, getHeaders } from "@/libs/endorserServer"; |
||||
|
import { UserProfile } from "@/libs/partnerServer"; |
||||
|
import { retrieveAccountDids } from "@/libs/util"; |
||||
|
|
||||
|
@Component({ |
||||
|
components: { |
||||
|
LMap, |
||||
|
LMarker, |
||||
|
LPopup, |
||||
|
LTileLayer, |
||||
|
QuickNav, |
||||
|
TopMessage, |
||||
|
}, |
||||
|
}) |
||||
|
export default class UserProfileView extends Vue { |
||||
|
$notify!: (notification: NotificationIface, timeout?: number) => void; |
||||
|
$router!: Router; |
||||
|
$route!: RouteLocationNormalizedLoaded; |
||||
|
|
||||
|
activeDid = ""; |
||||
|
allContacts: Array<Contact> = []; |
||||
|
allMyDids: Array<string> = []; |
||||
|
isLoading = true; |
||||
|
partnerApiServer = DEFAULT_PARTNER_API_SERVER; |
||||
|
profile: UserProfile | null = null; |
||||
|
|
||||
|
// make this function available to the Vue template |
||||
|
didInfo = didInfo; |
||||
|
|
||||
|
async mounted() { |
||||
|
const settings = await db.settings.toArray(); |
||||
|
this.activeDid = settings[0]?.activeDid || ""; |
||||
|
this.partnerApiServer = |
||||
|
settings[0]?.partnerApiServer || this.partnerApiServer; |
||||
|
|
||||
|
this.allContacts = await db.contacts.toArray(); |
||||
|
this.allMyDids = await retrieveAccountDids(); |
||||
|
|
||||
|
await this.loadProfile(); |
||||
|
} |
||||
|
|
||||
|
async loadProfile() { |
||||
|
const profileId: string = this.$route.params.id as string; |
||||
|
if (!profileId) { |
||||
|
this.isLoading = false; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
const response = await fetch( |
||||
|
`${this.partnerApiServer}/api/partner/userProfile/${encodeURIComponent(profileId)}`, |
||||
|
{ |
||||
|
method: "GET", |
||||
|
headers: await getHeaders(this.activeDid), |
||||
|
}, |
||||
|
); |
||||
|
|
||||
|
if (response.status === 200) { |
||||
|
const result = await response.json(); |
||||
|
this.profile = result.data; |
||||
|
} else { |
||||
|
throw new Error("Failed to load profile"); |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error("Error loading profile:", error); |
||||
|
this.$notify( |
||||
|
{ |
||||
|
group: "alert", |
||||
|
type: "danger", |
||||
|
title: "Error", |
||||
|
text: "There was a problem loading the profile.", |
||||
|
}, |
||||
|
5000, |
||||
|
); |
||||
|
} finally { |
||||
|
this.isLoading = false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
Loading…
Reference in new issue