From 582c42279226028bce286351361078735670f83c Mon Sep 17 00:00:00 2001 From: Vishal <64505169+vishalxl@users.noreply.github.com> Date: Thu, 1 Sep 2022 16:57:57 +0530 Subject: [PATCH] added dm to menu and displayed them but no en/decryption done yet --- lib/console_ui.dart | 19 ++-- lib/event_ds.dart | 4 +- lib/tree_ds.dart | 184 +++++++++++++++++++++++++++++++++-- test/nostr_console_test.dart | 2 +- 4 files changed, 191 insertions(+), 18 deletions(-) diff --git a/lib/console_ui.dart b/lib/console_ui.dart index c88951c..6aeab44 100644 --- a/lib/console_ui.dart +++ b/lib/console_ui.dart @@ -433,9 +433,11 @@ Future channelMenuUI(Store node) async { //gDebug = 0; bool continueChatMenu = true; while(continueChatMenu) { - int option = showMenu([ 'Show channels', // 1 - 'Enter a channel', // 2 - 'Go back to main menu'], // 3 + int option = showMenu([ 'Show public channels', // 1 + 'Enter a public channel', // 2 + 'See personal Inbox', + 'Send a direct message', + 'Go back to main menu'], // 5 "Channel Menu"); // name of menu print('You picked: $option'); switch(option) { @@ -491,6 +493,11 @@ Future channelMenuUI(Store node) async { break; case 3: + print("total direct rooms = ${node.directRooms.length}"); + node.printDirectRoomInfo(); + break; + + case 5: continueChatMenu = false; break; @@ -506,7 +513,7 @@ Future mainMenuUi(Store node) async { // at the very beginning, show the tree as it is, and then show the options menu bool hasRepliesAndLikes (Tree t) => t.hasRepliesAndLikes(userPublicKey); - node.printTree(0, DateTime.now().subtract(Duration(days:gNumLastDays)), hasRepliesAndLikes); + //node.printTree(0, DateTime.now().subtract(Duration(days:gNumLastDays)), hasRepliesAndLikes); bool userContinue = true; while(userContinue) { @@ -528,14 +535,14 @@ Future mainMenuUi(Store node) async { // the main menu int option = showMenu(['Display feed', // 1 'Post/Reply/Like', // 2 - 'Channels', // 3 + 'Direct Messsage and Public Channels', // 3 'Other Options', // 4 'Quit'], // 5 "Main Menu"); print('You picked: $option'); switch(option) { case 1: - node.printTree(0, DateTime.now().subtract(Duration(days:gNumLastDays)), selectAll); + //node.printTree(0, DateTime.now().subtract(Duration(days:gNumLastDays)), selectAll); break; case 2: diff --git a/lib/event_ds.dart b/lib/event_ds.dart index 994e0b4..05723c1 100644 --- a/lib/event_ds.dart +++ b/lib/event_ds.dart @@ -5,7 +5,7 @@ import 'package:translator/translator.dart'; import 'package:crypto/crypto.dart'; import 'package:nostr_console/settings.dart'; -int gDebug = 0; +int gDebug = 1; // translate final translator = GoogleTranslator(); @@ -100,7 +100,7 @@ class EventData { } } else { int eKind = json['kind']; - if ( eKind == 1 || eKind == 7 || eKind == 42 || eKind == 5) { + if ( eKind == 1 || eKind == 7 || eKind == 42 || eKind == 5 || eKind == 4) { for( int i = 0; i < numTags; i++) { var tag = jsonTags[i]; //stdout.write(tag); diff --git a/lib/tree_ds.dart b/lib/tree_ds.dart index 66184c0..d095ea8 100644 --- a/lib/tree_ds.dart +++ b/lib/tree_ds.dart @@ -353,9 +353,11 @@ class Store { List eventsWithoutParent; bool whetherTopMost; Map chatRooms = {}; + Map directRooms = {}; + Set eventsNotReadFromFile; - Store(this.children, this.allChildEventsMap, this.eventsWithoutParent, this.whetherTopMost, this.chatRooms, this.eventsNotReadFromFile) { + Store(this.children, this.allChildEventsMap, this.eventsWithoutParent, this.whetherTopMost, this.chatRooms, this.directRooms, this.eventsNotReadFromFile) { allChildEventsMap.forEach((eventId, tree) { if( tree.store == null) { tree.setStore(this); @@ -363,7 +365,7 @@ class Store { }); } - static const Set typesInEventMap = {0, 1, 3, 5, 7, 40, 42}; // 0 meta, 1 post, 3 follows list, 7 reactions + static const Set typesInEventMap = {0, 1, 3, 4, 5, 7, 40, 42}; // 0 meta, 1 post, 3 follows list, 7 reactions static void handleChannelEvents( Map rooms, Map tempChildEventsMap, Event ce) { String eId = ce.eventData.id; @@ -424,13 +426,63 @@ class Store { break; } // end switch } + + static bool isUserDirectMessage(Event directMessage) { + if( directMessage.eventData.pubkey == userPublicKey) { + return true; + } + + bool sentToUser = false; + directMessage.eventData.tags.forEach((tag) { + if( tag.length < 2 ) + return; + if( tag[0] == "p" && tag[1] == userPublicKey) { + sentToUser = true; + } + }); + + return sentToUser; + } + + static void handleDirectMessages( Map directRooms, Map tempChildEventsMap, Event ce) { + String eId = ce.eventData.id; + int eKind = ce.eventData.kind; + + if( !isUserDirectMessage(ce)) { + return; + } + + switch(eKind) { + case 4: + { + String directRoomId = getDirectRoomId(ce.eventData); + if( directRoomId != "") { + if( directRooms.containsKey(directRoomId)) { + if( gDebug >= 0) print("Adding new message ${ce.eventData.id} to a direct room $directRoomId. "); + addMessageToDirectRoom(directRoomId, eId, tempChildEventsMap, directRooms); + } else { + List temp = []; + temp.add(eId); + DirectMessageRoom newDirectRoom= DirectMessageRoom(directRoomId, temp); + directRooms[directRoomId] = newDirectRoom; + if( gDebug >= 0) print("Adding new message ${ce.eventData.id} to NEW direct room $directRoomId. "); + } + } else { + if( gDebug > 0) print("Could not get chat room id for event ${ce.eventData.id}, its original json: "); + } + } + break; + default: + break; + } // end switch + } /***********************************************************************************************************************************/ // @method create top level Tree from events. // first create a map. then process each element in the map by adding it to its parent ( if its a child tree) factory Store.fromEvents(Set events) { if( events.isEmpty) { - return Store( [], {}, [], false, {}, {}); + return Store( [], {}, [], false, {}, {}, {}); } // create a map tempChildEventsMap from list of events, key is eventId and value is event itself @@ -448,6 +500,7 @@ class Store { List topLevelTrees = [];// this will become the children of the main top node. These are events without parents, which are printed at top. List tempWithoutParent = []; Map rooms = {}; + Map tempDirectRooms= {}; int numEventsNotPosts = 0; // just for debugging info int numKind40Events = 0; @@ -460,6 +513,11 @@ class Store { handleChannelEvents(rooms, tempChildEventsMap, tree.event); } + if( eKind == 4) { + handleDirectMessages(tempDirectRooms, tempChildEventsMap, tree.event); + } + + // only posts, of kind 1, are added to the main tree structure if( eKind != 1) { numEventsNotPosts++; @@ -508,7 +566,7 @@ class Store { if(gDebug != 0) print("In Tree FromEvents: number of events without parent in fromEvents = ${tempWithoutParent.length}"); // create a dummy top level tree and then create the main Tree object - return Store( topLevelTrees, tempChildEventsMap, tempWithoutParent, true, rooms, {}); + return Store( topLevelTrees, tempChildEventsMap, tempWithoutParent, true, rooms, tempDirectRooms, {}); } // end fromEvents() /***********************************************************************************************************************************/ @@ -542,7 +600,12 @@ class Store { return; } - // only kind 0, 1, 3, 5( delete), 7, 40, 42 events are added to map, return otherwise + if( !isUserDirectMessage(newEvent)) { // direct message not relevant to user are ignored + return; + } + + + // only kind 0, 1, 3, 4, 5( delete), 7, 40, 42 events are added to map, return otherwise if( !typesInEventMap.contains(newEvent.eventData.kind) ) { return; } @@ -585,6 +648,22 @@ class Store { } } break; + case 4: + // add kind 4 direct chat message event to its direct massage room + String directRoomId = getDirectRoomId(newTree.event.eventData); + print("in insert events: got directRoomId = ${directRoomId}"); + if( directRoomId != "") { + if( directRooms.containsKey(directRoomId)) { + if( gDebug > 0) print("added event to chat room in insert event"); + addMessageToDirectRoom(directRoomId, newTree.event.eventData.id, allChildEventsMap, directRooms); + newTree.event.eventData.isNotification = true; // highlight it too in next printing + print(" in from event: added it to a direct room"); + } + } else { + print(" in insert events, could not find parent/channel id"); + } + break; + case 42: // add 42 chat message event id to its chat room String channelId = newTree.event.eventData.getParent(); @@ -740,10 +819,10 @@ class Store { * @printAllChennelsInfo Print one line information about all channels, which are type 40 events ( class ChatRoom) */ void printAllChannelsInfo() { - print("\n\nChannels/Rooms:"); - printUnderlined(" Channel/Room Name Num of Messages Latest Message "); + print("\n\nDirect messages inbox:"); + printUnderlined(" Channel Name Num of Messages Latest Message "); chatRooms.forEach((key, value) { - String name = ""; + String name = "direct room name"; if( value.chatRoomName == "") { name = value.chatRoomId.substring(0, 6); } else { @@ -766,6 +845,37 @@ class Store { }); } + /** + * @printAllChennelsInfo Print one line information about all channels, which are type 40 events ( class ChatRoom) + */ + void printDirectRoomInfo() { + print("\n\nDirect messages inbox:"); + printUnderlined(" From Num of Messages Latest Message "); + directRooms.forEach((key, value) { + String name = "direct room name"; +/* if( value.chatRoomName == "") { + name = value.chatRoomId.substring(0, 6); + } else { + name = "${value.chatRoomName} ( ${value.chatRoomId.substring(0, 6)})"; + } +*/ + int numMessages = value.messageIds.length; + stdout.write("${name} ${getNumSpaces(32-name.length)} $numMessages${getNumSpaces(12- numMessages.toString().length)}"); + List messageIds = value.messageIds; + for( int i = messageIds.length - 1; i >= 0; i++) { + if( allChildEventsMap.containsKey(messageIds[i])) { + Event? e = allChildEventsMap[messageIds[i]]?.event; + if( e!= null) { + stdout.write("${e.eventData.getAsLine()}"); + break; // print only one event, the latest one + } + } + } + print(""); + }); + } + + void printChannel(ChatRoom room, [int page = 1]) { if( page < 1) { if( gDebug > 0) log.info("In printChannel got page = $page"); @@ -1098,6 +1208,43 @@ void addMessageToChannel(String channelId, String messageId, Map t print("In addMessageToChannel: returning without inserting message"); } +void addMessageToDirectRoom(String directRoomId, String messageId, Map tempChildEventsMap, var directRooms) { + int newEventTime = (tempChildEventsMap[messageId]?.event.eventData.createdAt??0); + + if( directRooms.containsKey(directRoomId)) { + DirectMessageRoom? room = directRooms[directRoomId]; + if( room != null ) { + if( room.messageIds.isEmpty) { + room.messageIds.add(messageId); + return; + } + + if(gDebug> 0) print("direct room has ${room.messageIds.length} messages already. adding new one to it. "); + + for(int i = 0; i < room.messageIds.length; i++) { + int eventTime = (tempChildEventsMap[room.messageIds[i]]?.event.eventData.createdAt??0); + if( newEventTime < eventTime) { + // shift current i and rest one to the right, and put event Time here + if(gDebug> 0) print("In addMessageToChannel: inserted in middle to channel ${room.otherPubkey} "); + room.messageIds.insert(i, messageId); + return; + } + } + if(gDebug> 0) print("In addMessageToChannel: added to channel ${room.otherPubkey} "); + + // insert at end + room.messageIds.add(messageId); + return; + } else { + print("In addMessageToChannel: could not find room"); + } + } else { + print("In addMessageToChannel: could not find channel id"); + } + print("In addMessageToChannel: returning without inserting message"); +} + + int ascendingTimeTree(Tree a, Tree b) { if(a.event.eventData.createdAt < b.event.eventData.createdAt) { return -1; @@ -1190,7 +1337,7 @@ void processReactions(Set events) { Store getTree(Set events) { if( events.isEmpty) { if(gDebug > 0) log.info("Warning: In printEventsAsTree: events length = 0"); - return Store([], {}, [], true, {}, {}); + return Store([], {}, [], true, {}, {}, {}); } // remove all events other than kind 0 (meta data), 1(posts replies likes), 3 (contact list), 7(reactions), 40 and 42 (chat rooms) @@ -1227,3 +1374,22 @@ Store getTree(Set events) { if(gDebug != 0) print("total number of posts/replies in main tree = ${node.count()}"); return node; } + +// sort all participants by id; then create a large string with them together, thats the unique id for now +String getDirectRoomId(EventData eventData) { + List participantIds = []; + String roomId = ""; + eventData.tags.forEach((tag) { + if( tag.length < 2) + return; + + if( tag[0] == 'p') { + participantIds.add(tag[1]); + } + }); + + participantIds.sort(); + String uniqueId = ""; + participantIds.forEach((element) {uniqueId += element;}); + return uniqueId; +} diff --git a/test/nostr_console_test.dart b/test/nostr_console_test.dart index 70b7636..eb17566 100644 --- a/test/nostr_console_test.dart +++ b/test/nostr_console_test.dart @@ -9,7 +9,7 @@ EventData exampleEdataChild = EventData("id2", "pubkey", 1111111, 1, "content ch Event exampleEvent = Event('event', 'id3', exampleEdata, ['relay name'], "[json]"); Event exampleEventChild = Event('event', 'id4', exampleEdataChild, ['relay name'], "[json]"); -Store exampleStore = Store([], {}, [], false, {}, {}); +Store exampleStore = Store([], {}, [], false, {}, {}, {}); Tree exampleTree = Tree.withoutStore(exampleEvent, []); void main() {