161 changed files with 12196 additions and 11612 deletions
@ -1,32 +0,0 @@ |
|||||
module.exports = { |
|
||||
root: true, |
|
||||
env: { |
|
||||
node: true, |
|
||||
es2022: true, |
|
||||
}, |
|
||||
extends: [ |
|
||||
"plugin:vue/vue3-recommended", |
|
||||
"eslint:recommended", |
|
||||
"@vue/typescript/recommended", |
|
||||
"plugin:prettier/recommended" |
|
||||
], |
|
||||
// parserOptions: {
|
|
||||
// ecmaVersion: 2020,
|
|
||||
// },
|
|
||||
rules: { |
|
||||
"max-len": ["warn", { |
|
||||
code: 100, |
|
||||
ignoreComments: true, |
|
||||
ignorePattern: '^\\s*class="[^"]*"$', |
|
||||
ignoreStrings: true, |
|
||||
ignoreTemplateLiterals: true, |
|
||||
ignoreUrls: true, |
|
||||
}], |
|
||||
"no-console": process.env.NODE_ENV === "production" ? "error" : "warn", |
|
||||
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "warn", |
|
||||
"@typescript-eslint/no-explicit-any": "warn", |
|
||||
"@typescript-eslint/explicit-function-return-type": "off", |
|
||||
"@typescript-eslint/no-unnecessary-type-constraint": "off", |
|
||||
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }] |
|
||||
}, |
|
||||
}; |
|
||||
@ -0,0 +1,57 @@ |
|||||
|
{ |
||||
|
"root": true, |
||||
|
"env": { |
||||
|
"node": true, |
||||
|
"browser": true, |
||||
|
"es2022": true |
||||
|
}, |
||||
|
"extends": [ |
||||
|
"plugin:vue/vue3-recommended", |
||||
|
"eslint:recommended", |
||||
|
"@vue/typescript/recommended", |
||||
|
"plugin:prettier/recommended" |
||||
|
], |
||||
|
"parser": "vue-eslint-parser", |
||||
|
"parserOptions": { |
||||
|
"parser": "@typescript-eslint/parser", |
||||
|
"ecmaVersion": 2022, |
||||
|
"sourceType": "module", |
||||
|
"extraFileExtensions": [".vue"], |
||||
|
"ecmaFeatures": { |
||||
|
"jsx": true |
||||
|
} |
||||
|
}, |
||||
|
"plugins": [ |
||||
|
"@typescript-eslint", |
||||
|
"vue", |
||||
|
"prettier" |
||||
|
], |
||||
|
"rules": { |
||||
|
"no-console": "warn", |
||||
|
"no-debugger": "warn", |
||||
|
"@typescript-eslint/no-explicit-any": "off", |
||||
|
"vue/multi-word-component-names": "off", |
||||
|
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], |
||||
|
"@typescript-eslint/no-unnecessary-type-constraint": "off", |
||||
|
"vue/no-parsing-error": ["error", { |
||||
|
"x-invalid-end-tag": false, |
||||
|
"invalid-first-character-of-tag-name": false |
||||
|
}], |
||||
|
"vue/no-v-html": "warn", |
||||
|
"prettier/prettier": ["error", { |
||||
|
"singleQuote": true, |
||||
|
"semi": false, |
||||
|
"trailingComma": "none" |
||||
|
}] |
||||
|
}, |
||||
|
"overrides": [ |
||||
|
{ |
||||
|
"files": ["*.ts", "*.tsx", "*.mts"], |
||||
|
"parser": "@typescript-eslint/parser" |
||||
|
}, |
||||
|
{ |
||||
|
"files": ["*.js", "*.jsx", "*.mjs"], |
||||
|
"parser": "@typescript-eslint/parser" |
||||
|
} |
||||
|
] |
||||
|
} |
||||
@ -0,0 +1,55 @@ |
|||||
|
export default { |
||||
|
root: true, |
||||
|
env: { |
||||
|
node: true, |
||||
|
browser: true, |
||||
|
es2022: true |
||||
|
}, |
||||
|
extends: [ |
||||
|
'plugin:vue/vue3-recommended', |
||||
|
'eslint:recommended', |
||||
|
'@vue/typescript/recommended' |
||||
|
], |
||||
|
parser: 'vue-eslint-parser', |
||||
|
parserOptions: { |
||||
|
parser: { |
||||
|
'ts': '@typescript-eslint/parser', |
||||
|
'js': '@typescript-eslint/parser', |
||||
|
'<template>': 'espree' |
||||
|
}, |
||||
|
ecmaVersion: 2022, |
||||
|
sourceType: 'module', |
||||
|
extraFileExtensions: ['.vue'], |
||||
|
ecmaFeatures: { |
||||
|
jsx: true |
||||
|
} |
||||
|
}, |
||||
|
plugins: [ |
||||
|
'@typescript-eslint', |
||||
|
'vue' |
||||
|
], |
||||
|
rules: { |
||||
|
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', |
||||
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', |
||||
|
'@typescript-eslint/no-explicit-any': 'off', |
||||
|
'vue/multi-word-component-names': 'off', |
||||
|
'@typescript-eslint/no-unused-vars': ['error', { 'argsIgnorePattern': '^_' }] |
||||
|
}, |
||||
|
overrides: [ |
||||
|
{ |
||||
|
files: ['*.ts', '*.tsx', '*.mts'], |
||||
|
parser: '@typescript-eslint/parser' |
||||
|
}, |
||||
|
{ |
||||
|
files: ['*.js', '*.jsx', '*.mjs'], |
||||
|
parser: '@typescript-eslint/parser' |
||||
|
} |
||||
|
], |
||||
|
settings: { |
||||
|
'import/resolver': { |
||||
|
node: { |
||||
|
extensions: ['.js', '.jsx', '.ts', '.tsx', '.vue', '.mjs', '.mts'] |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
@ -0,0 +1,124 @@ |
|||||
|
import { |
||||
|
BarcodeScanner, |
||||
|
BarcodeFormat, |
||||
|
LensFacing, |
||||
|
ScanResult |
||||
|
} from '@capacitor-mlkit/barcode-scanning' |
||||
|
import type { QRScannerService, ScanListener } from './types' |
||||
|
import { logger } from '../../utils/logger' |
||||
|
|
||||
|
export class CapacitorQRScanner implements QRScannerService { |
||||
|
private scanListener: ScanListener | null = null |
||||
|
private isScanning = false |
||||
|
private listenerHandles: Array<() => Promise<void>> = [] |
||||
|
|
||||
|
async checkPermissions() { |
||||
|
try { |
||||
|
const { camera } = await BarcodeScanner.checkPermissions() |
||||
|
return camera === 'granted' |
||||
|
} catch (error) { |
||||
|
logger.error('Error checking camera permissions:', error) |
||||
|
return false |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async requestPermissions() { |
||||
|
try { |
||||
|
const { camera } = await BarcodeScanner.requestPermissions() |
||||
|
return camera === 'granted' |
||||
|
} catch (error) { |
||||
|
logger.error('Error requesting camera permissions:', error) |
||||
|
return false |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async isSupported() { |
||||
|
try { |
||||
|
const { supported } = await BarcodeScanner.isSupported() |
||||
|
return supported |
||||
|
} catch (error) { |
||||
|
logger.error('Error checking barcode scanner support:', error) |
||||
|
return false |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async startScan() { |
||||
|
if (this.isScanning) { |
||||
|
logger.warn('Scanner is already active') |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
// First register listeners before starting scan
|
||||
|
await this.registerListeners() |
||||
|
|
||||
|
this.isScanning = true |
||||
|
await BarcodeScanner.startScan({ |
||||
|
formats: [BarcodeFormat.QrCode], |
||||
|
lensFacing: LensFacing.Back |
||||
|
}) |
||||
|
} catch (error) { |
||||
|
// Ensure cleanup on error
|
||||
|
this.isScanning = false |
||||
|
await this.removeListeners() |
||||
|
logger.error('Error starting barcode scan:', error) |
||||
|
throw error |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private async registerListeners() { |
||||
|
try { |
||||
|
const handle = await BarcodeScanner.addListener( |
||||
|
'barcodesScanned', |
||||
|
async (result: ScanResult) => { |
||||
|
if (result.barcodes.length > 0 && this.scanListener) { |
||||
|
const barcode = result.barcodes[0] |
||||
|
this.scanListener.onScan(barcode.rawValue) |
||||
|
await this.stopScan() |
||||
|
} |
||||
|
} |
||||
|
) |
||||
|
this.listenerHandles.push(() => handle.remove()) |
||||
|
} catch (error) { |
||||
|
logger.error('Error registering barcode listener:', error) |
||||
|
throw error |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private async removeListeners() { |
||||
|
for (const remove of this.listenerHandles) { |
||||
|
try { |
||||
|
await remove() |
||||
|
} catch (error) { |
||||
|
logger.error('Error removing listener:', error) |
||||
|
} |
||||
|
} |
||||
|
this.listenerHandles = [] |
||||
|
} |
||||
|
|
||||
|
async stopScan() { |
||||
|
if (!this.isScanning) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
// First stop the scan
|
||||
|
await BarcodeScanner.stopScan() |
||||
|
} catch (error) { |
||||
|
logger.error('Error stopping barcode scan:', error) |
||||
|
} finally { |
||||
|
// Always cleanup state even if stop fails
|
||||
|
this.isScanning = false |
||||
|
await this.removeListeners() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
addListener(listener: ScanListener): void { |
||||
|
this.scanListener = listener |
||||
|
} |
||||
|
|
||||
|
async cleanup(): Promise<void> { |
||||
|
await this.stopScan() |
||||
|
this.scanListener = null |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,74 @@ |
|||||
|
import type { QRScannerService, ScanListener } from './types' |
||||
|
import QRScannerDialog from './QRScannerDialog.vue' |
||||
|
import { createApp, type App } from 'vue' |
||||
|
import { logger } from '../../utils/logger' |
||||
|
|
||||
|
// Import platform-specific flags from Vite config
|
||||
|
declare const __USE_QR_READER__: boolean |
||||
|
|
||||
|
export class WebDialogQRScanner implements QRScannerService { |
||||
|
private dialogApp: App | null = null |
||||
|
private dialogElement: HTMLDivElement | null = null |
||||
|
private scanListener: ScanListener | null = null |
||||
|
|
||||
|
async checkPermissions(): Promise<boolean> { |
||||
|
return navigator?.mediaDevices !== undefined |
||||
|
} |
||||
|
|
||||
|
async requestPermissions(): Promise<boolean> { |
||||
|
try { |
||||
|
const stream = await navigator.mediaDevices.getUserMedia({ video: true }) |
||||
|
stream.getTracks().forEach((track) => track.stop()) |
||||
|
return true |
||||
|
} catch (error) { |
||||
|
logger.error('Failed to get camera permissions:', error) |
||||
|
return false |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async isSupported(): Promise<boolean> { |
||||
|
return Promise.resolve( |
||||
|
__USE_QR_READER__ && navigator?.mediaDevices !== undefined |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
async startScan(): Promise<void> { |
||||
|
if (!(await this.isSupported())) { |
||||
|
throw new Error('QR scanning is not supported in this environment') |
||||
|
} |
||||
|
|
||||
|
this.dialogElement = document.createElement('div') |
||||
|
document.body.appendChild(this.dialogElement) |
||||
|
|
||||
|
this.dialogApp = createApp(QRScannerDialog, { |
||||
|
onScan: (result: string) => { |
||||
|
if (this.scanListener) { |
||||
|
this.scanListener.onScan(result) |
||||
|
} |
||||
|
}, |
||||
|
onClose: () => { |
||||
|
this.stopScan() |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
this.dialogApp.mount(this.dialogElement) |
||||
|
} |
||||
|
|
||||
|
async stopScan(): Promise<void> { |
||||
|
if (this.dialogApp && this.dialogElement) { |
||||
|
this.dialogApp.unmount() |
||||
|
this.dialogElement.remove() |
||||
|
this.dialogApp = null |
||||
|
this.dialogElement = null |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
addListener(listener: ScanListener): void { |
||||
|
this.scanListener = listener |
||||
|
} |
||||
|
|
||||
|
async cleanup(): Promise<void> { |
||||
|
await this.stopScan() |
||||
|
this.scanListener = null |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,38 @@ |
|||||
|
import { Capacitor } from '@capacitor/core' |
||||
|
import type { QRScannerService } from './types' |
||||
|
import { logger } from '../../utils/logger' |
||||
|
import { WebDialogQRScanner } from './WebDialogScanner' |
||||
|
import { CapacitorQRScanner } from './CapacitorScanner' |
||||
|
|
||||
|
// Import platform-specific flags from Vite config
|
||||
|
declare const __USE_QR_READER__: boolean |
||||
|
declare const __IS_MOBILE__: boolean |
||||
|
|
||||
|
export class QRScannerFactory { |
||||
|
private static instance: QRScannerService | null = null |
||||
|
|
||||
|
static getInstance(): QRScannerService { |
||||
|
if (!this.instance) { |
||||
|
// Use platform-specific flags for more accurate detection
|
||||
|
if (__IS_MOBILE__ || Capacitor.isNativePlatform()) { |
||||
|
logger.log('Creating native QR scanner instance') |
||||
|
this.instance = new CapacitorQRScanner() |
||||
|
} else if (__USE_QR_READER__) { |
||||
|
logger.log('Creating web QR scanner instance') |
||||
|
this.instance = new WebDialogQRScanner() |
||||
|
} else { |
||||
|
throw new Error( |
||||
|
'No QR scanner implementation available for this platform' |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
return this.instance |
||||
|
} |
||||
|
|
||||
|
static async cleanup() { |
||||
|
if (this.instance) { |
||||
|
await this.instance.cleanup() |
||||
|
this.instance = null |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
export interface ScanListener { |
||||
|
onScan: (result: string) => void |
||||
|
onError?: (error: Error) => void |
||||
|
} |
||||
|
|
||||
|
export interface QRScannerService { |
||||
|
checkPermissions(): Promise<boolean> |
||||
|
requestPermissions(): Promise<boolean> |
||||
|
isSupported(): Promise<boolean> |
||||
|
startScan(): Promise<void> |
||||
|
stopScan(): Promise<void> |
||||
|
addListener(listener: ScanListener): void |
||||
|
cleanup(): Promise<void> |
||||
|
} |
||||
@ -1,19 +1,19 @@ |
|||||
import { PerspectiveCamera } from "three"; |
import { PerspectiveCamera } from 'three' |
||||
|
|
||||
function createCamera() { |
function createCamera() { |
||||
const camera = new PerspectiveCamera( |
const camera = new PerspectiveCamera( |
||||
35, // fov = Field Of View
|
35, // fov = Field Of View
|
||||
1, // aspect ratio (dummy value)
|
1, // aspect ratio (dummy value)
|
||||
0.1, // near clipping plane
|
0.1, // near clipping plane
|
||||
350, // far clipping plane
|
350 // far clipping plane
|
||||
); |
) |
||||
|
|
||||
// move the camera back so we can view the scene
|
// move the camera back so we can view the scene
|
||||
camera.position.set(0, 100, 200); |
camera.position.set(0, 100, 200) |
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
camera.tick = () => {}; |
camera.tick = () => {} |
||||
|
|
||||
return camera; |
return camera |
||||
} |
} |
||||
|
|
||||
export { createCamera }; |
export { createCamera } |
||||
|
|||||
@ -1,14 +1,14 @@ |
|||||
import { DirectionalLight, DirectionalLightHelper } from "three"; |
import { DirectionalLight, DirectionalLightHelper } from 'three' |
||||
|
|
||||
function createLights(color) { |
function createLights(color) { |
||||
const light = new DirectionalLight(color, 4); |
const light = new DirectionalLight(color, 4) |
||||
const lightHelper = new DirectionalLightHelper(light, 0); |
const lightHelper = new DirectionalLightHelper(light, 0) |
||||
light.position.set(60, 100, 30); |
light.position.set(60, 100, 30) |
||||
|
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
light.tick = () => {}; |
light.tick = () => {} |
||||
|
|
||||
return { light, lightHelper }; |
return { light, lightHelper } |
||||
} |
} |
||||
|
|
||||
export { createLights }; |
export { createLights } |
||||
|
|||||
@ -1,29 +1,29 @@ |
|||||
import { PlaneGeometry, MeshLambertMaterial, Mesh, TextureLoader } from "three"; |
import { PlaneGeometry, MeshLambertMaterial, Mesh, TextureLoader } from 'three' |
||||
|
|
||||
export function createTerrain(props) { |
export function createTerrain(props) { |
||||
const loader = new TextureLoader(); |
const loader = new TextureLoader() |
||||
const height = loader.load("img/textures/leafy-autumn-forest-floor.jpg"); |
const height = loader.load('img/textures/leafy-autumn-forest-floor.jpg') |
||||
// w h
|
// w h
|
||||
const geometry = new PlaneGeometry(props.width, props.height, 64, 64); |
const geometry = new PlaneGeometry(props.width, props.height, 64, 64) |
||||
|
|
||||
const material = new MeshLambertMaterial({ |
const material = new MeshLambertMaterial({ |
||||
color: props.color, |
color: props.color, |
||||
flatShading: true, |
flatShading: true, |
||||
map: height, |
map: height |
||||
//displacementMap: height,
|
//displacementMap: height,
|
||||
//displacementScale: 5,
|
//displacementScale: 5,
|
||||
}); |
}) |
||||
|
|
||||
const plane = new Mesh(geometry, material); |
const plane = new Mesh(geometry, material) |
||||
plane.position.set(0, 0, 0); |
plane.position.set(0, 0, 0) |
||||
plane.rotation.x -= Math.PI * 0.5; |
plane.rotation.x -= Math.PI * 0.5 |
||||
|
|
||||
//Storing our original vertices position on a new attribute
|
//Storing our original vertices position on a new attribute
|
||||
plane.geometry.attributes.position.originalPosition = |
plane.geometry.attributes.position.originalPosition = |
||||
plane.geometry.attributes.position.array; |
plane.geometry.attributes.position.array |
||||
|
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
plane.tick = () => {}; |
plane.tick = () => {} |
||||
|
|
||||
return plane; |
return plane |
||||
} |
} |
||||
|
|||||
@ -1,11 +1,11 @@ |
|||||
import { Color, Scene } from "three"; |
import { Color, Scene } from 'three' |
||||
|
|
||||
function createScene(color) { |
function createScene(color) { |
||||
const scene = new Scene(); |
const scene = new Scene() |
||||
|
|
||||
scene.background = new Color(color); |
scene.background = new Color(color) |
||||
//scene.fog = new Fog(color, 60, 90);
|
//scene.fog = new Fog(color, 60, 90);
|
||||
return scene; |
return scene |
||||
} |
} |
||||
|
|
||||
export { createScene }; |
export { createScene } |
||||
|
|||||
@ -1,33 +1,33 @@ |
|||||
import { Clock } from "three"; |
import { Clock } from 'three' |
||||
|
|
||||
const clock = new Clock(); |
const clock = new Clock() |
||||
|
|
||||
class Loop { |
class Loop { |
||||
constructor(camera, scene, renderer) { |
constructor(camera, scene, renderer) { |
||||
this.camera = camera; |
this.camera = camera |
||||
this.scene = scene; |
this.scene = scene |
||||
this.renderer = renderer; |
this.renderer = renderer |
||||
this.updatables = []; |
this.updatables = [] |
||||
} |
} |
||||
|
|
||||
start() { |
start() { |
||||
this.renderer.setAnimationLoop(() => { |
this.renderer.setAnimationLoop(() => { |
||||
this.tick(); |
this.tick() |
||||
// render a frame
|
// render a frame
|
||||
this.renderer.render(this.scene, this.camera); |
this.renderer.render(this.scene, this.camera) |
||||
}); |
}) |
||||
} |
} |
||||
|
|
||||
stop() { |
stop() { |
||||
this.renderer.setAnimationLoop(null); |
this.renderer.setAnimationLoop(null) |
||||
} |
} |
||||
|
|
||||
tick() { |
tick() { |
||||
const delta = clock.getDelta(); |
const delta = clock.getDelta() |
||||
for (const object of this.updatables) { |
for (const object of this.updatables) { |
||||
object.tick(delta); |
object.tick(delta) |
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
export { Loop }; |
export { Loop } |
||||
|
|||||
@ -1,33 +1,33 @@ |
|||||
const setSize = (container, camera, renderer) => { |
const setSize = (container, camera, renderer) => { |
||||
// These are great for full-screen, which adjusts to a window.
|
// These are great for full-screen, which adjusts to a window.
|
||||
const height = window.innerHeight; |
const height = window.innerHeight |
||||
const width = window.innerWidth - 50; |
const width = window.innerWidth - 50 |
||||
// These are better for fitting in a container, which stays that size.
|
// These are better for fitting in a container, which stays that size.
|
||||
//const height = container.scrollHeight;
|
//const height = container.scrollHeight;
|
||||
//const width = container.scrollWidth;
|
//const width = container.scrollWidth;
|
||||
|
|
||||
camera.aspect = width / height; |
camera.aspect = width / height |
||||
camera.updateProjectionMatrix(); |
camera.updateProjectionMatrix() |
||||
|
|
||||
renderer.setSize(width, height); |
renderer.setSize(width, height) |
||||
renderer.setPixelRatio(window.devicePixelRatio); |
renderer.setPixelRatio(window.devicePixelRatio) |
||||
}; |
} |
||||
|
|
||||
class Resizer { |
class Resizer { |
||||
constructor(container, camera, renderer) { |
constructor(container, camera, renderer) { |
||||
// set initial size on load
|
// set initial size on load
|
||||
setSize(container, camera, renderer); |
setSize(container, camera, renderer) |
||||
|
|
||||
window.addEventListener("resize", () => { |
window.addEventListener('resize', () => { |
||||
// set the size again if a resize occurs
|
// set the size again if a resize occurs
|
||||
setSize(container, camera, renderer); |
setSize(container, camera, renderer) |
||||
// perform any custom actions
|
// perform any custom actions
|
||||
this.onResize(); |
this.onResize() |
||||
}); |
}) |
||||
} |
} |
||||
|
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
onResize() {} |
onResize() {} |
||||
} |
} |
||||
|
|
||||
export { Resizer }; |
export { Resizer } |
||||
|
|||||
@ -1,13 +1,13 @@ |
|||||
import { WebGLRenderer } from "three"; |
import { WebGLRenderer } from 'three' |
||||
|
|
||||
function createRenderer() { |
function createRenderer() { |
||||
const renderer = new WebGLRenderer({ antialias: true }); |
const renderer = new WebGLRenderer({ antialias: true }) |
||||
|
|
||||
// turn on the physically correct lighting model
|
// turn on the physically correct lighting model
|
||||
// (The browser complains: "THREE.WebGLRenderer: the property .physicallyCorrectLights has been removed. Set renderer.useLegacyLights instead." However, that changes the lighting in a way that doesn't look better.)
|
// (The browser complains: "THREE.WebGLRenderer: the property .physicallyCorrectLights has been removed. Set renderer.useLegacyLights instead." However, that changes the lighting in a way that doesn't look better.)
|
||||
renderer.physicallyCorrectLights = true; |
renderer.physicallyCorrectLights = true |
||||
|
|
||||
return renderer; |
return renderer |
||||
} |
} |
||||
|
|
||||
export { createRenderer }; |
export { createRenderer } |
||||
|
|||||
@ -1,24 +1,24 @@ |
|||||
export interface ContactMethod { |
export interface ContactMethod { |
||||
label: string; |
label: string |
||||
type: string; // eg. "EMAIL", "SMS", "WHATSAPP", maybe someday "GOOGLE-CONTACT-API"
|
type: string // eg. "EMAIL", "SMS", "WHATSAPP", maybe someday "GOOGLE-CONTACT-API"
|
||||
value: string; |
value: string |
||||
} |
} |
||||
|
|
||||
export interface Contact { |
export interface Contact { |
||||
//
|
//
|
||||
// When adding a property, consider whether it should be added when exporting & sharing contacts.
|
// When adding a property, consider whether it should be added when exporting & sharing contacts.
|
||||
|
|
||||
did: string; |
did: string |
||||
contactMethods?: Array<ContactMethod>; |
contactMethods?: Array<ContactMethod> |
||||
name?: string; |
name?: string |
||||
nextPubKeyHashB64?: string; // base64-encoded SHA256 hash of next public key
|
nextPubKeyHashB64?: string // base64-encoded SHA256 hash of next public key
|
||||
notes?: string; |
notes?: string |
||||
profileImageUrl?: string; |
profileImageUrl?: string |
||||
publicKeyBase64?: string; |
publicKeyBase64?: string |
||||
seesMe?: boolean; // cached value of the server setting
|
seesMe?: boolean // cached value of the server setting
|
||||
registered?: boolean; // cached value of the server setting
|
registered?: boolean // cached value of the server setting
|
||||
} |
} |
||||
|
|
||||
export const ContactSchema = { |
export const ContactSchema = { |
||||
contacts: "&did, name", // no need to key by other things
|
contacts: '&did, name' // no need to key by other things
|
||||
}; |
} |
||||
|
|||||
@ -1,11 +1,11 @@ |
|||||
export interface Log { |
export interface Log { |
||||
date: string; |
date: string |
||||
message: string; |
message: string |
||||
} |
} |
||||
|
|
||||
export const LogSchema = { |
export const LogSchema = { |
||||
// Currently keyed by "date" because A) today's log data is what we need so we append, and
|
// Currently keyed by "date" because A) today's log data is what we need so we append, and
|
||||
// B) we don't want it to grow so we remove everything if this is the first entry today.
|
// B) we don't want it to grow so we remove everything if this is the first entry today.
|
||||
// See safari-notifications.js logMessage for the associated logic.
|
// See safari-notifications.js logMessage for the associated logic.
|
||||
logs: "date", // definitely don't key by the potentially large message field
|
logs: 'date' // definitely don't key by the potentially large message field
|
||||
}; |
} |
||||
|
|||||
@ -1,12 +1,12 @@ |
|||||
// for ephemeral uses, eg. passing a blob from the service worker to the main thread
|
// for ephemeral uses, eg. passing a blob from the service worker to the main thread
|
||||
|
|
||||
export type Temp = { |
export type Temp = { |
||||
id: string; |
id: string |
||||
blob?: Blob; // deprecated because webkit (Safari) does not support Blob
|
blob?: Blob // deprecated because webkit (Safari) does not support Blob
|
||||
blobB64?: string; // base64-encoded blob
|
blobB64?: string // base64-encoded blob
|
||||
}; |
} |
||||
|
|
||||
/** |
/** |
||||
* Schema for the Temp table in the database. |
* Schema for the Temp table in the database. |
||||
*/ |
*/ |
||||
export const TempSchema = { temp: "id" }; |
export const TempSchema = { temp: 'id' } |
||||
|
|||||
@ -1,78 +1,78 @@ |
|||||
const { contextBridge, ipcRenderer } = require("electron"); |
const { contextBridge, ipcRenderer } = require('electron') |
||||
|
|
||||
const logger = { |
const logger = { |
||||
log: (message, ...args) => { |
log: (message, ...args) => { |
||||
if (process.env.NODE_ENV !== "production") { |
if (process.env.NODE_ENV !== 'production') { |
||||
/* eslint-disable no-console */ |
/* eslint-disable no-console */ |
||||
console.log(message, ...args); |
console.log(message, ...args) |
||||
/* eslint-enable no-console */ |
/* eslint-enable no-console */ |
||||
} |
} |
||||
}, |
}, |
||||
warn: (message, ...args) => { |
warn: (message, ...args) => { |
||||
if (process.env.NODE_ENV !== "production") { |
if (process.env.NODE_ENV !== 'production') { |
||||
/* eslint-disable no-console */ |
/* eslint-disable no-console */ |
||||
console.warn(message, ...args); |
console.warn(message, ...args) |
||||
/* eslint-enable no-console */ |
/* eslint-enable no-console */ |
||||
} |
} |
||||
}, |
}, |
||||
error: (message, ...args) => { |
error: (message, ...args) => { |
||||
/* eslint-disable no-console */ |
/* eslint-disable no-console */ |
||||
console.error(message, ...args); // Errors should always be logged
|
console.error(message, ...args) // Errors should always be logged
|
||||
/* eslint-enable no-console */ |
/* eslint-enable no-console */ |
||||
}, |
} |
||||
}; |
} |
||||
|
|
||||
// Use a more direct path resolution approach
|
// Use a more direct path resolution approach
|
||||
const getPath = (pathType) => { |
const getPath = (pathType) => { |
||||
switch (pathType) { |
switch (pathType) { |
||||
case "userData": |
case 'userData': |
||||
return ( |
return ( |
||||
process.env.APPDATA || |
process.env.APPDATA || |
||||
(process.platform === "darwin" |
(process.platform === 'darwin' |
||||
? `${process.env.HOME}/Library/Application Support` |
? `${process.env.HOME}/Library/Application Support` |
||||
: `${process.env.HOME}/.local/share`) |
: `${process.env.HOME}/.local/share`) |
||||
); |
) |
||||
case "home": |
case 'home': |
||||
return process.env.HOME; |
return process.env.HOME |
||||
case "appPath": |
case 'appPath': |
||||
return process.resourcesPath; |
return process.resourcesPath |
||||
default: |
default: |
||||
return ""; |
return '' |
||||
} |
} |
||||
}; |
} |
||||
|
|
||||
logger.log("Preload script starting..."); |
logger.log('Preload script starting...') |
||||
|
|
||||
try { |
try { |
||||
contextBridge.exposeInMainWorld("electronAPI", { |
contextBridge.exposeInMainWorld('electronAPI', { |
||||
// Path utilities
|
// Path utilities
|
||||
getPath, |
getPath, |
||||
|
|
||||
// IPC functions
|
// IPC functions
|
||||
send: (channel, data) => { |
send: (channel, data) => { |
||||
const validChannels = ["toMain"]; |
const validChannels = ['toMain'] |
||||
if (validChannels.includes(channel)) { |
if (validChannels.includes(channel)) { |
||||
ipcRenderer.send(channel, data); |
ipcRenderer.send(channel, data) |
||||
} |
} |
||||
}, |
}, |
||||
receive: (channel, func) => { |
receive: (channel, func) => { |
||||
const validChannels = ["fromMain"]; |
const validChannels = ['fromMain'] |
||||
if (validChannels.includes(channel)) { |
if (validChannels.includes(channel)) { |
||||
ipcRenderer.on(channel, (event, ...args) => func(...args)); |
ipcRenderer.on(channel, (event, ...args) => func(...args)) |
||||
} |
} |
||||
}, |
}, |
||||
// Environment info
|
// Environment info
|
||||
env: { |
env: { |
||||
isElectron: true, |
isElectron: true, |
||||
isDev: process.env.NODE_ENV === "development", |
isDev: process.env.NODE_ENV === 'development' |
||||
}, |
}, |
||||
// Path utilities
|
// Path utilities
|
||||
getBasePath: () => { |
getBasePath: () => { |
||||
return process.env.NODE_ENV === "development" ? "/" : "./"; |
return process.env.NODE_ENV === 'development' ? '/' : './' |
||||
}, |
} |
||||
}); |
}) |
||||
|
|
||||
logger.log("Preload script completed successfully"); |
logger.log('Preload script completed successfully') |
||||
} catch (error) { |
} catch (error) { |
||||
logger.error("Error in preload script:", error); |
logger.error('Error in preload script:', error) |
||||
} |
} |
||||
|
|||||
@ -1,58 +1,58 @@ |
|||||
import { AxiosResponse } from "axios"; |
import { AxiosResponse } from 'axios' |
||||
import { GiverReceiverInputInfo } from "../libs/util"; |
import { GiverReceiverInputInfo } from '../libs/util' |
||||
import { ErrorResult, ResultWithType } from "./common"; |
import { ErrorResult, ResultWithType } from './common' |
||||
|
|
||||
export interface GiverOutputInfo { |
export interface GiverOutputInfo { |
||||
action: string; |
action: string |
||||
giver?: GiverReceiverInputInfo; |
giver?: GiverReceiverInputInfo |
||||
description?: string; |
description?: string |
||||
amount?: number; |
amount?: number |
||||
unitCode?: string; |
unitCode?: string |
||||
} |
} |
||||
|
|
||||
export interface ClaimResult { |
export interface ClaimResult { |
||||
success: { claimId: string; handleId: string }; |
success: { claimId: string; handleId: string } |
||||
error: { code: string; message: string }; |
error: { code: string; message: string } |
||||
} |
} |
||||
|
|
||||
export interface VerifiableCredential { |
export interface VerifiableCredential { |
||||
exp?: number; |
exp?: number |
||||
iat: number; |
iat: number |
||||
iss: string; |
iss: string |
||||
vc: { |
vc: { |
||||
"@context": string[]; |
'@context': string[] |
||||
type: string[]; |
type: string[] |
||||
credentialSubject: VerifiableCredentialSubject; |
credentialSubject: VerifiableCredentialSubject |
||||
}; |
} |
||||
} |
} |
||||
|
|
||||
export interface VerifiableCredentialSubject { |
export interface VerifiableCredentialSubject { |
||||
"@context": string; |
'@context': string |
||||
"@type": string; |
'@type': string |
||||
[key: string]: unknown; |
[key: string]: unknown |
||||
} |
} |
||||
|
|
||||
export interface WorldProperties { |
export interface WorldProperties { |
||||
startTime?: string; |
startTime?: string |
||||
endTime?: string; |
endTime?: string |
||||
} |
} |
||||
|
|
||||
export interface ProviderInfo { |
export interface ProviderInfo { |
||||
/** |
/** |
||||
* Could be a DID or a handleId that identifies the provider |
* Could be a DID or a handleId that identifies the provider |
||||
*/ |
*/ |
||||
identifier: string; |
identifier: string |
||||
/** |
/** |
||||
* Indicates if the provider link has been confirmed |
* Indicates if the provider link has been confirmed |
||||
*/ |
*/ |
||||
linkConfirmed: boolean; |
linkConfirmed: boolean |
||||
} |
} |
||||
|
|
||||
// Type for createAndSubmitClaim result
|
// Type for createAndSubmitClaim result
|
||||
export type CreateAndSubmitClaimResult = SuccessResult | ErrorResult; |
export type CreateAndSubmitClaimResult = SuccessResult | ErrorResult |
||||
|
|
||||
// Update SuccessResult to use ClaimResult
|
// Update SuccessResult to use ClaimResult
|
||||
export interface SuccessResult extends ResultWithType { |
export interface SuccessResult extends ResultWithType { |
||||
type: "success"; |
type: 'success' |
||||
response: AxiosResponse<ClaimResult>; |
response: AxiosResponse<ClaimResult> |
||||
} |
} |
||||
|
|||||
@ -1,68 +1,68 @@ |
|||||
import { GenericVerifiableCredential } from "./common"; |
import { GenericVerifiableCredential } from './common' |
||||
|
|
||||
export interface AgreeVerifiableCredential { |
export interface AgreeVerifiableCredential { |
||||
"@context": string; |
'@context': string |
||||
"@type": string; |
'@type': string |
||||
object: Record<string, unknown>; |
object: Record<string, unknown> |
||||
} |
} |
||||
|
|
||||
// Note that previous VCs may have additional fields.
|
// Note that previous VCs may have additional fields.
|
||||
// https://endorser.ch/doc/html/transactions.html#id4
|
// https://endorser.ch/doc/html/transactions.html#id4
|
||||
export interface GiveVerifiableCredential extends GenericVerifiableCredential { |
export interface GiveVerifiableCredential extends GenericVerifiableCredential { |
||||
"@context"?: string; |
'@context'?: string |
||||
"@type": "GiveAction"; |
'@type': 'GiveAction' |
||||
agent?: { identifier: string }; |
agent?: { identifier: string } |
||||
description?: string; |
description?: string |
||||
fulfills?: { "@type": string; identifier?: string; lastClaimId?: string }[]; |
fulfills?: { '@type': string; identifier?: string; lastClaimId?: string }[] |
||||
identifier?: string; |
identifier?: string |
||||
image?: string; |
image?: string |
||||
object?: { amountOfThisGood: number; unitCode: string }; |
object?: { amountOfThisGood: number; unitCode: string } |
||||
provider?: GenericVerifiableCredential; |
provider?: GenericVerifiableCredential |
||||
recipient?: { identifier: string }; |
recipient?: { identifier: string } |
||||
} |
} |
||||
|
|
||||
// Note that previous VCs may have additional fields.
|
// Note that previous VCs may have additional fields.
|
||||
// https://endorser.ch/doc/html/transactions.html#id8
|
// https://endorser.ch/doc/html/transactions.html#id8
|
||||
export interface OfferVerifiableCredential extends GenericVerifiableCredential { |
export interface OfferVerifiableCredential extends GenericVerifiableCredential { |
||||
"@context"?: string; |
'@context'?: string |
||||
"@type": "Offer"; |
'@type': 'Offer' |
||||
description?: string; |
description?: string |
||||
includesObject?: { amountOfThisGood: number; unitCode: string }; |
includesObject?: { amountOfThisGood: number; unitCode: string } |
||||
itemOffered?: { |
itemOffered?: { |
||||
description?: string; |
description?: string |
||||
isPartOf?: { |
isPartOf?: { |
||||
identifier?: string; |
identifier?: string |
||||
lastClaimId?: string; |
lastClaimId?: string |
||||
"@type"?: string; |
'@type'?: string |
||||
name?: string; |
name?: string |
||||
}; |
} |
||||
}; |
} |
||||
offeredBy?: { identifier: string }; |
offeredBy?: { identifier: string } |
||||
recipient?: { identifier: string }; |
recipient?: { identifier: string } |
||||
validThrough?: string; |
validThrough?: string |
||||
} |
} |
||||
|
|
||||
// Note that previous VCs may have additional fields.
|
// Note that previous VCs may have additional fields.
|
||||
// https://endorser.ch/doc/html/transactions.html#id7
|
// https://endorser.ch/doc/html/transactions.html#id7
|
||||
export interface PlanVerifiableCredential extends GenericVerifiableCredential { |
export interface PlanVerifiableCredential extends GenericVerifiableCredential { |
||||
"@context": "https://schema.org"; |
'@context': 'https://schema.org' |
||||
"@type": "PlanAction"; |
'@type': 'PlanAction' |
||||
name: string; |
name: string |
||||
agent?: { identifier: string }; |
agent?: { identifier: string } |
||||
description?: string; |
description?: string |
||||
identifier?: string; |
identifier?: string |
||||
lastClaimId?: string; |
lastClaimId?: string |
||||
location?: { |
location?: { |
||||
geo: { "@type": "GeoCoordinates"; latitude: number; longitude: number }; |
geo: { '@type': 'GeoCoordinates'; latitude: number; longitude: number } |
||||
}; |
} |
||||
} |
} |
||||
|
|
||||
// AKA Registration & RegisterAction
|
// AKA Registration & RegisterAction
|
||||
export interface RegisterVerifiableCredential { |
export interface RegisterVerifiableCredential { |
||||
"@context": string; |
'@context': string |
||||
"@type": "RegisterAction"; |
'@type': 'RegisterAction' |
||||
agent: { identifier: string }; |
agent: { identifier: string } |
||||
identifier?: string; |
identifier?: string |
||||
object: string; |
object: string |
||||
participant?: { identifier: string }; |
participant?: { identifier: string } |
||||
} |
} |
||||
|
|||||
@ -1,36 +1,36 @@ |
|||||
// similar to VerifiableCredentialSubject... maybe rename this
|
// similar to VerifiableCredentialSubject... maybe rename this
|
||||
export interface GenericVerifiableCredential { |
export interface GenericVerifiableCredential { |
||||
"@context"?: string; |
'@context'?: string |
||||
"@type": string; |
'@type': string |
||||
[key: string]: unknown; |
[key: string]: unknown |
||||
} |
} |
||||
|
|
||||
export interface GenericCredWrapper<T extends GenericVerifiableCredential> { |
export interface GenericCredWrapper<T extends GenericVerifiableCredential> { |
||||
claim: T; |
claim: T |
||||
claimType?: string; |
claimType?: string |
||||
handleId: string; |
handleId: string |
||||
id: string; |
id: string |
||||
issuedAt: string; |
issuedAt: string |
||||
issuer: string; |
issuer: string |
||||
publicUrls?: Record<string, string>; |
publicUrls?: Record<string, string> |
||||
} |
} |
||||
|
|
||||
export interface ResultWithType { |
export interface ResultWithType { |
||||
type: string; |
type: string |
||||
} |
} |
||||
|
|
||||
export interface ErrorResponse { |
export interface ErrorResponse { |
||||
error?: { |
error?: { |
||||
message?: string; |
message?: string |
||||
}; |
} |
||||
} |
} |
||||
|
|
||||
export interface InternalError { |
export interface InternalError { |
||||
error: string; |
error: string |
||||
userMessage?: string; |
userMessage?: string |
||||
} |
} |
||||
|
|
||||
export interface ErrorResult extends ResultWithType { |
export interface ErrorResult extends ResultWithType { |
||||
type: "error"; |
type: 'error' |
||||
error: InternalError; |
error: InternalError |
||||
} |
} |
||||
|
|||||
@ -1,7 +1,7 @@ |
|||||
export * from "./claims"; |
export * from './claims' |
||||
export * from "./claims-result"; |
export * from './claims-result' |
||||
export * from "./common"; |
export * from './common' |
||||
export * from "./limits"; |
export * from './limits' |
||||
export * from "./records"; |
export * from './records' |
||||
export * from "./user"; |
export * from './user' |
||||
export * from "./deepLinks"; |
export * from './deepLinks' |
||||
|
|||||
@ -1,14 +1,14 @@ |
|||||
export interface EndorserRateLimits { |
export interface EndorserRateLimits { |
||||
doneClaimsThisWeek: string; |
doneClaimsThisWeek: string |
||||
doneRegistrationsThisMonth: string; |
doneRegistrationsThisMonth: string |
||||
maxClaimsPerWeek: string; |
maxClaimsPerWeek: string |
||||
maxRegistrationsPerMonth: string; |
maxRegistrationsPerMonth: string |
||||
nextMonthBeginDateTime: string; |
nextMonthBeginDateTime: string |
||||
nextWeekBeginDateTime: string; |
nextWeekBeginDateTime: string |
||||
} |
} |
||||
|
|
||||
export interface ImageRateLimits { |
export interface ImageRateLimits { |
||||
doneImagesThisWeek: string; |
doneImagesThisWeek: string |
||||
maxImagesPerWeek: string; |
maxImagesPerWeek: string |
||||
nextWeekBeginDateTime: string; |
nextWeekBeginDateTime: string |
||||
} |
} |
||||
|
|||||
@ -1,8 +1,8 @@ |
|||||
export interface UserInfo { |
export interface UserInfo { |
||||
did: string; |
did: string |
||||
name: string; |
name: string |
||||
publicEncKey: string; |
publicEncKey: string |
||||
registered: boolean; |
registered: boolean |
||||
profileImageUrl?: string; |
profileImageUrl?: string |
||||
nextPublicEncKeyHash?: string; |
nextPublicEncKeyHash?: string |
||||
} |
} |
||||
|
|||||
@ -1,11 +1,11 @@ |
|||||
export function urlBase64ToUint8Array(base64String: string): Uint8Array { |
export function urlBase64ToUint8Array(base64String: string): Uint8Array { |
||||
const padding = "=".repeat((4 - (base64String.length % 4)) % 4); |
const padding = '='.repeat((4 - (base64String.length % 4)) % 4) |
||||
const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/"); |
const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/') |
||||
const rawData = window.atob(base64); |
const rawData = window.atob(base64) |
||||
const outputArray = new Uint8Array(rawData.length); |
const outputArray = new Uint8Array(rawData.length) |
||||
|
|
||||
for (let i = 0; i < rawData.length; ++i) { |
for (let i = 0; i < rawData.length; ++i) { |
||||
outputArray[i] = rawData.charCodeAt(i); |
outputArray[i] = rawData.charCodeAt(i) |
||||
} |
} |
||||
return outputArray; |
return outputArray |
||||
} |
} |
||||
|
|||||
File diff suppressed because it is too large
@ -1,9 +1,9 @@ |
|||||
export interface UserProfile { |
export interface UserProfile { |
||||
description: string; |
description: string |
||||
locLat?: number; |
locLat?: number |
||||
locLon?: number; |
locLon?: number |
||||
locLat2?: number; |
locLat2?: number |
||||
locLon2?: number; |
locLon2?: number |
||||
issuerDid: string; |
issuerDid: string |
||||
rowId?: string; // set on profile retrieved from server
|
rowId?: string // set on profile retrieved from server
|
||||
} |
} |
||||
|
|||||
@ -1,7 +1,7 @@ |
|||||
// see also ../constants/app.ts and
|
// see also ../constants/app.ts and
|
||||
|
|
||||
function didProviderName(netName: string) { |
function didProviderName(netName: string) { |
||||
return "did:ethr" + (netName === "mainnet" ? "" : ":" + netName); |
return 'did:ethr' + (netName === 'mainnet' ? '' : ':' + netName) |
||||
} |
} |
||||
|
|
||||
export const DEFAULT_DID_PROVIDER_NAME = didProviderName("mainnet"); |
export const DEFAULT_DID_PROVIDER_NAME = didProviderName('mainnet') |
||||
|
|||||
@ -1,61 +1,61 @@ |
|||||
import { createPinia } from "pinia"; |
import { createPinia } from 'pinia' |
||||
import { App as VueApp, ComponentPublicInstance, createApp } from "vue"; |
import { App as VueApp, ComponentPublicInstance, createApp } from 'vue' |
||||
import App from "./App.vue"; |
import App from './App.vue' |
||||
import router from "./router"; |
import router from './router' |
||||
import axios from "axios"; |
import axios from 'axios' |
||||
import VueAxios from "vue-axios"; |
import VueAxios from 'vue-axios' |
||||
import Notifications from "notiwind"; |
import Notifications from 'notiwind' |
||||
import "./assets/styles/tailwind.css"; |
import './assets/styles/tailwind.css' |
||||
import { FontAwesomeIcon } from "./lib/fontawesome"; |
import { FontAwesomeIcon } from './lib/fontawesome' |
||||
import Camera from "simple-vue-camera"; |
import Camera from 'simple-vue-camera' |
||||
import { logger } from "./utils/logger"; |
import { logger } from './utils/logger' |
||||
|
|
||||
// Global Error Handler
|
// Global Error Handler
|
||||
function setupGlobalErrorHandler(app: VueApp) { |
function setupGlobalErrorHandler(app: VueApp) { |
||||
logger.log("[App Init] Setting up global error handler"); |
logger.log('[App Init] Setting up global error handler') |
||||
app.config.errorHandler = ( |
app.config.errorHandler = ( |
||||
err: unknown, |
err: unknown, |
||||
instance: ComponentPublicInstance | null, |
instance: ComponentPublicInstance | null, |
||||
info: string, |
info: string |
||||
) => { |
) => { |
||||
logger.error("[App Error] Global Error Handler:", { |
logger.error('[App Error] Global Error Handler:', { |
||||
error: err, |
error: err, |
||||
info, |
info, |
||||
component: instance?.$options.name || "unknown", |
component: instance?.$options.name || 'unknown' |
||||
}); |
}) |
||||
alert( |
alert( |
||||
(err instanceof Error ? err.message : "Something bad happened") + |
(err instanceof Error ? err.message : 'Something bad happened') + |
||||
" - Try reloading or restarting the app.", |
' - Try reloading or restarting the app.' |
||||
); |
) |
||||
}; |
} |
||||
} |
} |
||||
|
|
||||
// Function to initialize the app
|
// Function to initialize the app
|
||||
export function initializeApp() { |
export function initializeApp() { |
||||
logger.log("[App Init] Starting app initialization"); |
logger.log('[App Init] Starting app initialization') |
||||
logger.log("[App Init] Platform:", process.env.VITE_PLATFORM); |
logger.log('[App Init] Platform:', process.env.VITE_PLATFORM) |
||||
|
|
||||
const app = createApp(App); |
const app = createApp(App) |
||||
logger.log("[App Init] Vue app created"); |
logger.log('[App Init] Vue app created') |
||||
|
|
||||
app.component("FontAwesome", FontAwesomeIcon).component("camera", Camera); |
app.component('FontAwesome', FontAwesomeIcon).component('camera', Camera) |
||||
logger.log("[App Init] Components registered"); |
logger.log('[App Init] Components registered') |
||||
|
|
||||
const pinia = createPinia(); |
const pinia = createPinia() |
||||
app.use(pinia); |
app.use(pinia) |
||||
logger.log("[App Init] Pinia store initialized"); |
logger.log('[App Init] Pinia store initialized') |
||||
|
|
||||
app.use(VueAxios, axios); |
app.use(VueAxios, axios) |
||||
logger.log("[App Init] Axios initialized"); |
logger.log('[App Init] Axios initialized') |
||||
|
|
||||
app.use(router); |
app.use(router) |
||||
logger.log("[App Init] Router initialized"); |
logger.log('[App Init] Router initialized') |
||||
|
|
||||
app.use(Notifications); |
app.use(Notifications) |
||||
logger.log("[App Init] Notifications initialized"); |
logger.log('[App Init] Notifications initialized') |
||||
|
|
||||
setupGlobalErrorHandler(app); |
setupGlobalErrorHandler(app) |
||||
logger.log("[App Init] App initialization complete"); |
logger.log('[App Init] App initialization complete') |
||||
|
|
||||
return app; |
return app |
||||
} |
} |
||||
|
|||||
@ -1,4 +1,4 @@ |
|||||
import { initializeApp } from "./main.common"; |
import { initializeApp } from './main.common' |
||||
|
|
||||
const app = initializeApp(); |
const app = initializeApp() |
||||
app.mount("#app"); |
app.mount('#app') |
||||
|
|||||
@ -1,4 +1,4 @@ |
|||||
import { initializeApp } from "./main.common"; |
import { initializeApp } from './main.common' |
||||
|
|
||||
const app = initializeApp(); |
const app = initializeApp() |
||||
app.mount("#app"); |
app.mount('#app') |
||||
|
|||||
@ -1,5 +1,5 @@ |
|||||
import { initializeApp } from "./main.common"; |
import { initializeApp } from './main.common' |
||||
import "./registerServiceWorker"; // Web PWA support
|
import './registerServiceWorker' // Web PWA support
|
||||
|
|
||||
const app = initializeApp(); |
const app = initializeApp() |
||||
app.mount("#app"); |
app.mount('#app') |
||||
|
|||||
@ -1,39 +1,39 @@ |
|||||
/* eslint-disable no-console */ |
/* eslint-disable no-console */ |
||||
|
|
||||
import { register } from "register-service-worker"; |
import { register } from 'register-service-worker' |
||||
|
|
||||
// Only register service worker if explicitly enabled and in production
|
// Only register service worker if explicitly enabled and in production
|
||||
if ( |
if ( |
||||
process.env.VITE_PWA_ENABLED === "true" && |
process.env.VITE_PWA_ENABLED === 'true' && |
||||
process.env.NODE_ENV === "production" |
process.env.NODE_ENV === 'production' |
||||
) { |
) { |
||||
register(`${process.env.BASE_URL}sw.js`, { |
register(`${process.env.BASE_URL}sw.js`, { |
||||
ready() { |
ready() { |
||||
console.log("Service worker is active."); |
console.log('Service worker is active.') |
||||
}, |
}, |
||||
registered() { |
registered() { |
||||
console.log("Service worker has been registered."); |
console.log('Service worker has been registered.') |
||||
}, |
}, |
||||
cached() { |
cached() { |
||||
console.log("Content has been cached for offline use."); |
console.log('Content has been cached for offline use.') |
||||
}, |
}, |
||||
updatefound() { |
updatefound() { |
||||
console.log("New content is downloading."); |
console.log('New content is downloading.') |
||||
}, |
}, |
||||
updated() { |
updated() { |
||||
console.log("New content is available; please refresh."); |
console.log('New content is available; please refresh.') |
||||
}, |
}, |
||||
offline() { |
offline() { |
||||
console.log( |
console.log( |
||||
"No internet connection found. App is running in offline mode.", |
'No internet connection found. App is running in offline mode.' |
||||
); |
) |
||||
}, |
}, |
||||
error(error) { |
error(error) { |
||||
console.error("Error during service worker registration:", error); |
console.error('Error during service worker registration:', error) |
||||
}, |
} |
||||
}); |
}) |
||||
} else { |
} else { |
||||
console.log( |
console.log( |
||||
"Service worker registration skipped - not enabled or not in production", |
'Service worker registration skipped - not enabled or not in production' |
||||
); |
) |
||||
} |
} |
||||
|
|||||
@ -1,88 +1,88 @@ |
|||||
import { |
import { |
||||
BarcodeScanner, |
BarcodeScanner, |
||||
BarcodeFormat, |
BarcodeFormat, |
||||
LensFacing, |
LensFacing |
||||
} from "@capacitor-mlkit/barcode-scanning"; |
} from '@capacitor-mlkit/barcode-scanning' |
||||
import type { PluginListenerHandle } from "@capacitor/core"; |
import type { PluginListenerHandle } from '@capacitor/core' |
||||
import { QRScannerService, ScanListener } from "./types"; |
import { QRScannerService, ScanListener } from './types' |
||||
|
|
||||
export class NativeQRScanner implements QRScannerService { |
export class NativeQRScanner implements QRScannerService { |
||||
private scanListener: ScanListener | null = null; |
private scanListener: ScanListener | null = null |
||||
private isScanning = false; |
private isScanning = false |
||||
private listenerHandle: PluginListenerHandle | null = null; |
private listenerHandle: PluginListenerHandle | null = null |
||||
|
|
||||
async checkPermissions(): Promise<boolean> { |
async checkPermissions(): Promise<boolean> { |
||||
const { camera } = await BarcodeScanner.checkPermissions(); |
const { camera } = await BarcodeScanner.checkPermissions() |
||||
return camera === "granted"; |
return camera === 'granted' |
||||
} |
} |
||||
|
|
||||
async requestPermissions(): Promise<boolean> { |
async requestPermissions(): Promise<boolean> { |
||||
const { camera } = await BarcodeScanner.requestPermissions(); |
const { camera } = await BarcodeScanner.requestPermissions() |
||||
return camera === "granted"; |
return camera === 'granted' |
||||
} |
} |
||||
|
|
||||
async isSupported(): Promise<boolean> { |
async isSupported(): Promise<boolean> { |
||||
const { supported } = await BarcodeScanner.isSupported(); |
const { supported } = await BarcodeScanner.isSupported() |
||||
return supported; |
return supported |
||||
} |
} |
||||
|
|
||||
async startScan(): Promise<void> { |
async startScan(): Promise<void> { |
||||
if (this.isScanning) { |
if (this.isScanning) { |
||||
throw new Error("Scanner is already running"); |
throw new Error('Scanner is already running') |
||||
} |
} |
||||
|
|
||||
try { |
try { |
||||
this.isScanning = true; |
this.isScanning = true |
||||
await BarcodeScanner.startScan({ |
await BarcodeScanner.startScan({ |
||||
formats: [BarcodeFormat.QrCode], |
formats: [BarcodeFormat.QrCode], |
||||
lensFacing: LensFacing.Back, |
lensFacing: LensFacing.Back |
||||
}); |
}) |
||||
|
|
||||
this.listenerHandle = await BarcodeScanner.addListener( |
this.listenerHandle = await BarcodeScanner.addListener( |
||||
"barcodesScanned", |
'barcodesScanned', |
||||
async (result) => { |
async (result) => { |
||||
if (result.barcodes.length > 0 && this.scanListener) { |
if (result.barcodes.length > 0 && this.scanListener) { |
||||
const barcode = result.barcodes[0]; |
const barcode = result.barcodes[0] |
||||
this.scanListener.onScan(barcode.rawValue); |
this.scanListener.onScan(barcode.rawValue) |
||||
await this.stopScan(); |
await this.stopScan() |
||||
} |
} |
||||
}, |
} |
||||
); |
) |
||||
} catch (error) { |
} catch (error) { |
||||
this.isScanning = false; |
this.isScanning = false |
||||
if (this.scanListener?.onError) { |
if (this.scanListener?.onError) { |
||||
this.scanListener.onError(new Error(String(error))); |
this.scanListener.onError(new Error(String(error))) |
||||
} |
} |
||||
throw error; |
throw error |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
async stopScan(): Promise<void> { |
async stopScan(): Promise<void> { |
||||
if (!this.isScanning) { |
if (!this.isScanning) { |
||||
return; |
return |
||||
} |
} |
||||
|
|
||||
try { |
try { |
||||
await BarcodeScanner.stopScan(); |
await BarcodeScanner.stopScan() |
||||
this.isScanning = false; |
this.isScanning = false |
||||
} catch (error) { |
} catch (error) { |
||||
if (this.scanListener?.onError) { |
if (this.scanListener?.onError) { |
||||
this.scanListener.onError(new Error(String(error))); |
this.scanListener.onError(new Error(String(error))) |
||||
} |
} |
||||
throw error; |
throw error |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
addListener(listener: ScanListener): void { |
addListener(listener: ScanListener): void { |
||||
this.scanListener = listener; |
this.scanListener = listener |
||||
} |
} |
||||
|
|
||||
async cleanup(): Promise<void> { |
async cleanup(): Promise<void> { |
||||
await this.stopScan(); |
await this.stopScan() |
||||
if (this.listenerHandle) { |
if (this.listenerHandle) { |
||||
await this.listenerHandle.remove(); |
await this.listenerHandle.remove() |
||||
this.listenerHandle = null; |
this.listenerHandle = null |
||||
} |
} |
||||
this.scanListener = null; |
this.scanListener = null |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,29 +1,38 @@ |
|||||
import { Capacitor } from "@capacitor/core"; |
import { Capacitor } from '@capacitor/core' |
||||
import { CapacitorQRScanner } from "./CapacitorQRScanner"; |
import { CapacitorQRScanner } from './CapacitorQRScanner' |
||||
import { WebQRScanner } from "./WebQRScanner"; |
import type { QRScannerService } from './types' |
||||
import type { QRScannerService } from "./types"; |
import { logger } from '../../utils/logger' |
||||
import { logger } from "../../utils/logger"; |
import { WebDialogQRScanner } from './WebDialogQRScanner' |
||||
|
|
||||
|
// Import platform-specific flags from Vite config
|
||||
|
declare const __USE_QR_READER__: boolean |
||||
|
declare const __IS_MOBILE__: boolean |
||||
|
|
||||
export class QRScannerFactory { |
export class QRScannerFactory { |
||||
private static instance: QRScannerService | null = null; |
private static instance: QRScannerService | null = null |
||||
|
|
||||
static getInstance(): QRScannerService { |
static getInstance(): QRScannerService { |
||||
if (!this.instance) { |
if (!this.instance) { |
||||
if (Capacitor.isNativePlatform()) { |
// Use platform-specific flags for more accurate detection
|
||||
logger.log("Creating native QR scanner instance"); |
if (__IS_MOBILE__ || Capacitor.isNativePlatform()) { |
||||
this.instance = new CapacitorQRScanner(); |
logger.log('Creating native QR scanner instance') |
||||
|
this.instance = new CapacitorQRScanner() |
||||
|
} else if (__USE_QR_READER__) { |
||||
|
logger.log('Creating web QR scanner instance') |
||||
|
this.instance = new WebDialogQRScanner() |
||||
} else { |
} else { |
||||
logger.log("Creating web QR scanner instance"); |
throw new Error( |
||||
this.instance = new WebQRScanner(); |
'No QR scanner implementation available for this platform' |
||||
|
) |
||||
} |
} |
||||
} |
} |
||||
return this.instance; |
return this.instance |
||||
} |
} |
||||
|
|
||||
static async cleanup() { |
static async cleanup() { |
||||
if (this.instance) { |
if (this.instance) { |
||||
await this.instance.cleanup(); |
await this.instance.cleanup() |
||||
this.instance = null; |
this.instance = null |
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -0,0 +1,90 @@ |
|||||
|
import type { QRScannerService, ScanListener } from './types' |
||||
|
import QRScannerDialog from '../../components/QRScannerDialog.vue' |
||||
|
import { createApp, type App } from 'vue' |
||||
|
import { logger } from '../../utils/logger' |
||||
|
|
||||
|
// Import platform-specific flags from Vite config
|
||||
|
declare const __USE_QR_READER__: boolean |
||||
|
|
||||
|
export class WebDialogQRScanner implements QRScannerService { |
||||
|
private dialogInstance: App | null = null |
||||
|
private dialogComponent: InstanceType<typeof QRScannerDialog> | null = null |
||||
|
private scanListener: ScanListener | null = null |
||||
|
|
||||
|
async checkPermissions(): Promise<boolean> { |
||||
|
try { |
||||
|
const permissions = await navigator.permissions.query({ |
||||
|
name: 'camera' as PermissionName |
||||
|
}) |
||||
|
return permissions.state === 'granted' |
||||
|
} catch (error) { |
||||
|
logger.error('Error checking camera permissions:', error) |
||||
|
return false |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async requestPermissions(): Promise<boolean> { |
||||
|
try { |
||||
|
const stream = await navigator.mediaDevices.getUserMedia({ |
||||
|
video: { facingMode: 'environment' } |
||||
|
}) |
||||
|
stream.getTracks().forEach((track) => track.stop()) |
||||
|
return true |
||||
|
} catch (error) { |
||||
|
logger.error('Error requesting camera permissions:', error) |
||||
|
return false |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async isSupported(): Promise<boolean> { |
||||
|
if (!__USE_QR_READER__) { |
||||
|
return false |
||||
|
} |
||||
|
return !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia) |
||||
|
} |
||||
|
|
||||
|
async startScan(): Promise<void> { |
||||
|
if (!__USE_QR_READER__) { |
||||
|
throw new Error('Web QR scanner is not supported on this platform') |
||||
|
} |
||||
|
|
||||
|
if (!this.dialogInstance) { |
||||
|
const div = document.createElement('div') |
||||
|
document.body.appendChild(div) |
||||
|
|
||||
|
this.dialogInstance = createApp(QRScannerDialog) |
||||
|
this.dialogComponent = this.dialogInstance.mount(div) as InstanceType< |
||||
|
typeof QRScannerDialog |
||||
|
> |
||||
|
} |
||||
|
|
||||
|
if (this.dialogComponent && this.scanListener) { |
||||
|
this.dialogComponent.open((result: string) => { |
||||
|
if (this.scanListener) { |
||||
|
this.scanListener.onScan(result) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async stopScan(): Promise<void> { |
||||
|
if (this.dialogComponent) { |
||||
|
this.dialogComponent.close() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
addListener(listener: ScanListener): void { |
||||
|
this.scanListener = listener |
||||
|
} |
||||
|
|
||||
|
async cleanup(): Promise<void> { |
||||
|
if (this.dialogComponent) { |
||||
|
this.dialogComponent.close() |
||||
|
} |
||||
|
if (this.dialogInstance) { |
||||
|
this.dialogInstance.unmount() |
||||
|
this.dialogInstance = null |
||||
|
this.dialogComponent = null |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,142 +0,0 @@ |
|||||
import jsQR from "jsqr"; |
|
||||
import { QRScannerService, ScanListener } from "./types"; |
|
||||
import { logger } from "../../utils/logger"; |
|
||||
|
|
||||
export class WebQRScanner implements QRScannerService { |
|
||||
private video: HTMLVideoElement | null = null; |
|
||||
private canvas: HTMLCanvasElement | null = null; |
|
||||
private context: CanvasRenderingContext2D | null = null; |
|
||||
private animationFrameId: number | null = null; |
|
||||
private scanListener: ScanListener | null = null; |
|
||||
private isScanning = false; |
|
||||
private mediaStream: MediaStream | null = null; |
|
||||
|
|
||||
constructor() { |
|
||||
this.video = document.createElement("video"); |
|
||||
this.canvas = document.createElement("canvas"); |
|
||||
this.context = this.canvas.getContext("2d"); |
|
||||
} |
|
||||
|
|
||||
async checkPermissions(): Promise<boolean> { |
|
||||
try { |
|
||||
const permissions = await navigator.permissions.query({ |
|
||||
name: "camera" as PermissionName, |
|
||||
}); |
|
||||
return permissions.state === "granted"; |
|
||||
} catch (error) { |
|
||||
logger.error("Error checking camera permissions:", error); |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
async requestPermissions(): Promise<boolean> { |
|
||||
try { |
|
||||
const stream = await navigator.mediaDevices.getUserMedia({ |
|
||||
video: { facingMode: "environment" }, |
|
||||
}); |
|
||||
stream.getTracks().forEach((track) => track.stop()); |
|
||||
return true; |
|
||||
} catch (error) { |
|
||||
logger.error("Error requesting camera permissions:", error); |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
async isSupported(): Promise<boolean> { |
|
||||
return !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia); |
|
||||
} |
|
||||
|
|
||||
async startScan(): Promise<void> { |
|
||||
if (this.isScanning || !this.video || !this.canvas || !this.context) { |
|
||||
throw new Error("Scanner is already running or not properly initialized"); |
|
||||
} |
|
||||
|
|
||||
try { |
|
||||
this.mediaStream = await navigator.mediaDevices.getUserMedia({ |
|
||||
video: { facingMode: "environment" }, |
|
||||
}); |
|
||||
|
|
||||
this.video.srcObject = this.mediaStream; |
|
||||
this.video.setAttribute("playsinline", "true"); |
|
||||
await this.video.play(); |
|
||||
|
|
||||
this.canvas.width = this.video.videoWidth; |
|
||||
this.canvas.height = this.video.videoHeight; |
|
||||
|
|
||||
this.isScanning = true; |
|
||||
this.scanFrame(); |
|
||||
} catch (error) { |
|
||||
logger.error("Error starting scan:", error); |
|
||||
throw error; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private scanFrame = () => { |
|
||||
if ( |
|
||||
!this.isScanning || |
|
||||
!this.video || |
|
||||
!this.canvas || |
|
||||
!this.context || |
|
||||
!this.scanListener |
|
||||
) { |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (this.video.readyState === this.video.HAVE_ENOUGH_DATA) { |
|
||||
this.canvas.width = this.video.videoWidth; |
|
||||
this.canvas.height = this.video.videoHeight; |
|
||||
this.context.drawImage( |
|
||||
this.video, |
|
||||
0, |
|
||||
0, |
|
||||
this.canvas.width, |
|
||||
this.canvas.height, |
|
||||
); |
|
||||
|
|
||||
const imageData = this.context.getImageData( |
|
||||
0, |
|
||||
0, |
|
||||
this.canvas.width, |
|
||||
this.canvas.height, |
|
||||
); |
|
||||
const code = jsQR(imageData.data, imageData.width, imageData.height, { |
|
||||
inversionAttempts: "dontInvert", |
|
||||
}); |
|
||||
|
|
||||
if (code) { |
|
||||
this.scanListener.onScan(code.data); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
this.animationFrameId = requestAnimationFrame(this.scanFrame); |
|
||||
}; |
|
||||
|
|
||||
async stopScan(): Promise<void> { |
|
||||
this.isScanning = false; |
|
||||
if (this.animationFrameId) { |
|
||||
cancelAnimationFrame(this.animationFrameId); |
|
||||
this.animationFrameId = null; |
|
||||
} |
|
||||
|
|
||||
if (this.mediaStream) { |
|
||||
this.mediaStream.getTracks().forEach((track) => track.stop()); |
|
||||
this.mediaStream = null; |
|
||||
} |
|
||||
|
|
||||
if (this.video) { |
|
||||
this.video.srcObject = null; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
addListener(listener: ScanListener): void { |
|
||||
this.scanListener = listener; |
|
||||
} |
|
||||
|
|
||||
async cleanup(): Promise<void> { |
|
||||
await this.stopScan(); |
|
||||
this.scanListener = null; |
|
||||
this.video = null; |
|
||||
this.canvas = null; |
|
||||
this.context = null; |
|
||||
} |
|
||||
} |
|
||||
@ -1,29 +1,29 @@ |
|||||
export interface ScanResult { |
export interface ScanResult { |
||||
rawValue: string; |
rawValue: string |
||||
} |
} |
||||
|
|
||||
export interface QRScannerState { |
export interface QRScannerState { |
||||
isSupported: boolean; |
isSupported: boolean |
||||
granted: boolean; |
granted: boolean |
||||
denied: boolean; |
denied: boolean |
||||
isProcessing: boolean; |
isProcessing: boolean |
||||
processingStatus: string; |
processingStatus: string |
||||
processingDetails: string; |
processingDetails: string |
||||
error: string; |
error: string |
||||
status: string; |
status: string |
||||
} |
} |
||||
|
|
||||
export interface ScanListener { |
export interface ScanListener { |
||||
onScan: (result: string) => void; |
onScan: (result: string) => void |
||||
onError?: (error: Error) => void; |
onError?: (error: Error) => void |
||||
} |
} |
||||
|
|
||||
export interface QRScannerService { |
export interface QRScannerService { |
||||
checkPermissions(): Promise<boolean>; |
checkPermissions(): Promise<boolean> |
||||
requestPermissions(): Promise<boolean>; |
requestPermissions(): Promise<boolean> |
||||
isSupported(): Promise<boolean>; |
isSupported(): Promise<boolean> |
||||
startScan(): Promise<void>; |
startScan(): Promise<void> |
||||
stopScan(): Promise<void>; |
stopScan(): Promise<void> |
||||
addListener(listener: ScanListener): void; |
addListener(listener: ScanListener): void |
||||
cleanup(): Promise<void>; |
cleanup(): Promise<void> |
||||
} |
} |
||||
|
|||||
@ -1,62 +1,62 @@ |
|||||
import axios from "axios"; |
import axios from 'axios' |
||||
import * as didJwt from "did-jwt"; |
import * as didJwt from 'did-jwt' |
||||
import { AppString } from "../constants/app"; |
import { AppString } from '../constants/app' |
||||
import { retrieveSettingsForActiveAccount } from "../db"; |
import { retrieveSettingsForActiveAccount } from '../db' |
||||
import { SERVICE_ID } from "../libs/endorserServer"; |
import { SERVICE_ID } from '../libs/endorserServer' |
||||
import { deriveAddress, newIdentifier } from "../libs/crypto"; |
import { deriveAddress, newIdentifier } from '../libs/crypto' |
||||
import { logger } from "../utils/logger"; |
import { logger } from '../utils/logger' |
||||
/** |
/** |
||||
* Get User #0 to sign & submit a RegisterAction for the user's activeDid. |
* Get User #0 to sign & submit a RegisterAction for the user's activeDid. |
||||
*/ |
*/ |
||||
export async function testServerRegisterUser() { |
export async function testServerRegisterUser() { |
||||
const testUser0Mnem = |
const testUser0Mnem = |
||||
"seminar accuse mystery assist delay law thing deal image undo guard initial shallow wrestle list fragile borrow velvet tomorrow awake explain test offer control"; |
'seminar accuse mystery assist delay law thing deal image undo guard initial shallow wrestle list fragile borrow velvet tomorrow awake explain test offer control' |
||||
|
|
||||
const [addr, privateHex, publicHex, deriPath] = deriveAddress(testUser0Mnem); |
const [addr, privateHex, publicHex, deriPath] = deriveAddress(testUser0Mnem) |
||||
|
|
||||
const identity0 = newIdentifier(addr, publicHex, privateHex, deriPath); |
const identity0 = newIdentifier(addr, publicHex, privateHex, deriPath) |
||||
|
|
||||
const settings = await retrieveSettingsForActiveAccount(); |
const settings = await retrieveSettingsForActiveAccount() |
||||
|
|
||||
// Make a claim
|
// Make a claim
|
||||
const vcClaim = { |
const vcClaim = { |
||||
"@context": "https://schema.org", |
'@context': 'https://schema.org', |
||||
"@type": "RegisterAction", |
'@type': 'RegisterAction', |
||||
agent: { did: identity0.did }, |
agent: { did: identity0.did }, |
||||
object: SERVICE_ID, |
object: SERVICE_ID, |
||||
participant: { did: settings.activeDid }, |
participant: { did: settings.activeDid } |
||||
}; |
} |
||||
// Make a payload for the claim
|
// Make a payload for the claim
|
||||
const vcPayload = { |
const vcPayload = { |
||||
sub: "RegisterAction", |
sub: 'RegisterAction', |
||||
vc: { |
vc: { |
||||
"@context": ["https://www.w3.org/2018/credentials/v1"], |
'@context': ['https://www.w3.org/2018/credentials/v1'], |
||||
type: ["VerifiableCredential"], |
type: ['VerifiableCredential'], |
||||
credentialSubject: vcClaim, |
credentialSubject: vcClaim |
||||
}, |
} |
||||
}; |
} |
||||
// create a signature using private key of identity
|
// create a signature using private key of identity
|
||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||
const privateKeyHex: string = identity0.keys[0].privateKeyHex!; |
const privateKeyHex: string = identity0.keys[0].privateKeyHex!; |
||||
const signer = await didJwt.SimpleSigner(privateKeyHex); |
const signer = await didJwt.SimpleSigner(privateKeyHex) |
||||
const alg = undefined; |
const alg = undefined |
||||
// create a JWT for the request
|
// create a JWT for the request
|
||||
const vcJwt: string = await didJwt.createJWT(vcPayload, { |
const vcJwt: string = await didJwt.createJWT(vcPayload, { |
||||
alg: alg, |
alg: alg, |
||||
issuer: identity0.did, |
issuer: identity0.did, |
||||
signer: signer, |
signer: signer |
||||
}); |
}) |
||||
|
|
||||
// Make the xhr request payload
|
// Make the xhr request payload
|
||||
|
|
||||
const payload = JSON.stringify({ jwtEncoded: vcJwt }); |
const payload = JSON.stringify({ jwtEncoded: vcJwt }) |
||||
const endorserApiServer = |
const endorserApiServer = |
||||
settings.apiServer || AppString.TEST_ENDORSER_API_SERVER; |
settings.apiServer || AppString.TEST_ENDORSER_API_SERVER |
||||
const url = endorserApiServer + "/api/claim"; |
const url = endorserApiServer + '/api/claim' |
||||
const headers = { |
const headers = { |
||||
"Content-Type": "application/json", |
'Content-Type': 'application/json' |
||||
}; |
} |
||||
|
|
||||
const resp = await axios.post(url, payload, { headers }); |
const resp = await axios.post(url, payload, { headers }) |
||||
logger.log("User registration result:", resp); |
logger.log('User registration result:', resp) |
||||
} |
} |
||||
|
|||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue