| | 1 | | namespace NostrSure.Domain.Entities; |
| | 2 | |
|
| | 3 | | using NostrSure.Domain.ValueObjects; |
| | 4 | |
|
| | 5 | | /// <summary> |
| | 6 | | /// Represents a contact entry in a NIP-02 contact list. |
| | 7 | | /// Immutable value object following SOLID principles. |
| | 8 | | /// </summary> |
| 48 | 9 | | public sealed record ContactEntry( |
| 53 | 10 | | Pubkey ContactPubkey, |
| 62 | 11 | | string? Petname = null, |
| 113 | 12 | | string? RelayUrl = null) |
| | 13 | | { |
| | 14 | | /// <summary> |
| | 15 | | /// Validates the contact entry according to NIP-02 specifications. |
| | 16 | | /// </summary> |
| 11 | 17 | | public bool IsValid => ContactPubkey.IsValid && |
| 11 | 18 | | (string.IsNullOrEmpty(RelayUrl) || Uri.TryCreate(RelayUrl, UriKind.Absolute, out _)); |
| | 19 | |
|
| | 20 | | /// <summary> |
| | 21 | | /// Creates a ContactEntry from a NostrTag "p" tag. |
| | 22 | | /// </summary> |
| | 23 | | public static ContactEntry FromPTag(NostrTag pTag) |
| 23 | 24 | | { |
| 23 | 25 | | if (pTag.Name != "p" || pTag.Values.Count == 0) |
| 2 | 26 | | throw new ArgumentException("Invalid p tag for contact entry", nameof(pTag)); |
| | 27 | |
|
| 21 | 28 | | var pubkeyValue = pTag.Values[0]; |
| 21 | 29 | | var petname = pTag.Values.ElementAtOrDefault(1); |
| 21 | 30 | | var relayUrl = pTag.Values.ElementAtOrDefault(2); |
| | 31 | |
|
| 21 | 32 | | return new ContactEntry( |
| 21 | 33 | | new Pubkey(pubkeyValue), |
| 21 | 34 | | string.IsNullOrWhiteSpace(petname) ? null : petname, |
| 21 | 35 | | string.IsNullOrWhiteSpace(relayUrl) ? null : relayUrl |
| 21 | 36 | | ); |
| 21 | 37 | | } |
| | 38 | |
|
| | 39 | | /// <summary> |
| | 40 | | /// Converts this ContactEntry to a NostrTag "p" tag. |
| | 41 | | /// </summary> |
| | 42 | | public NostrTag ToPTag() |
| 23 | 43 | | { |
| 23 | 44 | | var values = BuildPTagValues(); |
| 23 | 45 | | return new NostrTag("p", values); |
| 23 | 46 | | } |
| | 47 | |
|
| | 48 | | /// <summary> |
| | 49 | | /// Builds the values list for the "p" tag, handling optional fields explicitly. |
| | 50 | | /// </summary> |
| | 51 | | private List<string> BuildPTagValues() |
| 23 | 52 | | { |
| 23 | 53 | | var values = new List<string> { ContactPubkey.Value }; |
| | 54 | |
|
| 23 | 55 | | bool hasPetname = !string.IsNullOrWhiteSpace(Petname); |
| 23 | 56 | | bool hasRelayUrl = !string.IsNullOrWhiteSpace(RelayUrl); |
| | 57 | |
|
| 23 | 58 | | if (hasPetname) |
| 18 | 59 | | { |
| 18 | 60 | | values.Add(Petname!); |
| 18 | 61 | | } |
| 5 | 62 | | else if (hasRelayUrl) |
| 2 | 63 | | { |
| | 64 | | // Petname slot must be present (as empty string) if relay URL is present |
| 2 | 65 | | values.Add(string.Empty); |
| 2 | 66 | | } |
| | 67 | |
|
| 23 | 68 | | if (hasRelayUrl) |
| 8 | 69 | | { |
| 8 | 70 | | values.Add(RelayUrl!); |
| 8 | 71 | | } |
| | 72 | |
|
| 23 | 73 | | return values; |
| 23 | 74 | | } |
| | 75 | | } |