Browse Source

Merge branch 'build-improvement' into performance-optimizations-testing

pull/158/head
Matthew Raymer 3 weeks ago
parent
commit
09bf7db536
  1. 8
      .cursor/rules/version_control.mdc
  2. 19
      src/components/MembersList.vue
  3. 3
      src/constants/app.ts
  4. 10
      src/interfaces/common.ts
  5. 3
      src/libs/endorserServer.ts
  6. 2
      src/services/AbsurdSqlDatabaseService.ts
  7. 5
      src/services/platforms/CapacitorPlatformService.ts
  8. 45
      src/utils/PlatformServiceMixin.ts
  9. 16
      src/views/ContactImportView.vue
  10. 83
      src/views/HomeView.vue
  11. 30
      src/views/IdentitySwitcherView.vue
  12. 7
      src/views/ImportDerivedAccountView.vue

8
.cursor/rules/version_control.mdc

@ -1,7 +1,13 @@
--- ---
alwaysApply: true alwaysApply: true
--- ---
do not add or commit for the user; let him control that process # Rules for peaceful co-existence with developers
do not add or commit for the user; let him control that process
the content of commit messages should be from the files awaiting staging
and those which have been staged. use the differences in those files
to inform the content of the commit message
always preview changes and commit message to use and allow me to copy and paste always preview changes and commit message to use and allow me to copy and paste
✅ Preferred Commit Message Format ✅ Preferred Commit Message Format

19
src/components/MembersList.vue

@ -159,25 +159,6 @@
</template> </template>
<script lang="ts"> <script lang="ts">
/* TODO: Human Testing Required - PlatformServiceMixin Migration */
// Priority: High | Migrated: 2025-07-06 | Author: Matthew Raymer
//
// TESTING NEEDED: Component migrated from legacy logConsoleAndDb to PlatformServiceMixin
// but requires human validation due to meeting component accessibility limitations.
//
// Test Scenarios Required:
// 1. Load members list with valid meeting password
// 2. Test member admission toggle (organizer role)
// 3. Test adding member as contact
// 4. Test error scenarios: network failure, invalid password, server errors
// 5. Verify error logging appears in console and database
// 6. Cross-platform testing: web, mobile, desktop
//
// Reference: docs/migration-testing/migration-checklist-MembersList.md
// Migration Details: Replaced 3 logConsoleAndDb() calls with this.$logAndConsole()
// Validation: Passes lint checks and TypeScript compilation
// Navigation: Contacts Chair Icon Start/Join Meeting Members List
import { Component, Vue, Prop, Emit } from "vue-facing-decorator"; import { Component, Vue, Prop, Emit } from "vue-facing-decorator";
import { import {

3
src/constants/app.ts

@ -19,7 +19,8 @@ export enum AppString {
PROD_PARTNER_API_SERVER = "https://partner-api.endorser.ch", PROD_PARTNER_API_SERVER = "https://partner-api.endorser.ch",
TEST_PARTNER_API_SERVER = "https://test-partner-api.endorser.ch", TEST_PARTNER_API_SERVER = "https://test-partner-api.endorser.ch",
LOCAL_PARTNER_API_SERVER = "http://127.0.0.1:3002", // eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values
LOCAL_PARTNER_API_SERVER = "http://127.0.0.1:3000",
PROD_PUSH_SERVER = "https://timesafari.app", PROD_PUSH_SERVER = "https://timesafari.app",
TEST1_PUSH_SERVER = "https://test.timesafari.app", TEST1_PUSH_SERVER = "https://test.timesafari.app",

10
src/interfaces/common.ts

@ -1,10 +1,7 @@
// similar to VerifiableCredentialSubject... maybe rename this // similar to VerifiableCredentialSubject... maybe rename this
export interface GenericVerifiableCredential { export interface GenericVerifiableCredential {
"@context"?: string; "@context"?: string;
"@type": string; "@type"?: string;
name?: string;
description?: string;
agent?: string | { identifier: string };
[key: string]: unknown; [key: string]: unknown;
} }
@ -47,7 +44,7 @@ export interface KeyMetaWithPrivate extends KeyMeta {
} }
export interface QuantitativeValue extends GenericVerifiableCredential { export interface QuantitativeValue extends GenericVerifiableCredential {
"@type": "QuantitativeValue"; "@type"?: "QuantitativeValue";
"@context"?: string; "@context"?: string;
amountOfThisGood: number; amountOfThisGood: number;
unitCode: string; unitCode: string;
@ -97,8 +94,7 @@ export interface ClaimObject {
export interface VerifiableCredentialClaim { export interface VerifiableCredentialClaim {
"@context"?: string; "@context"?: string;
"@type": string; "@type"?: string;
type: string[];
credentialSubject: ClaimObject; credentialSubject: ClaimObject;
[key: string]: unknown; [key: string]: unknown;
} }

3
src/libs/endorserServer.ts

@ -697,7 +697,6 @@ export function hydrateGive(
if (amount && !isNaN(amount)) { if (amount && !isNaN(amount)) {
const quantitativeValue: QuantitativeValue = { const quantitativeValue: QuantitativeValue = {
"@type": "QuantitativeValue",
amountOfThisGood: amount, amountOfThisGood: amount,
unitCode: unitCode || "HUR", unitCode: unitCode || "HUR",
}; };
@ -1342,7 +1341,6 @@ export async function createEndorserJwtVcFromClaim(
vc: { vc: {
"@context": "https://www.w3.org/2018/credentials/v1", "@context": "https://www.w3.org/2018/credentials/v1",
"@type": "VerifiableCredential", "@type": "VerifiableCredential",
type: ["VerifiableCredential"],
credentialSubject: claim, credentialSubject: claim,
}, },
}; };
@ -1380,7 +1378,6 @@ export async function createInviteJwt(
vc: { vc: {
"@context": "https://www.w3.org/2018/credentials/v1", "@context": "https://www.w3.org/2018/credentials/v1",
"@type": "VerifiableCredential", "@type": "VerifiableCredential",
type: ["VerifiableCredential"],
credentialSubject: vcClaim as unknown as ClaimObject, // Type assertion needed due to object being string credentialSubject: vcClaim as unknown as ClaimObject, // Type assertion needed due to object being string
}, },
}; };

2
src/services/AbsurdSqlDatabaseService.ts

@ -134,7 +134,7 @@ class AbsurdSqlDatabaseService implements DatabaseService {
// An error is thrown without this pragma: "File has invalid page size. (the first block of a new file must be written first)" // An error is thrown without this pragma: "File has invalid page size. (the first block of a new file must be written first)"
await this.db.exec(`PRAGMA journal_mode=MEMORY;`); await this.db.exec(`PRAGMA journal_mode=MEMORY;`);
// Create wrapper functions that match the expected signatures // Create wrapper functions that match the expected signatures
const sqlExec = async (sql: string, params?: unknown[]): Promise<void> => { const sqlExec = async (sql: string, params?: unknown[]): Promise<void> => {
await this.db!.run(sql, params); await this.db!.run(sql, params);

5
src/services/platforms/CapacitorPlatformService.ts

@ -492,10 +492,7 @@ export class CapacitorPlatformService implements PlatformService {
* @param params - Optional parameters for prepared statements * @param params - Optional parameters for prepared statements
* @returns Promise resolving to execution results * @returns Promise resolving to execution results
*/ */
const sqlExec = async ( const sqlExec = async (sql: string, params?: unknown[]): Promise<void> => {
sql: string,
params?: unknown[],
): Promise<void> => {
logger.debug(`🔧 [CapacitorMigration] Executing SQL:`, sql); logger.debug(`🔧 [CapacitorMigration] Executing SQL:`, sql);
if (params && params.length > 0) { if (params && params.length > 0) {

45
src/utils/PlatformServiceMixin.ts

@ -459,13 +459,10 @@ export const PlatformServiceMixin = {
return settings; return settings;
} catch (error) { } catch (error) {
logger.error( logger.error(`[Settings Trace] ❌ Failed to get settings:`, {
`[${(this as unknown as VueComponentWithMixin).$options.name}] Failed to get settings:`, key,
{ error,
key, });
error,
},
);
return fallback; return fallback;
} }
}, },
@ -533,14 +530,11 @@ export const PlatformServiceMixin = {
return mergedSettings; return mergedSettings;
} catch (error) { } catch (error) {
logger.error( logger.error(`[Settings Trace] ❌ Failed to get merged settings:`, {
`[${(this as unknown as VueComponentWithMixin).$options.name}] Failed to get merged settings:`, defaultKey,
{ accountDid,
defaultKey, error,
accountDid, });
error,
},
);
return defaultFallback; return defaultFallback;
} }
}, },
@ -748,25 +742,20 @@ export const PlatformServiceMixin = {
); );
mergedSettings.apiServer = DEFAULT_ENDORSER_API_SERVER; mergedSettings.apiServer = DEFAULT_ENDORSER_API_SERVER;
logger.debug(
`[Electron Settings] Forced API server to: ${DEFAULT_ENDORSER_API_SERVER}`,
);
} }
// Merge with any provided defaults (these take highest precedence) // Merge with any provided defaults (these take highest precedence)
const finalSettings = { ...mergedSettings, ...defaults }; // Filter out undefined and empty string values to prevent overriding real settings
const filteredDefaults = Object.fromEntries(
console.log( Object.entries(defaults).filter(
"[PlatformServiceMixin] $accountSettings", ([_, value]) => value !== undefined && value !== "",
JSON.stringify(finalSettings, null, 2), ),
); );
const finalSettings = { ...mergedSettings, ...filteredDefaults };
return finalSettings; return finalSettings;
} catch (error) { } catch (error) {
logger.error( logger.error("[Settings Trace] ❌ Error in $accountSettings:", error);
"[PlatformServiceMixin] Error in $accountSettings:",
error,
);
// Fallback to defaults on error // Fallback to defaults on error
return defaults; return defaults;

16
src/views/ContactImportView.vue

@ -253,22 +253,6 @@ import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
* *
* @component * @component
*/ */
// TODO: Testing Required - Database Operations + Logging Migration to PlatformServiceMixin
// Priority: Medium | Migrated: 2025-07-06 | Author: Matthew Raymer
//
//
// TESTING NEEDED: Contact import functionality
// 1. Test contact import via URL: /contact-import?contacts=[{"did":"did:example:123","name":"Alice"}]
// 2. Test JWT import via URL path: /contact-import/[JWT_TOKEN]
// 3. Test manual JWT input via textarea
// 4. Test duplicate contact detection and field comparison
// 5. Test error scenarios: invalid JWT, malformed data, network issues
// 6. Verify error logging appears correctly
//
// Test URLs:
// /contact-import (manual input)
// /contact-import?contacts=[{"did":"did:test:123","name":"Test User"}]
@Component({ @Component({
components: { EntityIcon, OfferDialog, QuickNav }, components: { EntityIcon, OfferDialog, QuickNav },
mixins: [PlatformServiceMixin], mixins: [PlatformServiceMixin],

83
src/views/HomeView.vue

@ -310,7 +310,6 @@ import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify"; import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
import { import {
NOTIFY_CONTACT_LOADING_ISSUE, NOTIFY_CONTACT_LOADING_ISSUE,
NOTIFY_FEED_LOADING_ISSUE,
NOTIFY_CONFIRMATION_ERROR, NOTIFY_CONFIRMATION_ERROR,
} from "@/constants/notifications"; } from "@/constants/notifications";
import * as Package from "../../package.json"; import * as Package from "../../package.json";
@ -486,6 +485,10 @@ export default class HomeView extends Vue {
if (newDid !== oldDid) { if (newDid !== oldDid) {
// Re-initialize identity with new settings (loads settings internally) // Re-initialize identity with new settings (loads settings internally)
await this.initializeIdentity(); await this.initializeIdentity();
} else {
logger.info(
"[HomeView Settings Trace] 📍 DID unchanged, skipping re-initialization",
);
} }
} }
@ -528,11 +531,7 @@ export default class HomeView extends Vue {
// Load settings with better error context using ultra-concise mixin // Load settings with better error context using ultra-concise mixin
let settings; let settings;
try { try {
settings = await this.$settings({ settings = await this.$accountSettings();
apiServer: "",
activeDid: "",
isRegistered: false,
});
} catch (error) { } catch (error) {
this.$logAndConsole( this.$logAndConsole(
`[HomeView] Failed to retrieve settings: ${error}`, `[HomeView] Failed to retrieve settings: ${error}`,
@ -600,65 +599,21 @@ export default class HomeView extends Vue {
// Ultra-concise settings update with automatic cache invalidation! // Ultra-concise settings update with automatic cache invalidation!
await this.$saveMySettings({ isRegistered: true }); await this.$saveMySettings({ isRegistered: true });
this.isRegistered = true; this.isRegistered = true;
// Force Vue to re-render the template
await this.$nextTick();
} }
} catch (error) { } catch (error) {
// Consolidate logging: Only log unexpected errors, not expected 400s logger.warn(
const axiosError = error as any; "[HomeView Settings Trace] ⚠️ Registration check failed",
if (axiosError?.response?.status !== 400) { {
this.$logAndConsole( error: error instanceof Error ? error.message : String(error),
`[HomeView] Registration check failed: ${error}`, },
true,
);
}
// Continue as unregistered - this is expected for new users
}
}
// Initialize feed and offers
try {
// Start feed update in background
this.updateAllFeed().catch((error) => {
this.$logAndConsole(
`[HomeView] Background feed update failed: ${error}`,
true,
); );
});
// Load new offers if we have an active DID
if (this.activeDid) {
const [offersToUser, offersToProjects] = await Promise.all([
getNewOffersToUser(
this.axios,
this.apiServer,
this.activeDid,
this.lastAckedOfferToUserJwtId,
),
getNewOffersToUserProjects(
this.axios,
this.apiServer,
this.activeDid,
this.lastAckedOfferToUserProjectsJwtId,
),
]);
this.numNewOffersToUser = offersToUser.data.length;
this.newOffersToUserHitLimit = offersToUser.hitLimit;
this.numNewOffersToUserProjects = offersToProjects.data.length;
this.newOffersToUserProjectsHitLimit = offersToProjects.hitLimit;
} }
} catch (error) {
this.$logAndConsole(
`[HomeView] Failed to initialize feed/offers: ${error}`,
true,
);
// Don't throw - we can continue with empty feed
this.notify.warning(NOTIFY_FEED_LOADING_ISSUE.message, TIMEOUTS.LONG);
} }
} catch (error) { } catch (err: unknown) {
this.handleError(error); logger.error("[HomeView Settings Trace] ❌ initializeIdentity() failed", {
throw error; // Re-throw to be caught by mounted() error: err instanceof Error ? err.message : String(err),
});
throw err;
} }
} }
@ -683,10 +638,8 @@ export default class HomeView extends Vue {
} }
/** /**
* Loads user settings from storage using ultra-concise mixin utilities * Loads user settings from database using ultra-concise mixin
* Sets component state for: * Used for displaying settings in feed and actions
* - API server, Active DID, Feed filters and view settings
* - Registration status, Notification acknowledgments
* *
* @internal * @internal
* Called by mounted() and reloadFeedOnChange() * Called by mounted() and reloadFeedOnChange()
@ -816,7 +769,7 @@ export default class HomeView extends Vue {
* Called by mounted() * Called by mounted()
*/ */
private async checkOnboarding() { private async checkOnboarding() {
const settings = await this.$settings(); const settings = await this.$accountSettings();
if (!settings.finishedOnboarding) { if (!settings.finishedOnboarding) {
(this.$refs.onboardingDialog as OnboardingDialog).open(OnboardPage.Home); (this.$refs.onboardingDialog as OnboardingDialog).open(OnboardPage.Home);
} }

30
src/views/IdentitySwitcherView.vue

@ -214,7 +214,10 @@ export default class IdentitySwitcherView extends Vue {
} }
} catch (err) { } catch (err) {
this.notify.error(NOTIFY_ERROR_LOADING_ACCOUNTS.message, TIMEOUTS.LONG); this.notify.error(NOTIFY_ERROR_LOADING_ACCOUNTS.message, TIMEOUTS.LONG);
logger.error("Telling user to clear cache at page create because:", err); logger.error(
"[IdentitySwitcher Settings Trace] ❌ Error loading accounts:",
err,
);
} }
} }
@ -225,12 +228,35 @@ export default class IdentitySwitcherView extends Vue {
// 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) {
try { try {
await this.$accountSettings(did); const newSettings = await this.$accountSettings(did);
logger.info(
"[IdentitySwitcher Settings Trace] ✅ New account settings loaded",
{
did,
settingsKeys: Object.keys(newSettings).filter(
(k) => (newSettings as any)[k] !== undefined,
),
},
);
} catch (error) { } catch (error) {
logger.warn(
"[IdentitySwitcher Settings Trace] ⚠️ Error loading new account settings",
{
did,
error: error instanceof Error ? error.message : String(error),
},
);
// Handle error silently - user settings will be loaded when needed // Handle error silently - user settings will be loaded when needed
} }
} }
logger.info(
"[IdentitySwitcher Settings Trace] 🔄 Navigating to home to trigger watcher",
{
newDid: did,
},
);
// Navigate to home page to trigger the watcher // Navigate to home page to trigger the watcher
this.$router.push({ name: "home" }); this.$router.push({ name: "home" });
} }

7
src/views/ImportDerivedAccountView.vue

@ -166,10 +166,8 @@ export default class ImportAccountView extends Vue {
] as string; ] as string;
const newDerivPath = nextDerivationPath(maxDerivPath); const newDerivPath = nextDerivationPath(maxDerivPath);
const mne = selectedArray[0].mnemonic as string; const mne = selectedArray[0].mnemonic as string;
const [address, privateHex, publicHex] = deriveAddress(mne, newDerivPath); const [address, privateHex, publicHex] = deriveAddress(mne, newDerivPath);
const newId = newIdentifier(address, publicHex, privateHex, newDerivPath); const newId = newIdentifier(address, publicHex, privateHex, newDerivPath);
try { try {
@ -187,7 +185,10 @@ export default class ImportAccountView extends Vue {
); );
this.$router.push({ name: "account" }); this.$router.push({ name: "account" });
} catch (err) { } catch (err) {
logger.error("Error saving mnemonic & updating settings:", err); logger.error(
"[ImportDerived Settings Trace] ❌ Error saving mnemonic & updating settings:",
err,
);
this.notify.error(NOTIFY_ACCOUNT_DERIVATION_ERROR.message, TIMEOUTS.LONG); this.notify.error(NOTIFY_ACCOUNT_DERIVATION_ERROR.message, TIMEOUTS.LONG);
} }
} }

Loading…
Cancel
Save