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"
},
"scripts": {
"dev": "vite",
"dev": "vite --config vite.config.dev.mts",
"serve": "vite preview",
"build": "VITE_GIT_HASH=`git log -1 --pretty=format:%h` vite build",
"lint": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src",
@ -66,7 +66,7 @@
"cbor-x": "^1.5.9",
"class-transformer": "^0.5.1",
"dexie": "^3.2.7",
"dexie-export-import": "^4.1.1",
"dexie-export-import": "^4.1.4",
"did-jwt": "^7.4.7",
"did-resolver": "^4.1.0",
"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 router from "./router";
import { handleApiError } from "./services/api";
import { loadPlanWithRetry } from "./services/plan";
import { Capacitor } from '@capacitor/core';
console.log("[Capacitor] Starting initialization");
console.log("[Capacitor] Platform:", process.env.VITE_PLATFORM);
const app = initializeApp();
// Store initial deep link if app is not ready
let pendingDeepLink: string | null = null;
// Initialize API error handling
window.addEventListener('unhandledrejection', (event) => {
window.addEventListener("unhandledrejection", (event) => {
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 {
console.log("[Capacitor Deep Link] START Handler");
console.log("[Capacitor Deep Link] Received URL:", data.url);
// Wait for router to be ready
await router.isReady();
// Parse the custom URL scheme
const parts = data.url.split('://');
const parts = data.url.split("://");
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);
// Map parameterized routes
const paramRoutes = {
'claim': /^claim\/(.+)$/, // Updated pattern without leading slash
claim: /^claim\/(.+)$/, // Updated pattern without leading slash
};
// Check if path matches any parameterized route
for (const [routeName, pattern] of Object.entries(paramRoutes)) {
const match = path.match(pattern);
if (match) {
console.log(`[Capacitor Deep Link] Matched route: ${routeName}, param: ${match[1]}`);
await router.replace({
console.log(
`[Capacitor Deep Link] Matched route: ${routeName}, param: ${match[1]}`,
);
await router.replace({
name: routeName,
params: { id: match[1] }
params: { id: match[1] },
});
return;
}
}
await router.replace('/' + path);
await router.replace("/" + path);
} catch (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");
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 = (
err: unknown,
instance: ComponentPublicInstance | null,
info: string
info: string,
) => {
console.error("[App Error] Global Error Handler:", {
error: err,
info,
component: instance?.$options.name || 'unknown'
component: instance?.$options.name || "unknown",
});
alert(
(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() {
console.log("[App Init] Starting app initialization");
console.log("[App Init] Platform:", process.env.VITE_PLATFORM);
const app = createApp(App);
console.log("[App Init] Vue app created");
app.component("fa", FontAwesomeIcon)
.component("camera", Camera);
app.component("font-awesome", FontAwesomeIcon).component("camera", Camera);
console.log("[App Init] Components registered");
const pinia = createPinia();
app.use(pinia);
console.log("[App Init] Pinia store initialized");
app.use(VueAxios, axios);
console.log("[App Init] Axios initialized");
app.use(router);
console.log("[App Init] Router initialized");
app.use(Notifications);
console.log("[App Init] Notifications initialized");

2
src/main.electron.ts

@ -1,4 +1,4 @@
import { initializeApp } from "./main.common";
const app = initializeApp();
app.mount("#app");
app.mount("#app");

2
src/main.pywebview.ts

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

71
src/services/plan.ts

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

2
src/views/AccountViewView.vue

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

5
src/views/NewEditProjectView.vue

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

18
vite.config.common.mts

@ -34,7 +34,10 @@ export async function createBuildConfig(mode: string) {
build: {
outDir: isElectron ? "dist-electron" : "dist",
assetsDir: 'assets',
chunkSizeWarningLimit: 1000
chunkSizeWarningLimit: 1000,
rollupOptions: {
external: isCapacitor ? ['@capacitor/app'] : []
}
},
define: {
'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()) : '""',
},
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: {
include: ['nostr-tools', 'nostr-tools/nip06', 'nostr-tools/core', 'dexie-export-import'],
exclude: isElectron ? [
'register-service-worker',
'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