identity.component.ts raw
1 import { Component, inject, OnInit } from '@angular/core';
2 import { Router } from '@angular/router';
3 import {
4 Identity_DECRYPTED,
5 LoggerService,
6 NavComponent,
7 NostrHelper,
8 ProfileMetadata,
9 ProfileMetadataService,
10 PubkeyComponent,
11 ToastComponent,
12 VisualNip05Pipe,
13 validateNip05,
14 } from '@common';
15
16 @Component({
17 selector: 'app-identity',
18 imports: [PubkeyComponent, VisualNip05Pipe, ToastComponent],
19 templateUrl: './identity.component.html',
20 styleUrl: './identity.component.scss',
21 })
22 export class IdentityComponent extends NavComponent implements OnInit {
23 selectedIdentity: Identity_DECRYPTED | undefined;
24 selectedIdentityNpub: string | undefined;
25 profile: ProfileMetadata | null = null;
26 nip05isValidated: boolean | undefined;
27 validating = false;
28 loading = true;
29
30 readonly #router = inject(Router);
31 readonly #profileMetadata = inject(ProfileMetadataService);
32 readonly #logger = inject(LoggerService);
33
34 ngOnInit(): void {
35 this.#loadData();
36 }
37
38 get displayName(): string | undefined {
39 return this.#profileMetadata.getDisplayName(this.profile);
40 }
41
42 get username(): string | undefined {
43 return this.#profileMetadata.getUsername(this.profile);
44 }
45
46 get avatarUrl(): string | undefined {
47 return this.profile?.picture;
48 }
49
50 get bannerUrl(): string | undefined {
51 return this.profile?.banner;
52 }
53
54 get aboutText(): string | undefined {
55 return this.profile?.about;
56 }
57
58 copyToClipboard(pubkey: string | undefined) {
59 if (!pubkey) {
60 return;
61 }
62 navigator.clipboard.writeText(pubkey);
63 }
64
65 onClickShowDetails() {
66 if (!this.selectedIdentity) {
67 return;
68 }
69
70 this.#router.navigateByUrl(
71 `/edit-identity/${this.selectedIdentity.id}/home`
72 );
73 }
74
75 onClickEditProfile() {
76 if (!this.selectedIdentity) {
77 return;
78 }
79 this.#router.navigateByUrl('/profile-edit');
80 }
81
82 async onClickLock() {
83 this.#logger.logVaultLock();
84 await this.storage.lockVault();
85 this.#router.navigateByUrl('/vault-login');
86 }
87
88 async #loadData() {
89 try {
90 const selectedIdentityId =
91 this.storage.getBrowserSessionHandler().browserSessionData
92 ?.selectedIdentityId ?? null;
93
94 const identity = this.storage
95 .getBrowserSessionHandler()
96 .browserSessionData?.identities.find(
97 (x) => x.id === selectedIdentityId
98 );
99
100 if (!identity) {
101 this.loading = false;
102 return;
103 }
104
105 this.selectedIdentity = identity;
106 const pubkey = NostrHelper.pubkeyFromPrivkey(identity.privkey);
107 this.selectedIdentityNpub = NostrHelper.pubkey2npub(pubkey);
108
109 // Initialize the profile metadata service (loads cache from storage)
110 await this.#profileMetadata.initialize();
111
112 // Check if we have cached profile data
113 const cachedProfile = this.#profileMetadata.getCachedProfile(pubkey);
114 if (cachedProfile) {
115 this.profile = cachedProfile;
116 this.loading = false;
117 // Validate NIP-05 if present (in background)
118 if (cachedProfile.nip05) {
119 this.#validateNip05(pubkey, cachedProfile.nip05);
120 }
121 return; // Use cached data, don't fetch again
122 }
123
124 // No cached data, fetch from relays
125 this.loading = true;
126 const fetchedProfile = await this.#profileMetadata.fetchProfile(pubkey);
127 if (fetchedProfile) {
128 this.profile = fetchedProfile;
129 // Validate NIP-05 if present
130 if (fetchedProfile.nip05) {
131 this.#validateNip05(pubkey, fetchedProfile.nip05);
132 }
133 }
134
135 this.loading = false;
136 } catch (error) {
137 console.error(error);
138 this.loading = false;
139 }
140 }
141
142 async #validateNip05(pubkey: string, nip05: string) {
143 try {
144 this.validating = true;
145
146 // Direct NIP-05 validation - fetches .well-known/nostr.json directly
147 const result = await validateNip05(nip05, pubkey);
148 this.nip05isValidated = result.valid;
149
150 if (result.valid) {
151 this.#logger.logNip05ValidationSuccess(nip05, pubkey);
152 } else {
153 this.#logger.logNip05ValidationError(nip05, result.error ?? 'Unknown error');
154 }
155
156 this.validating = false;
157 } catch (error) {
158 const errorMsg = error instanceof Error ? error.message : 'Unknown error';
159 this.#logger.logNip05ValidationError(nip05, errorMsg);
160 this.nip05isValidated = false;
161 this.validating = false;
162 }
163 }
164 }
165