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