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({
required Nip01Event event,
addElementToList
Adds an element to a list. Creates the list if it doesn't exist.
}
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.
).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;
}
return list;
}
/// Returns a NIP-51 list by kind for a given public key.
///
Sets Methods
getSetByName
Gets a specific set by name (d-tag) and kind.
/// [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,
Iterable<String>? broadcastRelays,
}) async {
if (_eventSigner == null) {
throw Exception(
"cannot broadcast private nip51 list without a signer that can sign",
);
}
Nip51List? list = await getSingleNip51List(kind, forceRefresh: true);
getPublicSets
Returns a stream of all public sets for a given public key and kind.
kinds: [kind],
);
events = events.where((event) {
if (event.getDtag() != null && event.getDtag() == name) {
return true;
}
return false;
}).toList();
events.sort((a, b) => b.createdAt.compareTo(a.createdAt));
return events.isNotEmpty
? await _parseSetEvent(events.first, signer)
: null;
}
/// get a nip51 set
addElementToSet
Adds an element to a named set. Creates the set if it doesn't exist.
}) {
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 {
final dtag = event.getDtag()!;
final existingSet = relaySets[dtag];
if (existingSet == null || existingSet.createdAt < event.createdAt) {
final newSet = await _parseSetEvent(event, signer);
if (newSet != null) {
await _cacheManager.saveEvent(event);
relaySets[newSet.name] = newSet;
removeElementFromSet
Removes an element from a named set.
if (relaySet == null || forceRefresh) {
Nip51Set? newRelaySet;
await for (final event in _requests.query(
filters: [
Filter(
authors: [signer.getPublicKey()],
kinds: [kind],
tags: {
"#d": [name],
},
),
],
cacheRead: !forceRefresh,
).stream) {
if (newRelaySet == null || newRelaySet.createdAt < event.createdAt) {
if (event.getDtag() != null && event.getDtag() == name) {
newRelaySet = await _parseSetEvent(event, signer);
await _cacheManager.saveEvent(event);
} else if (Helpers.isNotBlank(event.content)) {
Nip51Set? decryptedRelaySet = await _parseSetEvent(event, signer);
if (decryptedRelaySet != null && decryptedRelaySet.name == name) {
newRelaySet = decryptedRelaySet;
setCompleteSet
Overwrites or creates a complete set. Warning: This replaces the entire set.
/// [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 \
/// [private] if true, encrypt the element in the set content \
/// [specificRelays] optional specific relays to broadcast to
///
/// 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,
deleteSet
Deletes a set by name and broadcasts a deletion event.
events = events.where((event) {
if (event.getDtag() != null && event.getDtag() == name) {
return true;
}
return false;
}).toList();
for (final event in events) {
_cacheManager.removeEvent(event.id);
}
await _cacheManager.saveEvent(event);
return set;
}
/// Removes an element from a NIP-51 set.
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");
});