From 87b95aed9f344028bf7161365b0a559cc0bbd9a9 Mon Sep 17 00:00:00 2001 From: LeoLox <58687994+leo-lox@users.noreply.github.com> Date: Fri, 22 Nov 2024 14:00:47 +0100 Subject: [PATCH] nostrList entities, models --- lib/data_layer/models/nostr_lists_model.dart | 119 ++++++++++ .../nostr_list_repository_impl.dart | 53 +++++ lib/domain_layer/entities/nostr_list.dart | 213 ++++++++++++++++++ .../repositories/nostr_list_repository.dart | 8 + 4 files changed, 393 insertions(+) create mode 100644 lib/data_layer/models/nostr_lists_model.dart create mode 100644 lib/data_layer/repositories/nostr_list_repository_impl.dart create mode 100644 lib/domain_layer/entities/nostr_list.dart create mode 100644 lib/domain_layer/repositories/nostr_list_repository.dart diff --git a/lib/data_layer/models/nostr_lists_model.dart b/lib/data_layer/models/nostr_lists_model.dart new file mode 100644 index 0000000..28ba3bb --- /dev/null +++ b/lib/data_layer/models/nostr_lists_model.dart @@ -0,0 +1,119 @@ +import '../../domain_layer/entities/nostr_list.dart'; +import 'package:ndk/entities.dart' as ndk_entities; + +class NostrListModel extends NostrList { + NostrListModel({ + required super.pubKey, + required super.kind, + required super.createdAt, + required super.elements, + }); + + // Convert from Nip51List (NDK) to NostrListModel + static NostrListModel fromNDK(ndk_entities.Nip51List ndkList) { + return NostrListModel( + pubKey: ndkList.pubKey, + kind: ndkList.kind, + createdAt: ndkList.createdAt, + elements: ndkList.elements + .map((e) => NostrListElementModel.fromNDK(e)) + .toList(), + )..id = ndkList.id; + } + + // Convert from NostrListModel to Nip51List (NDK) + ndk_entities.Nip51List toNDK() { + return ndk_entities.Nip51List( + pubKey: pubKey, + kind: kind, + createdAt: createdAt, + elements: elements + .map((e) => NostrListElementModel( + tag: e.tag, + value: e.value, + private: e.private, + ).toNDK()) + .toList(), + )..id = id; + } +} + +class NostrSetModel extends NostrSet { + //String name; + //String? title; + String? description; + String? image; + + NostrSetModel({ + required super.pubKey, + required super.name, + required super.createdAt, + required super.elements, + super.kind = NostrList.FOLLOW_SET, + super.title, + this.description, + this.image, + }); + + // Convert from Nip51Set (NDK) to NostrSetModel + static NostrSetModel fromNDK(ndk_entities.Nip51Set ndkSet) { + return NostrSetModel( + pubKey: ndkSet.pubKey, + name: ndkSet.name, + createdAt: ndkSet.createdAt, + elements: + ndkSet.elements.map((e) => NostrListElementModel.fromNDK(e)).toList(), + title: ndkSet.title, + description: ndkSet.description, + image: ndkSet.image, + )..id = ndkSet.id; + } + + // Convert from NostrSetModel to Nip51Set (NDK) + @override + ndk_entities.Nip51Set toNDK() { + return ndk_entities.Nip51Set( + pubKey: pubKey, + name: name, + createdAt: createdAt, + elements: elements + .map((e) => NostrListElementModel( + tag: e.tag, + value: e.value, + private: e.private, + ).toNDK()) + .toList(), + title: title, + ) + ..id = id + ..description = description + ..image = image; + } +} + +class NostrListElementModel extends NostrListElement { + NostrListElementModel({ + required super.tag, + required super.value, + required super.private, + }); + + // Convert from Nip51ListElement (NDK) to Nip51ListElementModel + static NostrListElementModel fromNDK( + ndk_entities.Nip51ListElement ndkElement) { + return NostrListElementModel( + tag: ndkElement.tag, + value: ndkElement.value, + private: ndkElement.private, + ); + } + + // Convert from Nip51ListElementModel to Nip51ListElement (NDK) + ndk_entities.Nip51ListElement toNDK() { + return ndk_entities.Nip51ListElement( + tag: tag, + value: value, + private: private, + ); + } +} diff --git a/lib/data_layer/repositories/nostr_list_repository_impl.dart b/lib/data_layer/repositories/nostr_list_repository_impl.dart new file mode 100644 index 0000000..6cf0a62 --- /dev/null +++ b/lib/data_layer/repositories/nostr_list_repository_impl.dart @@ -0,0 +1,53 @@ +import 'dart:developer'; + +import 'package:camelus/domain_layer/entities/nostr_list.dart'; +import 'package:ndk/entities.dart' as ndk_entities; +import 'package:ndk/ndk.dart' as ndk; +import 'package:rxdart/rxdart.dart'; + +import '../../domain_layer/entities/nostr_note.dart'; +import '../../domain_layer/repositories/nostr_list_repository.dart'; +import '../data_sources/dart_ndk_source.dart'; +import '../models/nostr_lists_model.dart'; +import '../models/nostr_note_model.dart'; + +class NostrListRepositoryImpl implements NostrListRepository { + final DartNdkSource dartNdkSource; + final ndk.EventVerifier eventVerifier; + + NostrListRepositoryImpl( + {required this.dartNdkSource, required this.eventVerifier, r}); + + @override + Stream getAllNotes() { + ndk.Filter filter = ndk.Filter( + authors: [], + kinds: [ndk_entities.Nip01Event.TEXT_NODE_KIND], + ); + + final response = dartNdkSource.dartNdk.requests + .query(filters: [filter], name: 'getAllNotes-'); + + return response.stream.map( + (event) => NostrNoteModel.fromNDKEvent(event), + ); + } + + @override + Future getNostrFollowSet({ + required String pubKey, + required String name, + }) async { + final ndkSet = + await dartNdkSource.dartNdk.lists.getSinglePublicNip51RelaySet( + name: name, + publicKey: pubKey, + ); + + if (ndkSet == null) { + return null; + } + final listSet = NostrSetModel.fromNDK(ndkSet); + return listSet; + } +} diff --git a/lib/domain_layer/entities/nostr_list.dart b/lib/domain_layer/entities/nostr_list.dart new file mode 100644 index 0000000..8769058 --- /dev/null +++ b/lib/domain_layer/entities/nostr_list.dart @@ -0,0 +1,213 @@ +class NostrList { + static const int MUTE = 10000; + static const int PIN = 10001; + static const int BOOKMARKS = 10003; + static const int COMMUNITIES = 10004; + static const int PUBLIC_CHATS = 10005; + static const int BLOCKED_RELAYS = 10006; + static const int SEARCH_RELAYS = 10007; + static const int INTERESTS = 10015; + static const int EMOJIS = 10030; + + static const int FOLLOW_SET = 30000; + static const int RELAY_SET = 30002; + static const int BOOKMARKS_SET = 30003; + static const int CURATION_SET = 30004; + static const int INTERESTS_SET = 30015; + static const int EMOJIS_SET = 30030; + + static const String RELAY = "relay"; + static const String PUB_KEY = "p"; + static const String HASHTAG = "t"; + static const String WORD = "word"; + static const String THREAD = "e"; + static const String RESOURCE = "r"; + static const String EMOJI = "emoji"; + static const String A = "a"; + + static const List POSSIBLE_KINDS = [ + MUTE, + PIN, + BOOKMARKS, + COMMUNITIES, + PUBLIC_CHATS, + BLOCKED_RELAYS, + SEARCH_RELAYS, + INTERESTS, + EMOJIS, + FOLLOW_SET, + RELAY_SET, + BOOKMARKS_SET, + CURATION_SET, + INTERESTS_SET, + EMOJIS_SET + ]; + + static const List POSSIBLE_TAGS = [ + RELAY, + PUB_KEY, + HASHTAG, + WORD, + THREAD, + RESOURCE, + EMOJI, + A + ]; + + late String id; + late String pubKey; + late int kind; + + List elements = []; + + List byTag(String tag) => + elements.where((element) => element.tag == tag).toList(); + + List get relays => byTag(RELAY); + List get pubKeys => byTag(PUB_KEY); + List get hashtags => byTag(HASHTAG); + List get words => byTag(WORD); + List get threads => byTag(THREAD); + + List get publicRelays => + relays.where((element) => !element.private).map((e) => e.value).toList(); + List get privateRelays => + relays.where((element) => !element.private).map((e) => e.value).toList(); + + set privateRelays(List list) { + elements.removeWhere((element) => element.tag == RELAY && element.private); + elements.addAll(list + .map((url) => NostrListElement(tag: RELAY, value: url, private: true))); + } + + set publicRelays(List list) { + elements.removeWhere((element) => element.tag == RELAY && !element.private); + elements.addAll(list.map( + (url) => NostrListElement(tag: RELAY, value: url, private: false))); + } + + late int createdAt; + + @override + // coverage:ignore-start + String toString() { + return 'Nip51List { $kind}'; + } + // coverage:ignore-end + + String get displayTitle { + if (kind == NostrList.SEARCH_RELAYS) { + return "Search"; + } + if (kind == NostrList.BLOCKED_RELAYS) { + return "Blocked"; + } + if (kind == NostrList.MUTE) { + return "Mute"; + } + return "kind $kind"; + } + + List get allRelays => relays.map((e) => e.value).toList(); + + NostrList({ + required this.pubKey, + required this.kind, + required this.createdAt, + required this.elements, + }); + + void parseTags(List tags, {required bool private}) { + for (var tag in tags) { + if (tag is! List) continue; + final length = tag.length; + if (length <= 1) continue; + final tagName = tag[0]; + final value = tag[1]; + if (POSSIBLE_TAGS.contains(tagName)) { + elements.add( + NostrListElement(tag: tagName, value: value, private: private)); + } + } + } + + void addRelay(String relayUrl, bool private) { + elements + .add(NostrListElement(tag: RELAY, value: relayUrl, private: private)); + } + + void addElement(String tag, String value, bool private) { + elements.add(NostrListElement(tag: tag, value: value, private: private)); + } + + void removeRelay(String relayUrl) { + elements.removeWhere( + (element) => element.tag == RELAY && element.value == relayUrl); + } + + void removeElement(String tag, String value) { + elements + .removeWhere((element) => element.tag == tag && element.value == value); + } +} + +class NostrListElement { + bool private; + String tag; + String value; + + NostrListElement({ + required this.tag, + required this.value, + required this.private, + }); +} + +class NostrSet extends NostrList { + late String name; + String? title; + String? description; + String? image; + + @override + String toString() { + return 'Nip51Set { $name}'; + } + + /// Create a new Nip51Set + /// default kind is Nip51List.FOLLOW_SET + NostrSet({ + required super.pubKey, + required this.name, + required super.createdAt, + required super.elements, + this.title, + super.kind = NostrList.FOLLOW_SET, + }); + + void parseSetTags(List tags) { + for (var tag in tags) { + if (tag is! List) continue; + final length = tag.length; + if (length <= 1) continue; + final tagName = tag[0]; + final value = tag[1]; + if (tagName == "d") { + name = value; + continue; + } + if (tagName == "title") { + title = value; + continue; + } + if (tagName == "description") { + description = value; + continue; + } + if (tagName == "image") { + image = value; + continue; + } + } + } +} diff --git a/lib/domain_layer/repositories/nostr_list_repository.dart b/lib/domain_layer/repositories/nostr_list_repository.dart new file mode 100644 index 0000000..74f59a2 --- /dev/null +++ b/lib/domain_layer/repositories/nostr_list_repository.dart @@ -0,0 +1,8 @@ +import '../entities/nostr_list.dart'; + +abstract class NostrListRepository { + Future getNostrFollowSet({ + required String pubKey, + required String name, + }); +}