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.
Current behavior
For public list/set content:
- reads use the latest visible event for the relevant list or set
- cached results are reused on later reads
For private list/set content:
- the original event content stays encrypted
- decrypted private tags can be loaded through NDK's decrypted payload cache
- repeated reads can reuse cached plaintext instead of decrypting again
Lists Methods
getSingleNip51List
Retrieves a NIP-51 list by kind.
return _decryptedEventPayloads.loadOrDecrypt(
event: event,
viewerPubKey: signer.getPublicKey(),
scheme: isNip04
? DecryptedPayloadScheme.nip04
: DecryptedPayloadScheme.nip44,
decrypt: () => isNip04
// ignore: deprecated_member_use_from_same_package
? signer.decrypt(event.content, signer.getPublicKey())
: signer.decryptNip44(
ciphertext: event.content,
senderPubKey: signer.getPublicKey(),
),
);
}
Future<void> _applyPrivateTags({
addElementToList
Adds an element to a list. Creates the list if it doesn't exist.
return list;
}
Future<Nip51Set?> _parseSetEvent(
Nip01Event event,
EventSigner? signer,
) async {
final set = await Nip51Set.fromEvent(event, null);
if (set != null && signer != null) {
await _applyPrivateTags(event: event, signer: signer, list: set);
}
return set;
}
///* lists *///
Future<Nip51List?> _getCachedNip51List(int kind, EventSigner signer) async {
List<Nip01Event>? events = await _cacheManager.loadEvents(
pubKeys: [signer.getPublicKey()],
kinds: [kind],
removeElementFromList
Removes an element from a list.
filters: [
Filter(authors: [signer.getPublicKey()], kinds: [kind]),
],
timeout: timeout,
)
.stream) {
if (refreshedList == null ||
refreshedList.createdAt <= event.createdAt) {
refreshedList = await _parseListEvent(event, signer);
// if (Helpers.isNotBlank(event.content)) {
// Nip51List? decryptedList = await Nip51List.fromEvent(event, signer);
// refreshedList = decryptedList;
// }
await _cacheManager.saveEvent(event);
}
}
return refreshedList;
}
Sets Methods
getSetByName
Gets a specific set by name (d-tag) and kind.
}
/// Removes an element from a NIP-51 list.
///
/// Updates the list by removing the specified element, then broadcasts
/// the updated list to relays and updates the cache.
///
/// [kind] the kind of NIP-51 list \
/// [tag] the tag type of the element to remove \
/// [value] the value to remove from the list \
/// [broadcastRelays] optional specific relays to broadcast to
///
/// Returns the updated list, or null if the list doesn't exist.\
/// Throws an exception if no event signer is available.
Future<Nip51List?> removeElementFromList({
required int kind,
required String tag,
required String value,
getPublicSets
Returns a stream of all public sets for a given public key and kind.
/// gets set by name with the specified signer
Future<Nip51Set?> _getCachedSetByName(
String name,
EventSigner signer,
int kind,
) async {
List<Nip01Event>? events = await _cacheManager.loadEvents(
pubKeys: [signer.getPublicKey()],
kinds: [kind],
);
events = events.where((event) {
if (event.getDtag() != null && event.getDtag() == name) {
return true;
}
return false;
addElementToSet
Adds an element to a named set. Creates the set if it doesn't exist.
: null;
}
/// 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]),
],
cacheRead: !forceRefresh,
)
.stream
.where((event) => event.getDtag() != null)
.asyncMap((event) async {
removeElementFromSet
Removes an element from a named set.
final EventSigner signer;
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],
},
),
],
setCompleteSet
Overwrites or creates a complete set. Warning: This replaces the entire set.
);
}
return _getSets(kind, mySigner, forceRefresh: forceRefresh);
}
/// Adds an element to a NIP-51 set.
///
/// If the set doesn't exist, it will be created. The updated set is
/// then broadcast to relays and cached locally.
///
/// [name] name of the set (d tag identifier) \
/// [tag] the tag type for the element (e.g., 'relay', 'p', 'e') \
/// [value] the value to add to the set \
/// [kind] kind of the set \
deleteSet
Deletes a set by name and broadcasts a deletion event.
final broadcastResponse = _broadcast.broadcast(
nostrEvent: event,
specificRelays: specificRelays,
customSigner: _eventSigner,
);
await broadcastResponse.broadcastDoneFuture;
List<Nip01Event>? events = await _cacheManager.loadEvents(
pubKeys: [_eventSigner!.getPublicKey()],
kinds: [kind],
);
events = events.where((event) {
if (event.getDtag() != null && event.getDtag() == name) {
return true;
}
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");
});