Files
plebeian-signer/projects/chrome/src/app/components/vault-login/vault-login.component.ts
mleku 45b1fb58e9 Release v1.0.4 - Add logging system, lock button, and emoji navigation
- Comprehensive logging system with chrome.storage.session persistence
- NIP-07 action logging in background scripts with standalone functions
- Vault operation logging (unlock, lock, create, reset, import, export)
- Profile and bookmark operation logging
- Logs page with refresh functionality and category icons
- Lock button (🔒) in navigation bar to quickly lock vault
- Reduced nav bar size (40px height, 16px font) with emoji icons
- Reordered navigation: You, Permissions, Bookmarks, Logs, About, Lock
- Bookmarks functionality for saving frequently used Nostr apps
- Fixed lock/unlock flow by properly clearing in-memory session data

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-20 12:42:19 +01:00

117 lines
3.3 KiB
TypeScript

import { AfterViewInit, Component, ElementRef, inject, ViewChild } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { Router } from '@angular/router';
import {
ConfirmComponent,
DerivingModalComponent,
LoggerService,
NostrHelper,
ProfileMetadataService,
StartupService,
StorageService,
} from '@common';
import { getNewStorageServiceConfig } from '../../common/data/get-new-storage-service-config';
@Component({
selector: 'app-vault-login',
templateUrl: './vault-login.component.html',
styleUrl: './vault-login.component.scss',
imports: [FormsModule, ConfirmComponent, DerivingModalComponent],
})
export class VaultLoginComponent implements AfterViewInit {
@ViewChild('passwordInputElement') passwordInput!: ElementRef<HTMLInputElement>;
@ViewChild('derivingModal') derivingModal!: DerivingModalComponent;
loginPassword = '';
showInvalidPasswordAlert = false;
readonly #storage = inject(StorageService);
readonly #router = inject(Router);
readonly #startup = inject(StartupService);
readonly #profileMetadata = inject(ProfileMetadataService);
readonly #logger = inject(LoggerService);
ngAfterViewInit() {
this.passwordInput.nativeElement.focus();
}
toggleType(element: HTMLInputElement) {
if (element.type === 'password') {
element.type = 'text';
} else {
element.type = 'password';
}
}
async loginVault() {
console.log('[login] loginVault called');
if (!this.loginPassword) {
console.log('[login] No password, returning');
return;
}
console.log('[login] Showing deriving modal');
// Show deriving modal during key derivation (~3-6 seconds)
this.derivingModal.show('Unlocking vault');
try {
console.log('[login] Calling unlockVault...');
await this.#storage.unlockVault(this.loginPassword);
console.log('[login] unlockVault succeeded!');
} catch (error) {
console.error('[login] unlockVault FAILED:', error);
this.derivingModal.hide();
this.showInvalidPasswordAlert = true;
window.setTimeout(() => {
this.showInvalidPasswordAlert = false;
}, 2000);
return;
}
// Unlock succeeded - hide modal and navigate
console.log('[login] Hiding modal and navigating');
this.derivingModal.hide();
this.#logger.logVaultUnlock();
// Fetch profile metadata for all identities in the background
this.#fetchAllProfiles();
this.#router.navigateByUrl('/home/identity');
}
/**
* Fetch profile metadata for all identities (runs in background)
*/
async #fetchAllProfiles() {
try {
const identities =
this.#storage.getBrowserSessionHandler().browserSessionData?.identities ?? [];
if (identities.length === 0) {
return;
}
// Get all pubkeys from identities
const pubkeys = identities.map((identity) =>
NostrHelper.pubkeyFromPrivkey(identity.privkey)
);
// Fetch all profiles in parallel
await this.#profileMetadata.fetchProfiles(pubkeys);
} catch (error) {
console.error('Failed to fetch profiles:', error);
}
}
async onClickResetExtension() {
try {
this.#logger.logVaultReset();
await this.#storage.resetExtension();
this.#startup.startOver(getNewStorageServiceConfig());
} catch (error) {
console.log(error);
// TODO
}
}
}