Lists

high level

Example

final myset = await ndk.lists.getSetByName(
  name: "myset",
  kind: Nip51List.kRelaySet,
  customSigner: mySigner,
);

if (myset == null) {
  print("set not found");
  return;
}
print("received a set with ${myset.elements.length} elements");

How to use

We distinguish between lists and sets:

  • Lists: Single lists identified by kind (e.g., bookmarks, mute list)
  • Sets: Named collections identified by kind + name/d-tag (e.g., relay sets, follow sets)

Both can have public and private (encrypted) elements.

Lists Methods

getSingleNip51List

Retrieves a NIP-51 list by kind.

/// Returns a NIP-51 list by the given kind.
///
/// Retrieves the most recent list event for the specified kind.
/// First checks cache unless [forceRefresh] is true, then queries relays.
///
/// [kind] the kind of NIP-51 list to retrieve \
/// [forceRefresh] if true, bypass cache and query relays directly \
/// [timeout] maximum duration to wait for relay responses
///
/// Returns the list if found, null otherwise.
Future<Nip51List?> getSingleNip51List(
  int kind, {
  bool forceRefresh = false,
  Duration timeout = const Duration(seconds: 5),
}) async {
  if (_eventSigner == null) {
    throw Exception("cannot get nip51 list without a signer");

addElementToList

Adds an element to a list. Creates the list if it doesn't exist.

}

/// Returns a NIP-51 list by kind for a given public key.
///
/// Retrieves the most recent list event for the specified kind and public key.
/// Unlike [getSingleNip51List], this works with any public key, not just
/// the logged-in user.
///
/// [kind] the kind of NIP-51 list to retrieve \
/// [publicKey] the public key of the user whose list to retrieve \
/// [forceRefresh] if true, bypass cache and query relays directly \
/// [timeout] maximum duration to wait for relay responses
///
/// Returns the list if found, null otherwise.
Future<Nip51List?> getPublicList({
  required int kind,
  required String publicKey,
  bool forceRefresh = false,
  Duration timeout = const Duration(seconds: 5),
}) async {

removeElementFromList

Removes an element from a list.

/// Returns the updated list.\
/// Throws an exception if no event signer is available.
Future<Nip51List> addElementToList({
  required int kind,
  required String tag,
  required String value,
  Iterable<String>? broadcastRelays,
  bool private = false,
}) async {
  if (_eventSigner == null) {
    throw Exception(
        "cannot broadcast private nip51 list without a signer that can sign");
  }
  Nip51List? list = await getSingleNip51List(
    kind,
    forceRefresh: true,
  );
  list ??= Nip51List(

Sets Methods

getSetByName

Gets a specific set by name (d-tag) and kind.

}

/// get a nip51 set
Stream<Iterable<Nip51Set>?> _getSets(
  int kind,
  EventSigner signer, {
  bool forceRefresh = false,
}) {
  final relaySets = <String, Nip51Set>{};

  return _requests
      .query(
        filters: [
          Filter(
            authors: [signer.getPublicKey()],
            kinds: [kind],
          )
        ],

getPublicSets

Returns a stream of all public sets for a given public key and kind.

if (_eventSigner == null) {
  throw Exception("getSetByName() no account");
}
signer = _eventSigner!;

Nip51Set? relaySet = await _getCachedSetByName(name, signer, kind);
if (relaySet == null || forceRefresh) {
  Nip51Set? newRelaySet;
  await for (final event in _requests.query(filters: [
    Filter(
      authors: [signer.getPublicKey()],
      kinds: [kind],
      tags: {
        "#d": [name]
      },

addElementToSet

Adds an element to a named set. Creates the set if it doesn't exist.

          newRelaySet = await Nip51Set.fromEvent(event, signer);
          await _cacheManager.saveEvent(event);
        } else if (Helpers.isNotBlank(event.content)) {
          Nip51Set? decryptedRelaySet =
              await Nip51Set.fromEvent(event, signer);
          if (decryptedRelaySet != null && decryptedRelaySet.name == name) {
            newRelaySet = decryptedRelaySet;
            await _cacheManager.saveEvent(event);
          }
        }
      }
    }
    return newRelaySet;
  }
  return relaySet;
}

/// Returns a stream of public sets for a given public key, default is pubkey of logged in user.
///
/// Queries relays for all sets of the specified kind belonging to the
/// given public key. Only public (non-encrypted) sets are returned.

removeElementFromSet

Removes an element from a named set.

///
/// Returns the updated set.
Future<Nip51Set?> addElementToSet({
  required String name,
  required String tag,
  required String value,
  required int kind,
  bool private = false,
  Iterable<String>? specificRelays,
}) async {
  Nip51Set? set =
      await getSetByName(name: name, kind: kind, forceRefresh: true);
  set ??= Nip51Set(
      name: name,
      pubKey: _eventSigner!.getPublicKey(),
      kind: Nip51List.kRelaySet,
      createdAt: Helpers.now,
      elements: []);
  set.addElement(tag, value, private);
  set.createdAt = Helpers.now;
  Nip01Event event = await set.toEvent(_eventSigner);

setCompleteSet

Overwrites or creates a complete set. Warning: This replaces the entire set.

  throw Exception(
      "cannot broadcast private nip51 list without a signer that can sign");
}
Nip51Set? mySet = await getSetByName(
  name: name,
  kind: kind,
  forceRefresh: true,
);
if ((mySet == null || mySet.allRelays.isEmpty)) {
  mySet = Nip51Set(
      name: name,
      kind: Nip51List.kRelaySet,
      pubKey: _eventSigner!.getPublicKey(),
      createdAt: Helpers.now,
      elements: []);

deleteSet

Deletes a set by name and broadcasts a deletion event.


/// Overwrites or creates a complete NIP-51 set.
///
/// **Warning:** This replaces the entire set. Consider using
/// [addElementToSet] or [removeElementFromSet] for incremental updates.
///
/// [set] the complete set to broadcast \
/// [kind] kind of the set \
/// [specificRelays] optional specific relays to broadcast to
///
/// Returns the set after broadcasting.
Future<Nip51Set> setCompleteSet({
  required Nip51Set set,
  required int kind,
  Iterable<String>? specificRelays,

Common Use Cases

Relay Sets

// Add relay to a set
await ndk.lists.addElementToSet(
  name: "my-relays",
  tag: "relay",
  value: "wss://relay.example.com",
  kind: Nip51List.kRelaySet,
);

// Get a relay set
final relaySet = await ndk.lists.getSetByName(
  name: "my-relays",
  kind: Nip51List.kRelaySet,
);

Bookmarks

// Add bookmark
await ndk.lists.addElementToList(
  kind: Nip51List.kBookmarks,
  tag: "e",
  value: eventId,
);

// Get bookmarks
final bookmarks = await ndk.lists.getSingleNip51List(
  Nip51List.kBookmarks,
  mySigner,
);

Follow Sets

// Add to follow set
await ndk.lists.addElementToSet(
  name: "close-friends",
  tag: "p",
  value: pubkey,
  kind: Nip51List.kFollowSet,
);

// Stream all public follow sets
ndk.lists.getPublicSets(
  kind: Nip51List.kFollowSet,
  publicKey: somePubkey,
).listen((sets) {
  print("Found ${sets?.length ?? 0} follow sets");
});