Add NIP-65 relay list display and improve identity UI

- Add NIP-65 relay list service to fetch kind 10002 events from relays
- Replace configurable relay page with read-only NIP-65 relay display
- Update identity page to show display name and username in same badge
- Use reglisse heading font for titles throughout the UI
- Navigate to You page after vault unlock instead of identities list
- Add autofocus to vault password input field
- Add profile metadata service for fetching kind 0 events
- Add readonly mode to relay-rw component

Files modified:
- package.json (version bump to 0.0.6)
- projects/common/src/lib/services/relay-list/relay-list.service.ts (new)
- projects/common/src/lib/services/profile-metadata/profile-metadata.service.ts (new)
- projects/common/src/lib/constants/fallback-relays.ts (new)
- projects/*/src/app/components/home/identity/* (UI improvements)
- projects/*/src/app/components/edit-identity/relays/* (NIP-65 display)
- projects/*/src/app/components/vault-login/* (autofocus, navigation)
- projects/common/src/lib/styles/* (heading fonts)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-17 15:21:57 +01:00
parent fe886d2101
commit 578f3e08ff
39 changed files with 1900 additions and 536 deletions

View File

@@ -1,7 +1,13 @@
import { Component, inject } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { Router } from '@angular/router';
import { ConfirmComponent, StartupService, StorageService } from '@common';
import {
ConfirmComponent,
NostrHelper,
ProfileMetadataService,
StartupService,
StorageService,
} from '@common';
import { getNewStorageServiceConfig } from '../../common/data/get-new-storage-service-config';
@Component({
@@ -17,6 +23,7 @@ export class VaultLoginComponent {
readonly #storage = inject(StorageService);
readonly #router = inject(Router);
readonly #startup = inject(StartupService);
readonly #profileMetadata = inject(ProfileMetadataService);
toggleType(element: HTMLInputElement) {
if (element.type === 'password') {
@@ -33,7 +40,11 @@ export class VaultLoginComponent {
try {
await this.#storage.unlockVault(this.loginPassword);
this.#router.navigateByUrl('/home/identities');
// Fetch profile metadata for all identities in the background
this.#fetchAllProfiles();
this.#router.navigateByUrl('/home/identity');
} catch (error) {
this.showInvalidPasswordAlert = true;
console.log(error);
@@ -43,6 +54,30 @@ export class VaultLoginComponent {
}
}
/**
* 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 {
await this.#storage.resetExtension();