You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

258 lines
8.1 KiB

<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("Telling user to clear cache at page create because:", 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 {
await this.$accountSettings(did);
} catch (error) {
// Handle error silently - user settings will be loaded when needed
}
}
// 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>