fix more types and make more code consistent (mobile test seems to hang)
This commit is contained in:
+1
-2
@@ -9,7 +9,6 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src",
|
"lint": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src",
|
||||||
"lint-fix": "eslint --ext .js,.ts,.vue --ignore-path .gitignore --fix src",
|
"lint-fix": "eslint --ext .js,.ts,.vue --ignore-path .gitignore --fix src",
|
||||||
"type-safety-check": "./scripts/type-safety-check.sh",
|
|
||||||
"type-check": "tsc --noEmit",
|
"type-check": "tsc --noEmit",
|
||||||
"type-check:vue": "vue-tsc --noEmit",
|
"type-check:vue": "vue-tsc --noEmit",
|
||||||
"prebuild": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src && node sw_combine.js && node scripts/copy-wasm.js",
|
"prebuild": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src && node sw_combine.js && node scripts/copy-wasm.js",
|
||||||
@@ -17,7 +16,7 @@
|
|||||||
"test:unit": "jest",
|
"test:unit": "jest",
|
||||||
"check:dependencies": "./scripts/check-dependencies.sh",
|
"check:dependencies": "./scripts/check-dependencies.sh",
|
||||||
"deps:update-daily-notification-plugin": "npm install @timesafari/daily-notification-plugin@git+https://gitea.anomalistdesign.com/trent_larson/daily-notification-plugin.git#master",
|
"deps:update-daily-notification-plugin": "npm install @timesafari/daily-notification-plugin@git+https://gitea.anomalistdesign.com/trent_larson/daily-notification-plugin.git#master",
|
||||||
"test:all": "npm run lint && tsc && npm run test:unit && npm run test:web && npm run test:mobile && ./scripts/test-safety-check.sh && echo '\n\n\nGotta add the performance tests'",
|
"test:all": "npm run lint && npm run type-check && npm run type-check:vue && npm run test:unit && npm run test:web && npm run test:mobile && echo '\n\n\nGotta add the performance tests'",
|
||||||
"test:web": "npx playwright test -c playwright.config-local.ts --trace on",
|
"test:web": "npx playwright test -c playwright.config-local.ts --trace on",
|
||||||
"test:mobile": "./scripts/test-mobile.sh",
|
"test:mobile": "./scripts/test-mobile.sh",
|
||||||
"test:android": "node scripts/test-android.js",
|
"test:android": "node scripts/test-android.js",
|
||||||
|
|||||||
@@ -1,103 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Type Safety Pre-commit Check Script
|
|
||||||
# This script ensures type safety before commits by running linting and type checking
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
echo "🔍 Running Type Safety Pre-commit Checks..."
|
|
||||||
|
|
||||||
# Colors for output
|
|
||||||
RED='\033[0;31m'
|
|
||||||
GREEN='\033[0;32m'
|
|
||||||
YELLOW='\033[1;33m'
|
|
||||||
NC='\033[0m' # No Color
|
|
||||||
|
|
||||||
# Function to print colored output
|
|
||||||
print_status() {
|
|
||||||
echo -e "${GREEN}✅ $1${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
print_warning() {
|
|
||||||
echo -e "${YELLOW}⚠️ $1${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
print_error() {
|
|
||||||
echo -e "${RED}❌ $1${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check if we're in the right directory
|
|
||||||
if [ ! -f "package.json" ]; then
|
|
||||||
print_error "Must run from project root directory"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Step 1: Run ESLint with TypeScript rules
|
|
||||||
print_status "Running ESLint TypeScript checks..."
|
|
||||||
if npm run lint > /dev/null 2>&1; then
|
|
||||||
print_status "ESLint passed - no type safety issues found"
|
|
||||||
else
|
|
||||||
print_error "ESLint failed - type safety issues detected"
|
|
||||||
echo ""
|
|
||||||
echo "Running lint with details..."
|
|
||||||
npm run lint
|
|
||||||
echo ""
|
|
||||||
print_error "Please fix the above type safety issues before committing"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Step 2: Run TypeScript type checking
|
|
||||||
print_status "Running TypeScript type checking..."
|
|
||||||
if npm run type-check > /dev/null 2>&1; then
|
|
||||||
print_status "TypeScript compilation passed"
|
|
||||||
else
|
|
||||||
print_error "TypeScript compilation failed"
|
|
||||||
echo ""
|
|
||||||
echo "Running type check with details..."
|
|
||||||
npm run type-check
|
|
||||||
echo ""
|
|
||||||
print_error "Please fix the above TypeScript errors before committing"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Step 3: Check for any remaining 'any' types
|
|
||||||
print_status "Scanning for any remaining 'any' types..."
|
|
||||||
ANY_COUNT=$(grep -r "any" src/ --include="*.ts" --include="*.vue" | grep -v "// eslint-disable" | grep -v "eslint-disable-next-line" | wc -l)
|
|
||||||
|
|
||||||
if [ "$ANY_COUNT" -eq 0 ]; then
|
|
||||||
print_status "No 'any' types found in source code"
|
|
||||||
else
|
|
||||||
print_warning "Found $ANY_COUNT instances of 'any' type usage"
|
|
||||||
echo ""
|
|
||||||
echo "Instances found:"
|
|
||||||
grep -r "any" src/ --include="*.ts" --include="*.vue" | grep -v "// eslint-disable" | grep -v "eslint-disable-next-line" || true
|
|
||||||
echo ""
|
|
||||||
print_error "Please replace 'any' types with proper TypeScript types before committing"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Step 4: Verify database migration status
|
|
||||||
print_status "Checking database migration status..."
|
|
||||||
if grep -r "databaseUtil" src/ --include="*.ts" --include="*.vue" > /dev/null 2>&1; then
|
|
||||||
print_warning "Found databaseUtil imports - ensure migration is complete"
|
|
||||||
echo ""
|
|
||||||
echo "Files with databaseUtil imports:"
|
|
||||||
grep -r "databaseUtil" src/ --include="*.ts" --include="*.vue" | head -5 || true
|
|
||||||
echo ""
|
|
||||||
print_warning "Consider completing database migration to PlatformServiceMixin"
|
|
||||||
else
|
|
||||||
print_status "No databaseUtil imports found - migration appears complete"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# All checks passed
|
|
||||||
echo ""
|
|
||||||
print_status "All type safety checks passed! 🎉"
|
|
||||||
print_status "Your code is ready for commit"
|
|
||||||
echo ""
|
|
||||||
echo "📚 Remember to follow the Type Safety Guidelines:"
|
|
||||||
echo " - doc/typescript-type-safety-guidelines.md"
|
|
||||||
echo " - Use proper error handling patterns"
|
|
||||||
echo " - Leverage existing type definitions"
|
|
||||||
echo " - Run 'npm run lint-fix' for automatic fixes"
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
@@ -130,6 +130,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Vue, Component, Prop } from "vue-facing-decorator";
|
import { Vue, Component, Prop } from "vue-facing-decorator";
|
||||||
|
import { NotificationIface } from "@/constants/app";
|
||||||
|
|
||||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||||
import { SOMEONE_UNNAMED } from "@/constants/entities";
|
import { SOMEONE_UNNAMED } from "@/constants/entities";
|
||||||
@@ -149,10 +150,7 @@ export default class BulkMembersDialog extends Vue {
|
|||||||
@Prop({ required: true }) isOrganizer!: boolean;
|
@Prop({ required: true }) isOrganizer!: boolean;
|
||||||
|
|
||||||
// Vue notification system
|
// Vue notification system
|
||||||
$notify!: (
|
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
||||||
notification: { group: string; type: string; title: string; text: string },
|
|
||||||
timeout?: number,
|
|
||||||
) => void;
|
|
||||||
|
|
||||||
// Notification system
|
// Notification system
|
||||||
notify!: ReturnType<typeof createNotifyHelpers>;
|
notify!: ReturnType<typeof createNotifyHelpers>;
|
||||||
@@ -381,9 +379,6 @@ export default class BulkMembersDialog extends Vue {
|
|||||||
contact,
|
contact,
|
||||||
);
|
);
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
if (result.embeddedRecordError) {
|
|
||||||
throw new Error(result.embeddedRecordError);
|
|
||||||
}
|
|
||||||
await this.$updateContact(member.did, { registered: true });
|
await this.$updateContact(member.did, { registered: true });
|
||||||
} else {
|
} else {
|
||||||
throw result;
|
throw result;
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ import { Router } from "vue-router";
|
|||||||
import * as R from "ramda";
|
import * as R from "ramda";
|
||||||
|
|
||||||
import { NotificationIface } from "../constants/app";
|
import { NotificationIface } from "../constants/app";
|
||||||
|
import { Contact } from "../db/tables/contacts";
|
||||||
|
|
||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ export default class EntitySummaryButton extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If the entity does not have a set name, but is not the special "Unnamed", use their DID
|
// If the entity does not have a set name, but is not the special "Unnamed", use their DID
|
||||||
return this.entity?.did;
|
return this.entity?.did ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -43,9 +43,6 @@ export default class InfiniteScroll extends Vue {
|
|||||||
/** Intersection Observer instance for detecting scroll position */
|
/** Intersection Observer instance for detecting scroll position */
|
||||||
private observer!: IntersectionObserver;
|
private observer!: IntersectionObserver;
|
||||||
|
|
||||||
/** Flag to track initial render state */
|
|
||||||
private isInitialRender = true;
|
|
||||||
|
|
||||||
/** Flag to prevent multiple simultaneous loading states */
|
/** Flag to prevent multiple simultaneous loading states */
|
||||||
private isLoading = false;
|
private isLoading = false;
|
||||||
|
|
||||||
|
|||||||
@@ -251,7 +251,7 @@ export default class PhotoDialog extends Vue {
|
|||||||
* Provides cached access to platform capabilities
|
* Provides cached access to platform capabilities
|
||||||
*/
|
*/
|
||||||
get platformCapabilities() {
|
get platformCapabilities() {
|
||||||
return this.$platformService.getCapabilities();
|
return this.platformService.getCapabilities();
|
||||||
}
|
}
|
||||||
|
|
||||||
// =================================================
|
// =================================================
|
||||||
|
|||||||
Vendored
+19
@@ -0,0 +1,19 @@
|
|||||||
|
import type { Camera, Scene } from "three";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type declarations for World.js, which is plain JavaScript.
|
||||||
|
*
|
||||||
|
* Only the members used outside the module are declared; its internals stay
|
||||||
|
* untyped.
|
||||||
|
*/
|
||||||
|
export declare class World {
|
||||||
|
camera: Camera;
|
||||||
|
scene: Scene;
|
||||||
|
|
||||||
|
constructor(container: Element | null, vue: unknown);
|
||||||
|
update(time: number): void;
|
||||||
|
render(): void;
|
||||||
|
start(): void;
|
||||||
|
stop(): void;
|
||||||
|
setExposedWorldProperties(key: string, value: unknown): void;
|
||||||
|
}
|
||||||
@@ -15,6 +15,23 @@ export interface GenericCredWrapper<T extends GenericVerifiableCredential> {
|
|||||||
publicUrls?: Record<string, string>;
|
publicUrls?: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The fields the certificate canvases read off a claim.
|
||||||
|
*
|
||||||
|
* GenericVerifiableCredential carries an index signature of `unknown`, which is
|
||||||
|
* correct for a claim of any type but leaves each field unusable without a
|
||||||
|
* cast. This names the handful the drawing code actually touches. An `agent` is
|
||||||
|
* either a bare DID string or an object carrying one, depending on claim type.
|
||||||
|
*/
|
||||||
|
export interface CertifiableClaim extends GenericVerifiableCredential {
|
||||||
|
agent?: string | { identifier?: string };
|
||||||
|
description?: string;
|
||||||
|
includesObject?: { amountOfThisGood?: number; unitCode?: string };
|
||||||
|
itemOffered?: { description?: string };
|
||||||
|
name?: string;
|
||||||
|
object?: { amountOfThisGood?: number; unitCode?: string };
|
||||||
|
}
|
||||||
|
|
||||||
export interface ErrorResponse {
|
export interface ErrorResponse {
|
||||||
error?: {
|
error?: {
|
||||||
message?: string;
|
message?: string;
|
||||||
|
|||||||
@@ -381,6 +381,19 @@ export function didInfoOrNobody(
|
|||||||
/**
|
/**
|
||||||
* return text description without any references to "you" as user
|
* return text description without any references to "you" as user
|
||||||
*/
|
*/
|
||||||
|
/**
|
||||||
|
* A claim's agent is either a bare DID string or an object carrying one,
|
||||||
|
* depending on the claim type.
|
||||||
|
*
|
||||||
|
* @param agent - the `agent` field of a claim, in either shape
|
||||||
|
* @returns the DID, or undefined when the agent carries none
|
||||||
|
*/
|
||||||
|
export function didOfAgent(
|
||||||
|
agent: string | { identifier?: string } | undefined,
|
||||||
|
): string | undefined {
|
||||||
|
return typeof agent === "string" ? agent : agent?.identifier;
|
||||||
|
}
|
||||||
|
|
||||||
export function didInfoForCertificate(
|
export function didInfoForCertificate(
|
||||||
did: string | undefined,
|
did: string | undefined,
|
||||||
contacts: Contact[],
|
contacts: Contact[],
|
||||||
@@ -676,15 +689,18 @@ export async function setPlanInCache(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts user-friendly message from server error
|
* Extracts user-friendly message from server error
|
||||||
* @param {AxiosErrorResponse} error - Error thrown from Endorser server call
|
*
|
||||||
|
* Takes `unknown` because callers pass it straight from a `catch`, and every
|
||||||
|
* access below is optional-chained, so a value of any shape is safe.
|
||||||
|
*
|
||||||
|
* @param error - Error thrown from an Endorser server call
|
||||||
* @returns {string|undefined} User-friendly message or undefined if none found
|
* @returns {string|undefined} User-friendly message or undefined if none found
|
||||||
*/
|
*/
|
||||||
export function serverMessageForUser(
|
export function serverMessageForUser(error: unknown): string | undefined {
|
||||||
error: AxiosErrorResponse,
|
const err = error as AxiosErrorResponse;
|
||||||
): string | undefined {
|
|
||||||
return (
|
return (
|
||||||
error?.response?.data?.error?.userMessage ||
|
err?.response?.data?.error?.userMessage ||
|
||||||
error?.response?.data?.error?.message
|
err?.response?.data?.error?.message
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2392,6 +2392,16 @@ declare module "@vue/runtime-core" {
|
|||||||
currentActiveDid: string | null;
|
currentActiveDid: string | null;
|
||||||
$updateActiveDid(newDid: string | null): Promise<void>;
|
$updateActiveDid(newDid: string | null): Promise<void>;
|
||||||
|
|
||||||
|
// Reads a column that may hold JSON text or an already-parsed value
|
||||||
|
_parseJsonField<T>(value: unknown, defaultValue: T): T;
|
||||||
|
|
||||||
|
// Account selection, implemented in the mixin above
|
||||||
|
$countAccounts(): Promise<number>;
|
||||||
|
$getAccountDidById(id: number): Promise<string>;
|
||||||
|
$getActiveDid(): Promise<string | null>;
|
||||||
|
$pickNextAccountDid(all: string[], current?: string): string;
|
||||||
|
$setActiveDid(did: string | null): Promise<void>;
|
||||||
|
|
||||||
// Ultra-concise database methods (shortest possible names)
|
// Ultra-concise database methods (shortest possible names)
|
||||||
$db(sql: string, params?: unknown[]): Promise<QueryExecResult | undefined>;
|
$db(sql: string, params?: unknown[]): Promise<QueryExecResult | undefined>;
|
||||||
$exec(sql: string, params?: unknown[]): Promise<DatabaseExecResult>;
|
$exec(sql: string, params?: unknown[]): Promise<DatabaseExecResult>;
|
||||||
|
|||||||
@@ -972,8 +972,6 @@ import "leaflet/dist/leaflet.css";
|
|||||||
import { Buffer } from "buffer/";
|
import { Buffer } from "buffer/";
|
||||||
import "dexie-export-import";
|
import "dexie-export-import";
|
||||||
|
|
||||||
// @ts-expect-error - they aren't exporting it but it's there
|
|
||||||
import { ImportProgress } from "dexie-export-import";
|
|
||||||
import { LeafletMouseEvent } from "leaflet";
|
import { LeafletMouseEvent } from "leaflet";
|
||||||
import * as L from "leaflet";
|
import * as L from "leaflet";
|
||||||
import { IIdentifier } from "@veramo/core";
|
import { IIdentifier } from "@veramo/core";
|
||||||
@@ -2389,19 +2387,6 @@ export default class AccountViewView extends Vue {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private progressCallback(progress: ImportProgress): boolean {
|
|
||||||
logger.log(
|
|
||||||
`Import progress: ${progress.completedRows} of ${progress.totalRows} rows completed.`,
|
|
||||||
);
|
|
||||||
if (progress.done) {
|
|
||||||
this.notify.success(
|
|
||||||
ACCOUNT_VIEW_CONSTANTS.SUCCESS.IMPORT_COMPLETE,
|
|
||||||
TIMEOUTS.LONG,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async checkLimits(): Promise<void> {
|
async checkLimits(): Promise<void> {
|
||||||
this.loadingLimits = true;
|
this.loadingLimits = true;
|
||||||
const did = this.activeDid;
|
const did = this.activeDid;
|
||||||
@@ -3111,30 +3096,6 @@ export default class AccountViewView extends Vue {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear profile location
|
|
||||||
*/
|
|
||||||
private clearProfileLocation(profileData: ProfileData): ProfileData {
|
|
||||||
return {
|
|
||||||
...profileData,
|
|
||||||
latitude: 0,
|
|
||||||
longitude: 0,
|
|
||||||
includeLocation: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get default profile data
|
|
||||||
*/
|
|
||||||
private getDefaultProfile(): ProfileData {
|
|
||||||
return {
|
|
||||||
description: "",
|
|
||||||
latitude: 0,
|
|
||||||
longitude: 0,
|
|
||||||
includeLocation: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete user profile from the partner API
|
* Delete user profile from the partner API
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import { nextTick } from "vue";
|
|||||||
import QRCode from "qrcode";
|
import QRCode from "qrcode";
|
||||||
import { APP_SERVER, NotificationIface } from "../constants/app";
|
import { APP_SERVER, NotificationIface } from "../constants/app";
|
||||||
import * as serverUtil from "../libs/endorserServer";
|
import * as serverUtil from "../libs/endorserServer";
|
||||||
import { GenericCredWrapper, GenericVerifiableCredential } from "../interfaces";
|
import { CertifiableClaim, GenericCredWrapper } from "../interfaces";
|
||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||||
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
||||||
@@ -39,6 +39,13 @@ export default class ClaimCertificateView extends Vue {
|
|||||||
|
|
||||||
async created() {
|
async created() {
|
||||||
this.notify = createNotifyHelpers(this.$notify);
|
this.notify = createNotifyHelpers(this.$notify);
|
||||||
|
|
||||||
|
// Read the id from the URL before the first await, so nothing can render
|
||||||
|
// or act against an empty id while the awaits below resolve.
|
||||||
|
const pathParams = window.location.pathname.substring(
|
||||||
|
"/claim-cert/".length,
|
||||||
|
);
|
||||||
|
this.claimId = pathParams;
|
||||||
const settings = await this.$accountSettings();
|
const settings = await this.$accountSettings();
|
||||||
|
|
||||||
// Get activeDid from active_identity table (single source of truth)
|
// Get activeDid from active_identity table (single source of truth)
|
||||||
@@ -47,10 +54,6 @@ export default class ClaimCertificateView extends Vue {
|
|||||||
this.activeDid = activeIdentity.activeDid || "";
|
this.activeDid = activeIdentity.activeDid || "";
|
||||||
|
|
||||||
this.apiServer = settings.apiServer || "";
|
this.apiServer = settings.apiServer || "";
|
||||||
const pathParams = window.location.pathname.substring(
|
|
||||||
"/claim-cert/".length,
|
|
||||||
);
|
|
||||||
this.claimId = pathParams;
|
|
||||||
await this.fetchClaim();
|
await this.fetchClaim();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +94,7 @@ export default class ClaimCertificateView extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async drawCanvas(
|
async drawCanvas(
|
||||||
claimData: GenericCredWrapper<GenericVerifiableCredential>,
|
claimData: GenericCredWrapper<CertifiableClaim>,
|
||||||
confirmerIds: Array<string>,
|
confirmerIds: Array<string>,
|
||||||
) {
|
) {
|
||||||
const allContacts = await this.$getAllContacts();
|
const allContacts = await this.$getAllContacts();
|
||||||
@@ -142,8 +145,7 @@ export default class ClaimCertificateView extends Vue {
|
|||||||
(CANVAS_WIDTH - presentedWidth) / 2, // Center horizontally
|
(CANVAS_WIDTH - presentedWidth) / 2, // Center horizontally
|
||||||
CANVAS_HEIGHT * 0.37,
|
CANVAS_HEIGHT * 0.37,
|
||||||
);
|
);
|
||||||
const agentDid =
|
const agentDid = serverUtil.didOfAgent(claimData.claim.agent);
|
||||||
claimData.claim.agent.identifier || claimData.claim.agent;
|
|
||||||
const agentText = serverUtil.didInfoForCertificate(
|
const agentText = serverUtil.didInfoForCertificate(
|
||||||
agentDid,
|
agentDid,
|
||||||
allContacts,
|
allContacts,
|
||||||
@@ -168,8 +170,7 @@ export default class ClaimCertificateView extends Vue {
|
|||||||
CANVAS_HEIGHT * 0.37,
|
CANVAS_HEIGHT * 0.37,
|
||||||
);
|
);
|
||||||
// fulfills
|
// fulfills
|
||||||
const agentDid =
|
const agentDid = serverUtil.didOfAgent(claimData.claim.agent);
|
||||||
claimData.claim.agent.identifier || claimData.claim.agent;
|
|
||||||
const agentText = serverUtil.didInfoForCertificate(
|
const agentText = serverUtil.didInfoForCertificate(
|
||||||
agentDid,
|
agentDid,
|
||||||
allContacts,
|
allContacts,
|
||||||
@@ -223,7 +224,7 @@ export default class ClaimCertificateView extends Vue {
|
|||||||
serverUtil.isHiddenDid(claimData.issuer) ||
|
serverUtil.isHiddenDid(claimData.issuer) ||
|
||||||
// don't show if issuer claimed for themselves
|
// don't show if issuer claimed for themselves
|
||||||
// (The confirmations are the good stuff anyway, and self-issued certs shouldn't detract from that.)
|
// (The confirmations are the good stuff anyway, and self-issued certs shouldn't detract from that.)
|
||||||
claimData.issuer !== claimData.claim.agent?.identifier
|
claimData.issuer !== serverUtil.didOfAgent(claimData.claim.agent)
|
||||||
) {
|
) {
|
||||||
ctx.font = "14px Arial";
|
ctx.font = "14px Arial";
|
||||||
let fullIssuer = serverUtil.didInfoForCertificate(
|
let fullIssuer = serverUtil.didInfoForCertificate(
|
||||||
|
|||||||
@@ -13,10 +13,7 @@ import QRCode from "qrcode";
|
|||||||
|
|
||||||
import { APP_SERVER, NotificationIface } from "../constants/app";
|
import { APP_SERVER, NotificationIface } from "../constants/app";
|
||||||
import * as endorserServer from "../libs/endorserServer";
|
import * as endorserServer from "../libs/endorserServer";
|
||||||
import {
|
import { CertifiableClaim, GenericCredWrapper } from "../interfaces/common";
|
||||||
GenericCredWrapper,
|
|
||||||
GenericVerifiableCredential,
|
|
||||||
} from "../interfaces/common";
|
|
||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
import { Contact } from "@/db/tables/contacts";
|
import { Contact } from "@/db/tables/contacts";
|
||||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||||
@@ -53,6 +50,13 @@ export default class ClaimReportCertificateView extends Vue {
|
|||||||
// Initialize notification helper
|
// Initialize notification helper
|
||||||
this.notify = createNotifyHelpers(this.$notify);
|
this.notify = createNotifyHelpers(this.$notify);
|
||||||
|
|
||||||
|
// Read the id from the URL before the first await, so nothing can render
|
||||||
|
// or act against an empty id while the awaits below resolve.
|
||||||
|
const pathParams = window.location.pathname.substring(
|
||||||
|
"/claim-cert/".length,
|
||||||
|
);
|
||||||
|
this.claimId = pathParams;
|
||||||
|
|
||||||
const settings = await this.$accountSettings();
|
const settings = await this.$accountSettings();
|
||||||
|
|
||||||
// Get activeDid from active_identity table (single source of truth)
|
// Get activeDid from active_identity table (single source of truth)
|
||||||
@@ -61,10 +65,6 @@ export default class ClaimReportCertificateView extends Vue {
|
|||||||
this.activeDid = activeIdentity.activeDid || "";
|
this.activeDid = activeIdentity.activeDid || "";
|
||||||
|
|
||||||
this.apiServer = settings.apiServer || "";
|
this.apiServer = settings.apiServer || "";
|
||||||
const pathParams = window.location.pathname.substring(
|
|
||||||
"/claim-cert/".length,
|
|
||||||
);
|
|
||||||
this.claimId = pathParams;
|
|
||||||
await this.fetchClaim();
|
await this.fetchClaim();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ export default class ClaimReportCertificateView extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async drawCanvas(claimData: GenericCredWrapper<GenericVerifiableCredential>) {
|
async drawCanvas(claimData: GenericCredWrapper<CertifiableClaim>) {
|
||||||
if (!claimData || !claimData.claim) {
|
if (!claimData || !claimData.claim) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -502,6 +502,7 @@ export default class ConfirmGiftView extends Vue {
|
|||||||
apiServer = "";
|
apiServer = "";
|
||||||
|
|
||||||
canShare = false;
|
canShare = false;
|
||||||
|
claimId = "";
|
||||||
confirmerIdList: string[] = []; // list of DIDs that have confirmed this claim excluding the issuer
|
confirmerIdList: string[] = []; // list of DIDs that have confirmed this claim excluding the issuer
|
||||||
confsVisibleErrorMessage = "";
|
confsVisibleErrorMessage = "";
|
||||||
confsVisibleToIdList: string[] = []; // list of DIDs that can see any confirmer
|
confsVisibleToIdList: string[] = []; // list of DIDs that can see any confirmer
|
||||||
@@ -549,6 +550,11 @@ export default class ConfirmGiftView extends Vue {
|
|||||||
*/
|
*/
|
||||||
async mounted() {
|
async mounted() {
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
|
// Read the claim id from the URL before the first await, so nothing can
|
||||||
|
// render or act against an empty id while the awaits below resolve.
|
||||||
|
this.claimId = decodeURIComponent(
|
||||||
|
window.location.pathname.substring("/confirm-gift/".length),
|
||||||
|
);
|
||||||
try {
|
try {
|
||||||
await this.initializeSettings();
|
await this.initializeSettings();
|
||||||
await this.loadClaimFromUrl();
|
await this.loadClaimFromUrl();
|
||||||
@@ -586,15 +592,11 @@ export default class ConfirmGiftView extends Vue {
|
|||||||
* Loads and processes claim from URL parameters
|
* Loads and processes claim from URL parameters
|
||||||
*/
|
*/
|
||||||
private async loadClaimFromUrl() {
|
private async loadClaimFromUrl() {
|
||||||
const pathParam = window.location.pathname.substring(
|
const claimId = this.claimId;
|
||||||
"/confirm-gift/".length,
|
if (!claimId) {
|
||||||
);
|
|
||||||
if (!pathParam) {
|
|
||||||
throw new Error("No claim ID was provided.");
|
throw new Error("No claim ID was provided.");
|
||||||
}
|
}
|
||||||
|
|
||||||
const claimId = decodeURIComponent(pathParam);
|
|
||||||
|
|
||||||
// Use production URL for sharing to avoid localhost issues in development
|
// Use production URL for sharing to avoid localhost issues in development
|
||||||
this.windowLocation = `${APP_SERVER}/deep-link/confirm-gift/${claimId}`;
|
this.windowLocation = `${APP_SERVER}/deep-link/confirm-gift/${claimId}`;
|
||||||
|
|
||||||
|
|||||||
@@ -178,11 +178,11 @@
|
|||||||
{{ capitalizeAndInsertSpacesBeforeCaps(contactField) }}
|
{{ capitalizeAndInsertSpacesBeforeCaps(contactField) }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="contactField === 'labels'" class="border p-1">
|
<div v-if="contactField === 'labels'" class="border p-1">
|
||||||
{{ value.old.join(", ") }}
|
{{ joinIfList(value.old) }}
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="border p-1">{{ value.old }}</div>
|
<div v-else class="border p-1">{{ value.old }}</div>
|
||||||
<div v-if="contactField === 'labels'" class="border p-1">
|
<div v-if="contactField === 'labels'" class="border p-1">
|
||||||
{{ value.new.join(", ") }}
|
{{ joinIfList(value.new) }}
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="border p-1">{{ value.new }}</div>
|
<div v-else class="border p-1">{{ value.new }}</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -398,6 +398,16 @@ export default class ContactImportView extends Vue {
|
|||||||
// Constants
|
// Constants
|
||||||
AppString = AppString;
|
AppString = AppString;
|
||||||
capitalizeAndInsertSpacesBeforeCaps = capitalizeAndInsertSpacesBeforeCaps;
|
capitalizeAndInsertSpacesBeforeCaps = capitalizeAndInsertSpacesBeforeCaps;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders one side of a contact difference.
|
||||||
|
*
|
||||||
|
* The `labels` field holds a list while every other field holds a scalar, and
|
||||||
|
* the template cannot narrow the union, so the join happens here.
|
||||||
|
*/
|
||||||
|
joinIfList(value: unknown): string {
|
||||||
|
return Array.isArray(value) ? value.join(", ") : String(value ?? "");
|
||||||
|
}
|
||||||
libsUtil = libsUtil;
|
libsUtil = libsUtil;
|
||||||
R = R;
|
R = R;
|
||||||
|
|
||||||
|
|||||||
@@ -728,16 +728,8 @@ export default class ContactQRScanShow extends Vue {
|
|||||||
|
|
||||||
// Add new contact
|
// Add new contact
|
||||||
// @ts-expect-error because we're just using the value to store to the DB
|
// @ts-expect-error because we're just using the value to store to the DB
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
contact.contactMethods = JSON.stringify(
|
contact.contactMethods = JSON.stringify(
|
||||||
(
|
this._parseJsonField(contact.contactMethods, []),
|
||||||
this as {
|
|
||||||
_parseJsonField: (
|
|
||||||
value: unknown,
|
|
||||||
defaultValue: unknown[],
|
|
||||||
) => unknown[];
|
|
||||||
}
|
|
||||||
)._parseJsonField(contact.contactMethods, []),
|
|
||||||
);
|
);
|
||||||
await this.$insertContact(contact);
|
await this.$insertContact(contact);
|
||||||
|
|
||||||
|
|||||||
@@ -533,6 +533,7 @@ export default class DIDView extends Vue {
|
|||||||
userProfileData: UserProfile | null = null;
|
userProfileData: UserProfile | null = null;
|
||||||
userProfileError: string | null = null;
|
userProfileError: string | null = null;
|
||||||
userProfileLoading = false;
|
userProfileLoading = false;
|
||||||
|
didFromUrl = "";
|
||||||
viewingDid?: string;
|
viewingDid?: string;
|
||||||
|
|
||||||
capitalizeAndInsertSpacesBeforeCaps = capitalizeAndInsertSpacesBeforeCaps;
|
capitalizeAndInsertSpacesBeforeCaps = capitalizeAndInsertSpacesBeforeCaps;
|
||||||
@@ -558,6 +559,10 @@ export default class DIDView extends Vue {
|
|||||||
* 5. Determines if viewing own DID
|
* 5. Determines if viewing own DID
|
||||||
*/
|
*/
|
||||||
async mounted() {
|
async mounted() {
|
||||||
|
// Read the DID from the URL before the first await, so nothing can render
|
||||||
|
// or act against an empty value while the awaits below resolve.
|
||||||
|
this.didFromUrl = window.location.pathname.substring("/did/".length);
|
||||||
|
|
||||||
await this.initializeSettings();
|
await this.initializeSettings();
|
||||||
await this.determineDIDToDisplay();
|
await this.determineDIDToDisplay();
|
||||||
if (this.viewingDid) {
|
if (this.viewingDid) {
|
||||||
@@ -592,8 +597,7 @@ export default class DIDView extends Vue {
|
|||||||
* Validates DID format and shows error for invalid DIDs
|
* Validates DID format and shows error for invalid DIDs
|
||||||
*/
|
*/
|
||||||
private async determineDIDToDisplay() {
|
private async determineDIDToDisplay() {
|
||||||
const pathParam = window.location.pathname.substring("/did/".length);
|
let showDid = this.didFromUrl;
|
||||||
let showDid = pathParam;
|
|
||||||
|
|
||||||
if (!showDid) {
|
if (!showDid) {
|
||||||
// No DID provided in URL, use active DID
|
// No DID provided in URL, use active DID
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ export default class DeepLinkRedirectView extends Vue {
|
|||||||
webUrl: string | null = null; // web link, eg "https://timesafari.app/..."
|
webUrl: string | null = null; // web link, eg "https://timesafari.app/..."
|
||||||
isDevelopment: boolean = false;
|
isDevelopment: boolean = false;
|
||||||
userAgent: string = "";
|
userAgent: string = "";
|
||||||
private platformService = PlatformServiceFactory.getInstance();
|
platformService = PlatformServiceFactory.getInstance();
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
// Get the path from the route parameter (catch-all parameter)
|
// Get the path from the route parameter (catch-all parameter)
|
||||||
|
|||||||
@@ -547,7 +547,7 @@ export default class GiftedDetails extends Vue {
|
|||||||
confirmDeleteImage() {
|
confirmDeleteImage() {
|
||||||
this.notify.confirm(
|
this.notify.confirm(
|
||||||
NOTIFY_GIFTED_DETAILS_DELETE_IMAGE_CONFIRM.message,
|
NOTIFY_GIFTED_DETAILS_DELETE_IMAGE_CONFIRM.message,
|
||||||
() => {
|
async () => {
|
||||||
// Stage the image for deletion on submit rather than deleting immediately,
|
// Stage the image for deletion on submit rather than deleting immediately,
|
||||||
// so that canceling the edit doesn't destroy the referenced image.
|
// so that canceling the edit doesn't destroy the referenced image.
|
||||||
this.imageUrlToDelete = this.imageUrl;
|
this.imageUrlToDelete = this.imageUrl;
|
||||||
|
|||||||
@@ -115,9 +115,12 @@
|
|||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Vue } from "vue-facing-decorator";
|
import { Component, Vue } from "vue-facing-decorator";
|
||||||
|
import { Router } from "vue-router";
|
||||||
|
|
||||||
import QuickNav from "../components/QuickNav.vue";
|
import QuickNav from "../components/QuickNav.vue";
|
||||||
|
|
||||||
@Component({ components: { QuickNav } })
|
@Component({ components: { QuickNav } })
|
||||||
export default class HelpNotificationTypesView extends Vue {}
|
export default class HelpNotificationTypesView extends Vue {
|
||||||
|
$router!: Router;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -133,12 +133,14 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Vue } from "vue-facing-decorator";
|
import { Component, Vue } from "vue-facing-decorator";
|
||||||
|
import { Router } from "vue-router";
|
||||||
|
|
||||||
import QuickNav from "../components/QuickNav.vue";
|
import QuickNav from "../components/QuickNav.vue";
|
||||||
import { SUPPORT_EMAIL } from "../constants/app";
|
import { SUPPORT_EMAIL } from "../constants/app";
|
||||||
|
|
||||||
@Component({ components: { QuickNav } })
|
@Component({ components: { QuickNav } })
|
||||||
export default class HelpTermsView extends Vue {
|
export default class HelpTermsView extends Vue {
|
||||||
|
$router!: Router;
|
||||||
SUPPORT_EMAIL = SUPPORT_EMAIL;
|
SUPPORT_EMAIL = SUPPORT_EMAIL;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1855,14 +1855,6 @@ export default class HomeView extends Vue {
|
|||||||
this.isImageViewerOpen = true;
|
this.isImageViewerOpen = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
openPersonDialog(giver?: GiverReceiverInputInfo, prompt?: string) {
|
||||||
this.openDialog(giver, prompt);
|
this.openDialog(giver, prompt);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -236,8 +236,10 @@ export default class IdentitySwitcherView extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async switchAccount(did?: string) {
|
async switchAccount(did?: string) {
|
||||||
// Update the active DID in the active_identity table
|
// Update the active DID in the active_identity table.
|
||||||
await this.$updateActiveDid(did);
|
// Coerce to null: the value reaches a SQL parameter, where undefined is not
|
||||||
|
// a valid binding.
|
||||||
|
await this.$updateActiveDid(did ?? null);
|
||||||
|
|
||||||
// Check if we need to load user-specific settings for the new DID
|
// Check if we need to load user-specific settings for the new DID
|
||||||
if (did) {
|
if (did) {
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ export default class ImportAccountView extends Vue {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
const filteredDecryptedAccounts: Account[] = decryptedAccounts.filter(
|
const filteredDecryptedAccounts: Account[] = decryptedAccounts.filter(
|
||||||
(account) => account !== undefined,
|
(account): account is Account => account !== undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
// group by account.mnemonic
|
// group by account.mnemonic
|
||||||
|
|||||||
@@ -1232,7 +1232,7 @@ export default class ProjectViewView extends Vue {
|
|||||||
undefined,
|
undefined,
|
||||||
);
|
);
|
||||||
// Immediately select "Unnamed" and move to Step 2
|
// Immediately select "Unnamed" and move to Step 2
|
||||||
(this.$refs.giveDialogToThis as GiftedDialog).selectGiver();
|
(this.$refs.giveDialogToThis as GiftedDialog).selectGiverPerson();
|
||||||
} else {
|
} else {
|
||||||
// Open straight to Step 2 with current user as giver and current project as recipient
|
// Open straight to Step 2 with current user as giver and current project as recipient
|
||||||
(this.$refs.giveDialogToThis as GiftedDialog).open(
|
(this.$refs.giveDialogToThis as GiftedDialog).open(
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export default class StatisticsView extends Vue {
|
|||||||
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
||||||
$router!: Router;
|
$router!: Router;
|
||||||
|
|
||||||
world: World;
|
world!: World;
|
||||||
worldProperties: Dictionary<number> = {};
|
worldProperties: Dictionary<number> = {};
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|||||||
@@ -745,7 +745,7 @@ export default class Help extends Vue {
|
|||||||
* @returns True if not on production server (enables test utilities)
|
* @returns True if not on production server (enables test utilities)
|
||||||
*/
|
*/
|
||||||
public isNotProdServer() {
|
public isNotProdServer() {
|
||||||
return isNotProdServerUtil(this.apiServer);
|
return isNotProdServerUtil(this.apiServer ?? "");
|
||||||
}
|
}
|
||||||
|
|
||||||
async registerMe() {
|
async registerMe() {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Vue } from "vue-facing-decorator";
|
import { Component, Vue } from "vue-facing-decorator";
|
||||||
|
import { Router } from "vue-router";
|
||||||
import NotificationDebugPanel from "@/components/dev/NotificationDebugPanel.vue";
|
import NotificationDebugPanel from "@/components/dev/NotificationDebugPanel.vue";
|
||||||
import { includeDevToolkitRoutes } from "@/utils/includeDevToolkitRoutes";
|
import { includeDevToolkitRoutes } from "@/utils/includeDevToolkitRoutes";
|
||||||
|
|
||||||
@@ -38,6 +39,7 @@ import { includeDevToolkitRoutes } from "@/utils/includeDevToolkitRoutes";
|
|||||||
components: { NotificationDebugPanel },
|
components: { NotificationDebugPanel },
|
||||||
})
|
})
|
||||||
export default class NotificationDebugView extends Vue {
|
export default class NotificationDebugView extends Vue {
|
||||||
|
$router!: Router;
|
||||||
readonly isDev: boolean = includeDevToolkitRoutes;
|
readonly isDev: boolean = includeDevToolkitRoutes;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user