forked from trent_larson/crowd-funder-for-time-pwa
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.
This commit is contained in:
1055
package-lock.json
generated
1055
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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",
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { initializeApp } from "./main.common";
|
import { initializeApp } from "./main.common";
|
||||||
|
|
||||||
const app = initializeApp();
|
const app = initializeApp();
|
||||||
app.mount("#app");
|
app.mount("#app");
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { initializeApp } from "./main.common";
|
import { initializeApp } from "./main.common";
|
||||||
|
|
||||||
const app = initializeApp();
|
const app = initializeApp();
|
||||||
app.mount("#app");
|
app.mount("#app");
|
||||||
|
|||||||
@@ -1,5 +1,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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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 },
|
||||||
|
|||||||
@@ -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
Normal file
4
vite.config.dev.mts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import { defineConfig } from "vite";
|
||||||
|
import { createBuildConfig } from "./vite.config.common.mts";
|
||||||
|
|
||||||
|
export default defineConfig(async () => createBuildConfig('development'));
|
||||||
Reference in New Issue
Block a user