Files
crowd-funder-from-jason/src/views/IdentitySwitcherView.vue
Matthew Raymer 9386b2e96f refactor(services): inline ProfileService logic into AccountViewView
Removes over-engineered ProfileService and ServiceInitializationManager
classes that were only used in one place. Inlines all profile logic
directly into AccountViewView.vue to reduce complexity and improve
maintainability.

- Deletes ProfileService.ts (325 lines)
- Deletes ServiceInitializationManager.ts (207 lines)
- Inlines ProfileData interface and methods into AccountViewView
- Maintains all existing functionality while reducing code footprint

perf(logging): convert excessive info logs to debug level

Reduces console noise by converting high-frequency, low-value logging
from info to debug level across navigation, API calls, and component
lifecycle operations. Improves performance and reduces log verbosity
for normal application flow.

- Router navigation guards: info → debug
- Plan loading operations: info → debug
- User registration checks: info → debug
- Image server rate limits: info → debug
- Component lifecycle events: info → debug
- Settings loading operations: info → debug

Maintains warn/error levels for actual issues while reducing noise
from expected application behavior.
2025-08-26 07:23:24 +00:00

287 lines
8.9 KiB
Vue

<template>
<QuickNav selected="Profile"></QuickNav>
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
<!-- Breadcrumb -->
<div id="ViewBreadcrumb" class="mb-8">
<h1 class="text-lg text-center font-light relative px-7">
<!-- Cancel -->
<router-link
:to="{ name: 'account' }"
class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
><font-awesome icon="chevron-left" class="fa-fw"></font-awesome>
</router-link>
Switch Identity
</h1>
</div>
<!-- Identity List -->
<!-- Current Identity - Display First! -->
<div
v-if="hasCorruptedIdentity"
class="block bg-slate-100 rounded-md flex items-center px-4 py-3 mb-4"
>
<font-awesome
icon="circle-check"
class="fa-fw text-red-600 text-xl mr-3"
></font-awesome>
<div class="text-sm text-slate-500">
<div class="overflow-hidden truncate">
<b>ID:</b> <code>{{ activeDid }}</code>
</div>
<b
>There is a data corruption error: this identity is selected but it is
not in storage. You cannot send any more claims with this identity
until you import the seed again. This may require reinstalling the
app; if you know how, you can also clear out the TimeSafariAccounts
IndexedDB. Be sure to back up all your Settings & Contacts first.</b
>
</div>
</div>
<!-- Other Identity/ies -->
<ul class="mb-4">
<li v-for="ident in otherIdentities" :key="ident.did">
<div class="flex items-center justify-between mb-2">
<div
:class="identityListItemClasses"
@click="switchAccount(ident.did)"
>
<font-awesome
v-if="ident.did === activeDid"
icon="circle-check"
class="fa-fw text-blue-600 text-xl mr-3"
/>
<font-awesome
v-else
icon="circle"
class="fa-fw text-slate-400 text-xl mr-3"
/>
<span class="flex-grow overflow-hidden">
<div class="text-sm text-slate-500 truncate">
<b>ID:</b> <code>{{ ident.did }}</code>
</div>
</span>
</div>
<div>
<font-awesome
v-if="ident.did === activeDid"
icon="trash-can"
class="text-slate-400 text-xl ml-2 mr-2 cursor-pointer"
@click="notifyCannotDelete()"
/>
<font-awesome
v-else
icon="trash-can"
class="text-red-600 text-xl ml-2 mr-2 cursor-pointer"
@click="deleteAccount(ident.id)"
/>
</div>
</div>
</li>
</ul>
<!-- Actions -->
<!-- id used by puppeteer test script -->
<router-link
id="start-link"
:to="{ name: 'start' }"
:class="primaryButtonClasses"
>
Add Another Identity&hellip;
</router-link>
<a
href="#"
:class="secondaryButtonClasses"
@click="switchAccount(undefined)"
>
No Identity
</a>
</section>
</template>
<script lang="ts">
import { Component, Vue } from "vue-facing-decorator";
import { Router } from "vue-router";
import QuickNav from "../components/QuickNav.vue";
import { NotificationIface } from "../constants/app";
import { retrieveAllAccountsMetadata } from "../libs/util";
import { logger } from "../utils/logger";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
import {
NOTIFY_ERROR_LOADING_ACCOUNTS,
NOTIFY_CANNOT_DELETE_ACTIVE_IDENTITY,
NOTIFY_DELETE_IDENTITY_CONFIRM,
} from "@/constants/notifications";
import { Account } from "@/db/tables/accounts";
@Component({
components: { QuickNav },
mixins: [PlatformServiceMixin],
})
export default class IdentitySwitcherView extends Vue {
$notify!: (notification: NotificationIface, timeout?: number) => void;
$router!: Router;
notify!: ReturnType<typeof createNotifyHelpers>;
public activeDid = "";
public activeDidInIdentities = false;
public apiServer = "";
public apiServerInput = "";
public otherIdentities: Array<{ id: string; did: string }> = [];
/**
* Vue lifecycle hook - Initialize notification helpers
*/
mounted() {
this.notify = createNotifyHelpers(this.$notify);
}
// =================================================
// COMPUTED PROPERTIES - Template Logic Streamlining
// =================================================
/**
* CSS classes for primary action buttons (Add Another Identity)
* Reduces template complexity for gradient button styling
*/
get primaryButtonClasses(): string {
return "block text-center text-lg font-bold uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md mb-2";
}
/**
* CSS classes for secondary action buttons (No Identity)
* Reduces template complexity for gradient button styling
*/
get secondaryButtonClasses(): string {
return "block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md mb-8";
}
/**
* CSS classes for identity list items
* Reduces template complexity for repeated list item styling
*/
get identityListItemClasses(): string {
return "flex flex-grow items-center bg-slate-100 rounded-md px-4 py-3 mb-2 truncate cursor-pointer";
}
/**
* Detects if there's a data corruption issue with the active identity
* Shows warning when an identity is selected but not found in storage
*/
get hasCorruptedIdentity(): boolean {
return Boolean(this.activeDid && !this.activeDidInIdentities);
}
// =================================================
// HELPER METHODS - Template Logic Streamlining
// =================================================
/**
* Formats account data for display in the identity list
* Consolidates account processing logic from template
* @param account - Account object from database
* @returns Formatted account object for display
*/
formatAccountForDisplay(account: Account): { id: string; did: string } {
return {
id: (account.id ?? 0).toString(),
did: account.did,
};
}
// =================================================
// COMPONENT METHODS
// =================================================
async created() {
try {
const settings = await this.$accountSettings();
this.activeDid = settings.activeDid || "";
this.apiServer = settings.apiServer || "";
this.apiServerInput = settings.apiServer || "";
const accounts = await retrieveAllAccountsMetadata();
for (let n = 0; n < accounts.length; n++) {
const acct = accounts[n];
this.otherIdentities.push(this.formatAccountForDisplay(acct));
if (acct.did && this.activeDid === acct.did) {
this.activeDidInIdentities = true;
}
}
} catch (err) {
this.notify.error(NOTIFY_ERROR_LOADING_ACCOUNTS.message, TIMEOUTS.LONG);
logger.error(
"[IdentitySwitcher Settings Trace] ❌ Error loading accounts:",
err,
);
}
}
async switchAccount(did?: string) {
// Save the new active DID to master settings
await this.$saveSettings({ activeDid: did });
// Check if we need to load user-specific settings for the new DID
if (did) {
try {
const newSettings = await this.$accountSettings(did);
logger.debug(
"[IdentitySwitcher Settings Trace] ✅ New account settings loaded",
{
did,
settingsKeys: Object.keys(newSettings).filter(
(k) =>
k in newSettings &&
newSettings[k as keyof typeof newSettings] !== undefined,
),
},
);
} 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
}
}
logger.debug(
"[IdentitySwitcher Settings Trace] 🔄 Navigating to home to trigger watcher",
{
newDid: did,
},
);
// Navigate to home page to trigger the watcher
this.$router.push({ name: "home" });
}
async deleteAccount(id: string) {
this.notify.confirm(
NOTIFY_DELETE_IDENTITY_CONFIRM.text,
async () => {
await this.$exec(`DELETE FROM accounts WHERE id = ?`, [id]);
this.otherIdentities = this.otherIdentities.filter(
(ident) => ident.id !== id,
);
},
-1,
);
}
notifyCannotDelete() {
this.notify.warning(
NOTIFY_CANNOT_DELETE_ACTIVE_IDENTITY.message,
TIMEOUTS.SHORT,
);
}
}
</script>