Browse Source

WIP: certificate view and dependency updates

- Update certificate view canvas rendering and QR code generation
- Upgrade dependencies (expo-file-system, expo-font, expo-keep-awake)
- Fix type imports for nostr-tools and dexie-export-import
- Update vite config for better dependency resolution
- Clean up main entry points (capacitor, electron, pywebview)
- Improve error handling in API and plan services
- Add type safety to API error handling
- Update build configuration for platform-specific builds

This is a work in progress commit focusing on certificate view improvements
and dependency maintenance. Some type definitions and build configurations
may need further refinement.
side_step
Matthew Raymer 1 month ago
parent
commit
a8e15804a6
  1. 0
      id:
  2. 1055
      package-lock.json
  3. 4
      package.json
  4. 39
      src/main.capacitor.ts
  5. 21
      src/main.common.ts
  6. 2
      src/main.electron.ts
  7. 2
      src/main.pywebview.ts
  8. 16
      src/services/api.ts
  9. 71
      src/services/plan.ts
  10. 2
      src/views/AccountViewView.vue
  11. 5
      src/views/NewEditProjectView.vue
  12. 18
      vite.config.common.mts
  13. 4
      vite.config.dev.mts

0
id:

1055
package-lock.json

File diff suppressed because it is too large

4
package.json

@ -6,7 +6,7 @@
"name": "TimeSafari Team" "name": "TimeSafari Team"
}, },
"scripts": { "scripts": {
"dev": "vite", "dev": "vite --config vite.config.dev.mts",
"serve": "vite preview", "serve": "vite preview",
"build": "VITE_GIT_HASH=`git log -1 --pretty=format:%h` vite build", "build": "VITE_GIT_HASH=`git log -1 --pretty=format:%h` vite build",
"lint": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src", "lint": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src",
@ -66,7 +66,7 @@
"cbor-x": "^1.5.9", "cbor-x": "^1.5.9",
"class-transformer": "^0.5.1", "class-transformer": "^0.5.1",
"dexie": "^3.2.7", "dexie": "^3.2.7",
"dexie-export-import": "^4.1.1", "dexie-export-import": "^4.1.4",
"did-jwt": "^7.4.7", "did-jwt": "^7.4.7",
"did-resolver": "^4.1.0", "did-resolver": "^4.1.0",
"ethereum-cryptography": "^2.1.3", "ethereum-cryptography": "^2.1.3",

39
src/main.capacitor.ts

@ -2,21 +2,16 @@ import { initializeApp } from "./main.common";
import { App } from "@capacitor/app"; import { App } from "@capacitor/app";
import router from "./router"; import router from "./router";
import { handleApiError } from "./services/api"; import { handleApiError } from "./services/api";
import { loadPlanWithRetry } from "./services/plan";
import { Capacitor } from '@capacitor/core';
console.log("[Capacitor] Starting initialization"); console.log("[Capacitor] Starting initialization");
console.log("[Capacitor] Platform:", process.env.VITE_PLATFORM); console.log("[Capacitor] Platform:", process.env.VITE_PLATFORM);
const app = initializeApp(); const app = initializeApp();
// Store initial deep link if app is not ready
let pendingDeepLink: string | null = null;
// Initialize API error handling // Initialize API error handling
window.addEventListener('unhandledrejection', (event) => { window.addEventListener("unhandledrejection", (event) => {
if (event.reason?.response) { if (event.reason?.response) {
handleApiError(event.reason, event.reason.config?.url || 'unknown'); handleApiError(event.reason, event.reason.config?.url || "unknown");
} }
}); });
@ -25,41 +20,43 @@ const handleDeepLink = async (data: { url: string }) => {
try { try {
console.log("[Capacitor Deep Link] START Handler"); console.log("[Capacitor Deep Link] START Handler");
console.log("[Capacitor Deep Link] Received URL:", data.url); console.log("[Capacitor Deep Link] Received URL:", data.url);
// Wait for router to be ready // Wait for router to be ready
await router.isReady(); await router.isReady();
// Parse the custom URL scheme // Parse the custom URL scheme
const parts = data.url.split('://'); const parts = data.url.split("://");
if (parts.length !== 2) { if (parts.length !== 2) {
throw new Error('Invalid URL format'); throw new Error("Invalid URL format");
} }
const path = parts[1]; // This will be "claim/01JMAAFZRNSRTQ0EBSD70A8E1H" const path = parts[1]; // This will be "claim/01JMAAFZRNSRTQ0EBSD70A8E1H"
console.log("[Capacitor Deep Link] Parsed path:", path); console.log("[Capacitor Deep Link] Parsed path:", path);
// Map parameterized routes // Map parameterized routes
const paramRoutes = { const paramRoutes = {
'claim': /^claim\/(.+)$/, // Updated pattern without leading slash claim: /^claim\/(.+)$/, // Updated pattern without leading slash
}; };
// Check if path matches any parameterized route // Check if path matches any parameterized route
for (const [routeName, pattern] of Object.entries(paramRoutes)) { for (const [routeName, pattern] of Object.entries(paramRoutes)) {
const match = path.match(pattern); const match = path.match(pattern);
if (match) { if (match) {
console.log(`[Capacitor Deep Link] Matched route: ${routeName}, param: ${match[1]}`); console.log(
await router.replace({ `[Capacitor Deep Link] Matched route: ${routeName}, param: ${match[1]}`,
);
await router.replace({
name: routeName, name: routeName,
params: { id: match[1] } params: { id: match[1] },
}); });
return; return;
} }
} }
await router.replace('/' + path); await router.replace("/" + path);
} catch (error) { } catch (error) {
console.error("[Capacitor Deep Link] Error:", error); console.error("[Capacitor Deep Link] Error:", error);
handleApiError(error, 'deep-link'); handleApiError(error, "deep-link");
} }
}; };
@ -68,4 +65,4 @@ App.addListener("appUrlOpen", handleDeepLink);
console.log("[Capacitor] Mounting app"); console.log("[Capacitor] Mounting app");
app.mount("#app"); app.mount("#app");
console.log("[Capacitor] App mounted"); console.log("[Capacitor] App mounted");

21
src/main.common.ts

@ -15,16 +15,16 @@ function setupGlobalErrorHandler(app: VueApp) {
app.config.errorHandler = ( app.config.errorHandler = (
err: unknown, err: unknown,
instance: ComponentPublicInstance | null, instance: ComponentPublicInstance | null,
info: string info: string,
) => { ) => {
console.error("[App Error] Global Error Handler:", { console.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.",
); );
}; };
} }
@ -33,24 +33,23 @@ function setupGlobalErrorHandler(app: VueApp) {
export function initializeApp() { export function initializeApp() {
console.log("[App Init] Starting app initialization"); console.log("[App Init] Starting app initialization");
console.log("[App Init] Platform:", process.env.VITE_PLATFORM); console.log("[App Init] Platform:", process.env.VITE_PLATFORM);
const app = createApp(App); const app = createApp(App);
console.log("[App Init] Vue app created"); console.log("[App Init] Vue app created");
app.component("fa", FontAwesomeIcon) app.component("font-awesome", FontAwesomeIcon).component("camera", Camera);
.component("camera", Camera);
console.log("[App Init] Components registered"); console.log("[App Init] Components registered");
const pinia = createPinia(); const pinia = createPinia();
app.use(pinia); app.use(pinia);
console.log("[App Init] Pinia store initialized"); console.log("[App Init] Pinia store initialized");
app.use(VueAxios, axios); app.use(VueAxios, axios);
console.log("[App Init] Axios initialized"); console.log("[App Init] Axios initialized");
app.use(router); app.use(router);
console.log("[App Init] Router initialized"); console.log("[App Init] Router initialized");
app.use(Notifications); app.use(Notifications);
console.log("[App Init] Notifications initialized"); console.log("[App Init] Notifications initialized");

2
src/main.electron.ts

@ -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");

2
src/main.pywebview.ts

@ -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");

16
src/services/api.ts

@ -1,5 +1,7 @@
export const handleApiError = (error: any, endpoint: string) => { import { AxiosError } from "axios";
if (process.env.VITE_PLATFORM === 'capacitor') {
export const handleApiError = (error: AxiosError, endpoint: string) => {
if (process.env.VITE_PLATFORM === "capacitor") {
console.error(`[Capacitor API Error] ${endpoint}:`, { console.error(`[Capacitor API Error] ${endpoint}:`, {
message: error.message, message: error.message,
status: error.response?.status, status: error.response?.status,
@ -7,16 +9,16 @@ export const handleApiError = (error: any, endpoint: string) => {
config: { config: {
url: error.config?.url, url: error.config?.url,
method: error.config?.method, method: error.config?.method,
headers: error.config?.headers headers: error.config?.headers,
} },
}); });
} }
// Specific handling for rate limits // Specific handling for rate limits
if (error.response?.status === 400) { if (error.response?.status === 400) {
console.warn(`[Rate Limit] ${endpoint}`); console.warn(`[Rate Limit] ${endpoint}`);
return null; return null;
} }
throw error; throw error;
}; };

71
src/services/plan.ts

@ -1,71 +1,80 @@
import axios from 'axios'; import axios from "axios";
interface PlanResponse { interface PlanResponse {
data?: any; data?: unknown;
status?: number; status?: number;
error?: string; error?: string;
} }
export const loadPlanWithRetry = async (handle: string, retries = 3): Promise<PlanResponse> => { export const loadPlanWithRetry = async (
handle: string,
retries = 3,
): Promise<PlanResponse> => {
try { try {
console.log(`[Plan Service] Loading plan ${handle}, attempt 1/${retries}`); console.log(`[Plan Service] Loading plan ${handle}, attempt 1/${retries}`);
console.log(`[Plan Service] Context: Deep link handle=${handle}, isClaimFlow=${handle.includes('claim')}`); console.log(
`[Plan Service] Context: Deep link handle=${handle}, isClaimFlow=${handle.includes("claim")}`,
);
// Different endpoint if this is a claim flow // Different endpoint if this is a claim flow
const response = await loadPlan(handle); const response = await loadPlan(handle);
console.log(`[Plan Service] Plan ${handle} loaded successfully:`, { console.log(`[Plan Service] Plan ${handle} loaded successfully:`, {
status: response?.status, status: response?.status,
headers: response?.headers, headers: response?.headers,
data: response?.data data: response?.data,
}); });
return response; return response;
} catch (error: any) { } catch (error: unknown) {
console.error(`[Plan Service] Error loading plan ${handle}:`, { console.error(`[Plan Service] Error loading plan ${handle}:`, {
message: error.message, message: (error as Error).message,
status: error.response?.status, status: (error as { response?: { status?: number } })?.response?.status,
statusText: error.response?.statusText, statusText: (error as { response?: { statusText?: string } })?.response
data: error.response?.data, ?.statusText,
headers: error.response?.headers, data: (error as { response?: { data?: unknown } })?.response?.data,
headers: (error as { response?: { headers?: unknown } })?.response
?.headers,
config: { config: {
url: error.config?.url, url: (error as { config?: { url?: string } })?.config?.url,
method: error.config?.method, method: (error as { config?: { method?: string } })?.config?.method,
baseURL: error.config?.baseURL, baseURL: (error as { config?: { baseURL?: string } })?.config?.baseURL,
headers: error.config?.headers headers: (error as { config?: { headers?: unknown } })?.config?.headers,
} },
}); });
if (retries > 1) { if (retries > 1) {
console.log(`[Plan Service] Retrying plan ${handle}, ${retries-1} attempts remaining`); console.log(
await new Promise(resolve => setTimeout(resolve, 1000)); `[Plan Service] Retrying plan ${handle}, ${retries - 1} attempts remaining`,
);
await new Promise((resolve) => setTimeout(resolve, 1000));
return loadPlanWithRetry(handle, retries - 1); return loadPlanWithRetry(handle, retries - 1);
} }
return { return {
error: `Failed to load plan ${handle} after ${4-retries} attempts: ${error.message}`, error: `Failed to load plan ${handle} after ${4 - retries} attempts: ${(error as Error).message}`,
status: error.response?.status status: (error as { response?: { status?: number } })?.response?.status,
}; };
} }
}; };
export const loadPlan = async (handle: string): Promise<PlanResponse> => { export const loadPlan = async (handle: string): Promise<PlanResponse> => {
console.log(`[Plan Service] Making API request for plan ${handle}`); console.log(`[Plan Service] Making API request for plan ${handle}`);
const endpoint = handle.includes('claim') const endpoint = handle.includes("claim")
? `/api/claims/${handle}` ? `/api/claims/${handle}`
: `/api/plans/${handle}`; : `/api/plans/${handle}`;
console.log(`[Plan Service] Using endpoint: ${endpoint}`); console.log(`[Plan Service] Using endpoint: ${endpoint}`);
try { try {
const response = await axios.get(endpoint); const response = await axios.get(endpoint);
return response; return response;
} catch (error: any) { } catch (error: unknown) {
console.error(`[Plan Service] API request failed for ${handle}:`, { console.error(`[Plan Service] API request failed for ${handle}:`, {
endpoint, endpoint,
error: error.message, error: (error as Error).message,
response: error.response?.data response: (error as { response?: { data?: unknown } })?.response?.data,
}); });
throw error; throw error;
} }
}; };

2
src/views/AccountViewView.vue

@ -895,7 +895,7 @@ import { AxiosError } from "axios";
import { Buffer } from "buffer/"; import { Buffer } from "buffer/";
import Dexie from "dexie"; import Dexie from "dexie";
import "dexie-export-import"; import "dexie-export-import";
import { ImportProgress } from "dexie-export-import/dist/import"; import { importDB, ImportProgress } from "dexie-export-import";
import { LeafletMouseEvent } from "leaflet"; import { LeafletMouseEvent } from "leaflet";
import * as R from "ramda"; import * as R from "ramda";
import { IIdentifier } from "@veramo/core"; import { IIdentifier } from "@veramo/core";

5
src/views/NewEditProjectView.vue

@ -229,9 +229,7 @@
import "leaflet/dist/leaflet.css"; import "leaflet/dist/leaflet.css";
import { AxiosError, AxiosRequestHeaders } from "axios"; import { AxiosError, AxiosRequestHeaders } from "axios";
import { DateTime } from "luxon"; import { DateTime } from "luxon";
import { finalizeEvent, serializeEvent } from "nostr-tools"; import { finalizeEvent } from "nostr-tools";
// these core imports could also be included as "import type ..."
import { EventTemplate, UnsignedEvent, VerifiedEvent } from "nostr-tools/core";
import * as nip06 from "nostr-tools/nip06"; import * as nip06 from "nostr-tools/nip06";
import { Component, Vue } from "vue-facing-decorator"; import { Component, Vue } from "vue-facing-decorator";
import { LMap, LMarker, LTileLayer } from "@vue-leaflet/vue-leaflet"; import { LMap, LMarker, LTileLayer } from "@vue-leaflet/vue-leaflet";
@ -254,6 +252,7 @@ import {
retrieveAccountCount, retrieveAccountCount,
retrieveFullyDecryptedAccount, retrieveFullyDecryptedAccount,
} from "../libs/util"; } from "../libs/util";
import { EventTemplate, UnsignedEvent, VerifiedEvent } from "nostr-tools/core";
@Component({ @Component({
components: { ImageMethodDialog, LMap, LMarker, LTileLayer, QuickNav }, components: { ImageMethodDialog, LMap, LMarker, LTileLayer, QuickNav },

18
vite.config.common.mts

@ -34,7 +34,10 @@ export async function createBuildConfig(mode: string) {
build: { build: {
outDir: isElectron ? "dist-electron" : "dist", outDir: isElectron ? "dist-electron" : "dist",
assetsDir: 'assets', assetsDir: 'assets',
chunkSizeWarningLimit: 1000 chunkSizeWarningLimit: 1000,
rollupOptions: {
external: isCapacitor ? ['@capacitor/app'] : []
}
}, },
define: { define: {
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV), 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
@ -43,9 +46,20 @@ export async function createBuildConfig(mode: string) {
__dirname: isElectron ? JSON.stringify(process.cwd()) : '""', __dirname: isElectron ? JSON.stringify(process.cwd()) : '""',
}, },
resolve: { resolve: {
alias: appConfig.aliasConfig alias: {
...appConfig.aliasConfig,
'nostr-tools/nip06': mode === 'development'
? 'nostr-tools/nip06'
: path.resolve(__dirname, 'node_modules/nostr-tools/nip06'),
'nostr-tools/core': mode === 'development'
? 'nostr-tools'
: path.resolve(__dirname, 'node_modules/nostr-tools'),
'nostr-tools': path.resolve(__dirname, 'node_modules/nostr-tools'),
'dexie-export-import': path.resolve(__dirname, 'node_modules/dexie-export-import')
}
}, },
optimizeDeps: { optimizeDeps: {
include: ['nostr-tools', 'nostr-tools/nip06', 'nostr-tools/core', 'dexie-export-import'],
exclude: isElectron ? [ exclude: isElectron ? [
'register-service-worker', 'register-service-worker',
'workbox-window', 'workbox-window',

4
vite.config.dev.mts

@ -0,0 +1,4 @@
import { defineConfig } from "vite";
import { createBuildConfig } from "./vite.config.common.mts";
export default defineConfig(async () => createBuildConfig('development'));
Loading…
Cancel
Save