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": {
|
||||
"lint": "eslint --ext .js,.ts,.vue --ignore-path .gitignore 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:vue": "vue-tsc --noEmit",
|
||||
"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",
|
||||
"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",
|
||||
"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:mobile": "./scripts/test-mobile.sh",
|
||||
"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">
|
||||
import { Vue, Component, Prop } from "vue-facing-decorator";
|
||||
import { NotificationIface } from "@/constants/app";
|
||||
|
||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||
import { SOMEONE_UNNAMED } from "@/constants/entities";
|
||||
@@ -149,10 +150,7 @@ export default class BulkMembersDialog extends Vue {
|
||||
@Prop({ required: true }) isOrganizer!: boolean;
|
||||
|
||||
// Vue notification system
|
||||
$notify!: (
|
||||
notification: { group: string; type: string; title: string; text: string },
|
||||
timeout?: number,
|
||||
) => void;
|
||||
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
||||
|
||||
// Notification system
|
||||
notify!: ReturnType<typeof createNotifyHelpers>;
|
||||
@@ -381,9 +379,6 @@ export default class BulkMembersDialog extends Vue {
|
||||
contact,
|
||||
);
|
||||
if (result.success) {
|
||||
if (result.embeddedRecordError) {
|
||||
throw new Error(result.embeddedRecordError);
|
||||
}
|
||||
await this.$updateContact(member.did, { registered: true });
|
||||
} else {
|
||||
throw result;
|
||||
|
||||
@@ -106,6 +106,7 @@ import { Router } from "vue-router";
|
||||
import * as R from "ramda";
|
||||
|
||||
import { NotificationIface } from "../constants/app";
|
||||
import { Contact } from "../db/tables/contacts";
|
||||
|
||||
import { logger } from "../utils/logger";
|
||||
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
|
||||
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 */
|
||||
private observer!: IntersectionObserver;
|
||||
|
||||
/** Flag to track initial render state */
|
||||
private isInitialRender = true;
|
||||
|
||||
/** Flag to prevent multiple simultaneous loading states */
|
||||
private isLoading = false;
|
||||
|
||||
|
||||
@@ -251,7 +251,7 @@ export default class PhotoDialog extends Vue {
|
||||
* Provides cached access to platform capabilities
|
||||
*/
|
||||
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>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
error?: {
|
||||
message?: string;
|
||||
|
||||
@@ -381,6 +381,19 @@ export function didInfoOrNobody(
|
||||
/**
|
||||
* 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(
|
||||
did: string | undefined,
|
||||
contacts: Contact[],
|
||||
@@ -676,15 +689,18 @@ export async function setPlanInCache(
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
export function serverMessageForUser(
|
||||
error: AxiosErrorResponse,
|
||||
): string | undefined {
|
||||
export function serverMessageForUser(error: unknown): string | undefined {
|
||||
const err = error as AxiosErrorResponse;
|
||||
return (
|
||||
error?.response?.data?.error?.userMessage ||
|
||||
error?.response?.data?.error?.message
|
||||
err?.response?.data?.error?.userMessage ||
|
||||
err?.response?.data?.error?.message
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -2392,6 +2392,16 @@ declare module "@vue/runtime-core" {
|
||||
currentActiveDid: string | null;
|
||||
$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)
|
||||
$db(sql: string, params?: unknown[]): Promise<QueryExecResult | undefined>;
|
||||
$exec(sql: string, params?: unknown[]): Promise<DatabaseExecResult>;
|
||||
|
||||
@@ -972,8 +972,6 @@ import "leaflet/dist/leaflet.css";
|
||||
import { Buffer } from "buffer/";
|
||||
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 * as L from "leaflet";
|
||||
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> {
|
||||
this.loadingLimits = true;
|
||||
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
|
||||
*/
|
||||
|
||||
@@ -16,7 +16,7 @@ import { nextTick } from "vue";
|
||||
import QRCode from "qrcode";
|
||||
import { APP_SERVER, NotificationIface } from "../constants/app";
|
||||
import * as serverUtil from "../libs/endorserServer";
|
||||
import { GenericCredWrapper, GenericVerifiableCredential } from "../interfaces";
|
||||
import { CertifiableClaim, GenericCredWrapper } from "../interfaces";
|
||||
import { logger } from "../utils/logger";
|
||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
||||
@@ -39,6 +39,13 @@ export default class ClaimCertificateView extends Vue {
|
||||
|
||||
async created() {
|
||||
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();
|
||||
|
||||
// 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.apiServer = settings.apiServer || "";
|
||||
const pathParams = window.location.pathname.substring(
|
||||
"/claim-cert/".length,
|
||||
);
|
||||
this.claimId = pathParams;
|
||||
await this.fetchClaim();
|
||||
}
|
||||
|
||||
@@ -91,7 +94,7 @@ export default class ClaimCertificateView extends Vue {
|
||||
}
|
||||
|
||||
async drawCanvas(
|
||||
claimData: GenericCredWrapper<GenericVerifiableCredential>,
|
||||
claimData: GenericCredWrapper<CertifiableClaim>,
|
||||
confirmerIds: Array<string>,
|
||||
) {
|
||||
const allContacts = await this.$getAllContacts();
|
||||
@@ -142,8 +145,7 @@ export default class ClaimCertificateView extends Vue {
|
||||
(CANVAS_WIDTH - presentedWidth) / 2, // Center horizontally
|
||||
CANVAS_HEIGHT * 0.37,
|
||||
);
|
||||
const agentDid =
|
||||
claimData.claim.agent.identifier || claimData.claim.agent;
|
||||
const agentDid = serverUtil.didOfAgent(claimData.claim.agent);
|
||||
const agentText = serverUtil.didInfoForCertificate(
|
||||
agentDid,
|
||||
allContacts,
|
||||
@@ -168,8 +170,7 @@ export default class ClaimCertificateView extends Vue {
|
||||
CANVAS_HEIGHT * 0.37,
|
||||
);
|
||||
// fulfills
|
||||
const agentDid =
|
||||
claimData.claim.agent.identifier || claimData.claim.agent;
|
||||
const agentDid = serverUtil.didOfAgent(claimData.claim.agent);
|
||||
const agentText = serverUtil.didInfoForCertificate(
|
||||
agentDid,
|
||||
allContacts,
|
||||
@@ -223,7 +224,7 @@ export default class ClaimCertificateView extends Vue {
|
||||
serverUtil.isHiddenDid(claimData.issuer) ||
|
||||
// don't show if issuer claimed for themselves
|
||||
// (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";
|
||||
let fullIssuer = serverUtil.didInfoForCertificate(
|
||||
|
||||
@@ -13,10 +13,7 @@ import QRCode from "qrcode";
|
||||
|
||||
import { APP_SERVER, NotificationIface } from "../constants/app";
|
||||
import * as endorserServer from "../libs/endorserServer";
|
||||
import {
|
||||
GenericCredWrapper,
|
||||
GenericVerifiableCredential,
|
||||
} from "../interfaces/common";
|
||||
import { CertifiableClaim, GenericCredWrapper } from "../interfaces/common";
|
||||
import { logger } from "../utils/logger";
|
||||
import { Contact } from "@/db/tables/contacts";
|
||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||
@@ -53,6 +50,13 @@ export default class ClaimReportCertificateView extends Vue {
|
||||
// Initialize notification helper
|
||||
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();
|
||||
|
||||
// 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.apiServer = settings.apiServer || "";
|
||||
const pathParams = window.location.pathname.substring(
|
||||
"/claim-cert/".length,
|
||||
);
|
||||
this.claimId = pathParams;
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -502,6 +502,7 @@ export default class ConfirmGiftView extends Vue {
|
||||
apiServer = "";
|
||||
|
||||
canShare = false;
|
||||
claimId = "";
|
||||
confirmerIdList: string[] = []; // list of DIDs that have confirmed this claim excluding the issuer
|
||||
confsVisibleErrorMessage = "";
|
||||
confsVisibleToIdList: string[] = []; // list of DIDs that can see any confirmer
|
||||
@@ -549,6 +550,11 @@ export default class ConfirmGiftView extends Vue {
|
||||
*/
|
||||
async mounted() {
|
||||
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 {
|
||||
await this.initializeSettings();
|
||||
await this.loadClaimFromUrl();
|
||||
@@ -586,15 +592,11 @@ export default class ConfirmGiftView extends Vue {
|
||||
* Loads and processes claim from URL parameters
|
||||
*/
|
||||
private async loadClaimFromUrl() {
|
||||
const pathParam = window.location.pathname.substring(
|
||||
"/confirm-gift/".length,
|
||||
);
|
||||
if (!pathParam) {
|
||||
const claimId = this.claimId;
|
||||
if (!claimId) {
|
||||
throw new Error("No claim ID was provided.");
|
||||
}
|
||||
|
||||
const claimId = decodeURIComponent(pathParam);
|
||||
|
||||
// Use production URL for sharing to avoid localhost issues in development
|
||||
this.windowLocation = `${APP_SERVER}/deep-link/confirm-gift/${claimId}`;
|
||||
|
||||
|
||||
@@ -178,11 +178,11 @@
|
||||
{{ capitalizeAndInsertSpacesBeforeCaps(contactField) }}
|
||||
</div>
|
||||
<div v-if="contactField === 'labels'" class="border p-1">
|
||||
{{ value.old.join(", ") }}
|
||||
{{ joinIfList(value.old) }}
|
||||
</div>
|
||||
<div v-else class="border p-1">{{ value.old }}</div>
|
||||
<div v-if="contactField === 'labels'" class="border p-1">
|
||||
{{ value.new.join(", ") }}
|
||||
{{ joinIfList(value.new) }}
|
||||
</div>
|
||||
<div v-else class="border p-1">{{ value.new }}</div>
|
||||
</div>
|
||||
@@ -398,6 +398,16 @@ export default class ContactImportView extends Vue {
|
||||
// Constants
|
||||
AppString = AppString;
|
||||
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;
|
||||
R = R;
|
||||
|
||||
|
||||
@@ -728,16 +728,8 @@ export default class ContactQRScanShow extends Vue {
|
||||
|
||||
// Add new contact
|
||||
// @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(
|
||||
(
|
||||
this as {
|
||||
_parseJsonField: (
|
||||
value: unknown,
|
||||
defaultValue: unknown[],
|
||||
) => unknown[];
|
||||
}
|
||||
)._parseJsonField(contact.contactMethods, []),
|
||||
this._parseJsonField(contact.contactMethods, []),
|
||||
);
|
||||
await this.$insertContact(contact);
|
||||
|
||||
|
||||
@@ -533,6 +533,7 @@ export default class DIDView extends Vue {
|
||||
userProfileData: UserProfile | null = null;
|
||||
userProfileError: string | null = null;
|
||||
userProfileLoading = false;
|
||||
didFromUrl = "";
|
||||
viewingDid?: string;
|
||||
|
||||
capitalizeAndInsertSpacesBeforeCaps = capitalizeAndInsertSpacesBeforeCaps;
|
||||
@@ -558,6 +559,10 @@ export default class DIDView extends Vue {
|
||||
* 5. Determines if viewing own DID
|
||||
*/
|
||||
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.determineDIDToDisplay();
|
||||
if (this.viewingDid) {
|
||||
@@ -592,8 +597,7 @@ export default class DIDView extends Vue {
|
||||
* Validates DID format and shows error for invalid DIDs
|
||||
*/
|
||||
private async determineDIDToDisplay() {
|
||||
const pathParam = window.location.pathname.substring("/did/".length);
|
||||
let showDid = pathParam;
|
||||
let showDid = this.didFromUrl;
|
||||
|
||||
if (!showDid) {
|
||||
// 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/..."
|
||||
isDevelopment: boolean = false;
|
||||
userAgent: string = "";
|
||||
private platformService = PlatformServiceFactory.getInstance();
|
||||
platformService = PlatformServiceFactory.getInstance();
|
||||
|
||||
mounted() {
|
||||
// Get the path from the route parameter (catch-all parameter)
|
||||
|
||||
@@ -547,7 +547,7 @@ export default class GiftedDetails extends Vue {
|
||||
confirmDeleteImage() {
|
||||
this.notify.confirm(
|
||||
NOTIFY_GIFTED_DETAILS_DELETE_IMAGE_CONFIRM.message,
|
||||
() => {
|
||||
async () => {
|
||||
// Stage the image for deletion on submit rather than deleting immediately,
|
||||
// so that canceling the edit doesn't destroy the referenced image.
|
||||
this.imageUrlToDelete = this.imageUrl;
|
||||
|
||||
@@ -115,9 +115,12 @@
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from "vue-facing-decorator";
|
||||
import { Router } from "vue-router";
|
||||
|
||||
import QuickNav from "../components/QuickNav.vue";
|
||||
|
||||
@Component({ components: { QuickNav } })
|
||||
export default class HelpNotificationTypesView extends Vue {}
|
||||
export default class HelpNotificationTypesView extends Vue {
|
||||
$router!: Router;
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -133,12 +133,14 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from "vue-facing-decorator";
|
||||
import { Router } from "vue-router";
|
||||
|
||||
import QuickNav from "../components/QuickNav.vue";
|
||||
import { SUPPORT_EMAIL } from "../constants/app";
|
||||
|
||||
@Component({ components: { QuickNav } })
|
||||
export default class HelpTermsView extends Vue {
|
||||
$router!: Router;
|
||||
SUPPORT_EMAIL = SUPPORT_EMAIL;
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1855,14 +1855,6 @@ export default class HomeView extends Vue {
|
||||
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) {
|
||||
this.openDialog(giver, prompt);
|
||||
}
|
||||
|
||||
@@ -236,8 +236,10 @@ export default class IdentitySwitcherView extends Vue {
|
||||
}
|
||||
|
||||
async switchAccount(did?: string) {
|
||||
// Update the active DID in the active_identity table
|
||||
await this.$updateActiveDid(did);
|
||||
// Update the active DID in the active_identity table.
|
||||
// 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
|
||||
if (did) {
|
||||
|
||||
@@ -130,7 +130,7 @@ export default class ImportAccountView extends Vue {
|
||||
}),
|
||||
);
|
||||
const filteredDecryptedAccounts: Account[] = decryptedAccounts.filter(
|
||||
(account) => account !== undefined,
|
||||
(account): account is Account => account !== undefined,
|
||||
);
|
||||
|
||||
// group by account.mnemonic
|
||||
|
||||
@@ -1232,7 +1232,7 @@ export default class ProjectViewView extends Vue {
|
||||
undefined,
|
||||
);
|
||||
// Immediately select "Unnamed" and move to Step 2
|
||||
(this.$refs.giveDialogToThis as GiftedDialog).selectGiver();
|
||||
(this.$refs.giveDialogToThis as GiftedDialog).selectGiverPerson();
|
||||
} else {
|
||||
// Open straight to Step 2 with current user as giver and current project as recipient
|
||||
(this.$refs.giveDialogToThis as GiftedDialog).open(
|
||||
|
||||
@@ -79,7 +79,7 @@ export default class StatisticsView extends Vue {
|
||||
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
||||
$router!: Router;
|
||||
|
||||
world: World;
|
||||
world!: World;
|
||||
worldProperties: Dictionary<number> = {};
|
||||
|
||||
mounted() {
|
||||
|
||||
@@ -745,7 +745,7 @@ export default class Help extends Vue {
|
||||
* @returns True if not on production server (enables test utilities)
|
||||
*/
|
||||
public isNotProdServer() {
|
||||
return isNotProdServerUtil(this.apiServer);
|
||||
return isNotProdServerUtil(this.apiServer ?? "");
|
||||
}
|
||||
|
||||
async registerMe() {
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from "vue-facing-decorator";
|
||||
import { Router } from "vue-router";
|
||||
import NotificationDebugPanel from "@/components/dev/NotificationDebugPanel.vue";
|
||||
import { includeDevToolkitRoutes } from "@/utils/includeDevToolkitRoutes";
|
||||
|
||||
@@ -38,6 +39,7 @@ import { includeDevToolkitRoutes } from "@/utils/includeDevToolkitRoutes";
|
||||
components: { NotificationDebugPanel },
|
||||
})
|
||||
export default class NotificationDebugView extends Vue {
|
||||
$router!: Router;
|
||||
readonly isDev: boolean = includeDevToolkitRoutes;
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user