From 855b2fe8fee99e93639a9458ac702b0f48caf410 Mon Sep 17 00:00:00 2001 From: Vishal <64505169+vishalxl@users.noreply.github.com> Date: Wed, 7 Sep 2022 15:44:15 +0530 Subject: [PATCH] sorted direct room main view made gStore a global. Made directRooms a list rather than a map. --- bin/nostr_console.dart | 1 + lib/event_ds.dart | 7 +- lib/settings.dart | 7 +- lib/tree_ds.dart | 364 ++++++++++++++++++++++++----------------- 4 files changed, 229 insertions(+), 150 deletions(-) diff --git a/bin/nostr_console.dart b/bin/nostr_console.dart index e42eca8..16ac6c6 100644 --- a/bin/nostr_console.dart +++ b/bin/nostr_console.dart @@ -288,6 +288,7 @@ Future main(List arguments) async { // Creat tree from all events read form file Store node = getTree(initialEvents); + gStore = node; clearEvents(); mainMenuUi(node); diff --git a/lib/event_ds.dart b/lib/event_ds.dart index 7b99ed0..5043b3c 100644 --- a/lib/event_ds.dart +++ b/lib/event_ds.dart @@ -12,6 +12,8 @@ import 'package:kepler/kepler.dart'; int gDebug = 0; + + // translate GoogleTranslator? translator; // initialized in main when argument given @@ -131,11 +133,12 @@ class EventData { } } - if( json['id'] == gCheckEventId) { + if( gDebug > 0 && json['id'] == gCheckEventId) { print("\n----------------------------------------Creating EventData with content: ${json['content']}"); print("In Event fromJson: got message: $gCheckEventId"); } + return EventData(json['id'] as String, json['pubkey'] as String, json['created_at'] as int, json['kind'] as int, json['content'].trim() as String, @@ -793,6 +796,8 @@ bool isWhitespace(String s) { } extension StringX on String { + + isChannelPageNumber(int max) { int? n = int.tryParse(this); diff --git a/lib/settings.dart b/lib/settings.dart index ae9f7c8..3c8ef8b 100644 --- a/lib/settings.dart +++ b/lib/settings.dart @@ -1,9 +1,10 @@ import 'package:logging/logging.dart'; + final log = Logger('ExampleLogger'); // for debugging -String gCheckEventId = ""; //"1763016774ceaa8c135dce01e77923994c5afad4cd3e126704a1292ebb1a577e"; //"15d86a36a620fc1f735f2322f31366b2adde786361f568faf6a0dc8368f7e534"; +String gCheckEventId = "d367bb6ed06c23238f1b0b68470d332c931d635b2cc85a577f487304563946ff"; //"1763016774ceaa8c135dce01e77923994c5afad4cd3e126704a1292ebb1a577e"; //"15d86a36a620fc1f735f2322f31366b2adde786361f568faf6a0dc8368f7e534"; const int gDefaultNumWaitSeconds = 2000; // is used in main() @@ -16,8 +17,8 @@ bool gOverWriteFile = false; // overwrite the file, and don't ju const int gDontAddToStoreBeforeDays = 60; // events older than this are not added to the Store of all events -const int gDaysToGetEventsFor = 70; // when getting events, this is the since field (unless a fully formed request is given in command line) -const int gLimitPerSubscription = 5000; +const int gDaysToGetEventsFor = 30; // when getting events, this is the since field (unless a fully formed request is given in command line) +const int gLimitPerSubscription = 6000; // don't show notifications for events that are older than 5 days and come when program is running // applicable only for notifications and not for search results. Search results set a flag in EventData and don't use this variable diff --git a/lib/tree_ds.dart b/lib/tree_ds.dart index 5bd5e3f..a70695f 100644 --- a/lib/tree_ds.dart +++ b/lib/tree_ds.dart @@ -1,3 +1,4 @@ +import 'dart:collection'; import 'dart:io'; import 'dart:convert'; import 'package:nostr_console/event_ds.dart'; @@ -5,16 +6,71 @@ import 'package:nostr_console/settings.dart'; typedef fTreeSelector = bool Function(Tree a); +Store? gStore = null; + bool selectAll(Tree t) { return true; } +int getLatestMessageTime(List _messageIds) { + if( _messageIds.length <= 0 || gStore == null) { + return 0; + } + + int latest = 0; + for(int i = 0; i < _messageIds.length; i++) { + if( gStore != null) { + Tree? tree = (gStore?.allChildEventsMap[_messageIds[i]] ); + if( tree != null) { + EventData ed = tree.event.eventData; + if( ed.createdAt > latest) { + latest = ed.createdAt; + } + } + } + } + + return latest; +} + +DirectMessageRoom? getDirectRoom(List rooms, String otherPubkey) { + for( int i = 0; i < rooms.length; i++) { + if( rooms[i].otherPubkey == otherPubkey) { + return rooms[i]; + } + } + return null; +} + + +int directRoomCompareTo(DirectMessageRoom a, DirectMessageRoom b) { + + if( gStore == null) + return 0; + + int otherLatest = getLatestMessageTime(b.messageIds); + int thisLatest = getLatestMessageTime(a.messageIds); + + if( thisLatest < otherLatest) { + return 1; + } else { + if( thisLatest == otherLatest) { + return 0; + } else { + return -1; + } + } + +} + + class ScrollableMessages { String topHeader; List messageIds; ScrollableMessages(this.topHeader, this.messageIds); + void printOnePage(Map tempChildEventsMap, [int page = 1]) { if( page < 1) { if( gDebug > 0) log.info("In ScrollableMessages::printOnepage got page = $page"); @@ -82,8 +138,47 @@ class DirectMessageRoom extends ScrollableMessages{ super ( "${getAuthorName(otherPubkey)} ($otherPubkey)", messageIds) { //print ("Created direct room with otherPubkey = $otherPubkey"); } + +void addMessageToDirectRoom(String messageId, Map tempChildEventsMap) { + int newEventTime = (tempChildEventsMap[messageId]?.event.eventData.createdAt??0); + + DirectMessageRoom room = this; + + 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; + +} + + + bool isPrivateMessageRoom() { + return false; + } + + void printDirectMessageRoom(Store store, [int page = 1]) { + if( page < 1) { + if( gDebug > 0) log.info("In printChannel got page = $page"); + page = 1; + } + printOnePage(store.allChildEventsMap, page); + } } + + class Tree { Event event; // is dummy for very top level tree. Holds an event otherwise. List children; // only has kind 1 events @@ -333,8 +428,9 @@ class Store { Map allChildEventsMap; // has events of kind typesInEventMap List eventsWithoutParent; + Map chatRooms = {}; - Map directRooms = {}; + List directRooms = []; Store(this.topPosts, this.allChildEventsMap, this.eventsWithoutParent, this.chatRooms, this.directRooms) { allChildEventsMap.forEach((eventId, tree) { @@ -406,7 +502,7 @@ class Store { } // end switch } - static void handleDirectMessages( Map directRooms, Map tempChildEventsMap, Event ce) { + static void handleDirectMessages( List directRooms, Map tempChildEventsMap, Event ce) { String eId = ce.eventData.id; int eKind = ce.eventData.kind; @@ -419,14 +515,25 @@ class Store { { String directRoomId = getDirectRoomId(ce.eventData); if( directRoomId != "") { - if( directRooms.containsKey(directRoomId)) { + + bool alreadyExists = false; + + int i = 0; + for(i = 0; i < directRooms.length; i++) { + if ( directRoomId == directRooms[i].otherPubkey) { + alreadyExists = true; + break; + } + } + + if( alreadyExists) { if( gDebug > 0) print("Adding new message ${ce.eventData.id} to a direct room $directRoomId sender pubkey = ${ce.eventData.pubkey}. "); - addMessageToDirectRoom(directRoomId, eId, tempChildEventsMap, directRooms); + directRooms[i].addMessageToDirectRoom( eId, tempChildEventsMap); } else { List temp = []; temp.add(eId); DirectMessageRoom newDirectRoom= DirectMessageRoom(directRoomId, temp); - directRooms[directRoomId] = newDirectRoom; + directRooms.add( newDirectRoom); if( gDebug > 0) print("Adding new message ${ce.eventData.id} to NEW direct room $directRoomId. sender pubkey = ${ce.eventData.pubkey}."); } } else { @@ -444,7 +551,9 @@ class Store { // 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( [], {}, [], {}, {}); + List temp = []; + + return Store( [], {}, [], {}, temp); } // create a map tempChildEventsMap from list of events, key is eventId and value is event itself @@ -465,7 +574,8 @@ 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= {}; + List tempDirectRooms = []; + int numEventsNotPosts = 0; // just for debugging info int numKind40Events = 0; @@ -623,9 +733,10 @@ class Store { //print("in insert events: got directRoomId = ${directRoomId}"); if( directRoomId != "") { - if( directRooms.containsKey(directRoomId)) { + DirectMessageRoom? room = getDirectRoom(directRooms, directRoomId); + if( room != null) { if( gDebug > 0) print("added event to direct room $directRoomId in insert event"); - addMessageToDirectRoom(directRoomId, newTree.event.eventData.id, allChildEventsMap, directRooms); + room.addMessageToDirectRoom(newTree.event.eventData.id, allChildEventsMap); newTree.event.eventData.isNotification = true; // highlight it too in next printing //print(" in from event: added it to a direct room"); break; @@ -634,7 +745,7 @@ class Store { List temp = []; temp.add(newTree.event.eventData.id); - directRooms[directRoomId] = DirectMessageRoom(directRoomId, temp); // TODO sort it + directRooms.add(DirectMessageRoom(directRoomId, temp)); // TODO sort it break; @@ -831,92 +942,6 @@ 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 = getAuthorName(key, 4); - - int numMessages = value.messageIds.length; - stdout.write("${name} ${getNumSpaces(32-name.length)} $numMessages${getNumSpaces(12- numMessages.toString().length)}"); - - // print latest event in one line - 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) { - String line = e.eventData.getAsLine(); - stdout.write(line); - break; // print only one event, the latest one - } - } - } - print(""); - }); - } - - // shows the given directRoomId, where directRoomId is prefix-id or pubkey of the other user. returns full id of other user. - String showDirectRoom(String directRoomId, [int page = 1]) { - //print("In show DirectRoom to show with id: $directRoomId"); - if( directRoomId.length > 64) { // TODO revisit cause if name is > 64 should not return - return ""; - } - Set lookedUpName = {}; - - // TODO improve lookup logic. - directRooms.forEach((roomId, directRoom) { - //print("looking up $directRoomId in $roomId ${directRoom.otherPubkey}"); - if( directRoomId == roomId) { - lookedUpName.add(roomId); - } - - //print("directRoom.otherPubkey = ${directRoom.otherPubkey} len = ${directRoom.otherPubkey.length}"); - if( directRoom.otherPubkey.substring(0, directRoomId.length) == directRoomId){ - lookedUpName.add(roomId); - } - - if( getAuthorName( directRoom.otherPubkey) == directRoomId){ - lookedUpName.add(roomId); - } - }); - - - if( lookedUpName.length == 1) { - DirectMessageRoom? room = directRooms[lookedUpName.first]; - if( room != null) { - printDirectMessageRoom(room, page); - return lookedUpName.first; - } else { - if( isValidPubkey(lookedUpName.first)) { - print("Could not find a conversation or room with the given id. Creating one with ${lookedUpName.first}"); - createDirectRoom(directRoomId); - return directRoomId; - } - } - } else { - if( lookedUpName.length > 0) - print("Got more than one public id for the name given, which are: ${lookedUpName.length}"); - else { - if( isValidPubkey(directRoomId)) { - print("Could not find a conversation or room with the given id. Creating one with $directRoomId"); - createDirectRoom(directRoomId); - return directRoomId; - } - } - return ""; - } - return ""; - } - - void createDirectRoom(String directRoomId) { - if( !directRooms.containsKey(directRoomId)) - directRooms[directRoomId] = DirectMessageRoom(directRoomId, []); - } - void printChannel(ChatRoom room, [int page = 1]) { if( page < 1) { if( gDebug > 0) log.info("In printChannel got page = $page"); @@ -926,16 +951,6 @@ class Store { room.printOnePage(allChildEventsMap, page); } - void printDirectMessageRoom(DirectMessageRoom directRoom, [int page = 1]) { - if( page < 1) { - if( gDebug > 0) log.info("In printChannel got page = $page"); - page = 1; - } - - directRoom.printOnePage(allChildEventsMap, page); - } - - // shows the given channelId, where channelId is prefix-id or channel name as mentioned in room.name. returns full id of channel. // looks for channelId in id first, then in names. String showChannel(String channelId, [int page = 1]) { @@ -983,6 +998,96 @@ class Store { return ""; } + /** + * @printDirectRoomInfo Print one line information about chat rooms + */ + void printDirectRoomInfo() { + directRooms.sort(directRoomCompareTo); + print("\n\nDirect messages inbox:"); + printUnderlined(" From Num of Messages Latest Message "); + for( int j = 0; j < directRooms.length; j++) { + + DirectMessageRoom room = directRooms[j]; + String name = getAuthorName(room.otherPubkey, 4); + + int numMessages = room.messageIds.length; + stdout.write("${name} ${getNumSpaces(32-name.length)} $numMessages${getNumSpaces(12- numMessages.toString().length)}"); + + // print latest event in one line + List messageIds = room.messageIds; + for( int i = messageIds.length - 1; i >= 0; i++) { + if( allChildEventsMap.containsKey(messageIds[i])) { + Event? e = allChildEventsMap[messageIds[i]]?.event; + if( e!= null) { + String line = e.eventData.getAsLine(); + stdout.write(line); + break; // print only one event, the latest one + } + } + } + print(""); + } + } + + // shows the given directRoomId, where directRoomId is prefix-id or pubkey of the other user. returns full id of other user. + String showDirectRoom( String directRoomId, [int page = 1]) { + //print("In show DirectRoom to show with id: $directRoomId"); + if( directRoomId.length > 64) { // TODO revisit cause if name is > 64 should not return + return ""; + } + Set lookedUpName = {}; + + // TODO improve lookup logic. + for( int j = 0; j < directRooms.length; j++) { + String roomId = directRooms[j].otherPubkey; + //print("looking up $directRoomId in $roomId ${directRoom.otherPubkey}"); + if( directRoomId == roomId) { + lookedUpName.add(roomId); + } + + //print("directRoom.otherPubkey = ${directRoom.otherPubkey} len = ${directRoom.otherPubkey.length}"); + if( directRooms[j].otherPubkey.substring(0, directRoomId.length) == directRoomId){ + lookedUpName.add(roomId); + } + + if( getAuthorName( directRooms[j].otherPubkey) == directRoomId){ + lookedUpName.add(roomId); + } + } + + + if( lookedUpName.length == 1) { + DirectMessageRoom? room = getDirectRoom(directRooms, lookedUpName.first); + if( room != null) { + room.printDirectMessageRoom(this, page); + return lookedUpName.first; + } else { + if( isValidPubkey(lookedUpName.first)) { + print("Could not find a conversation or room with the given id. Creating one with ${lookedUpName.first}"); + createDirectRoom( directRoomId); + return directRoomId; + } + } + } else { + if( lookedUpName.length > 0) + print("Got more than one public id for the name given, which are: ${lookedUpName.length}"); + else { + if( isValidPubkey(directRoomId)) { + print("Could not find a conversation or room with the given id. Creating one with $directRoomId"); + createDirectRoom(directRoomId); + return directRoomId; + } + } + return ""; + } + return ""; + } + + void createDirectRoom(String directRoomId) { + directRooms.add(DirectMessageRoom(directRoomId, [])); + } + + // Write the tree's events to file as one event's json per line Future writeEventsToFile(String filename) async { if( gDebug > 0) print("opening $filename to write to."); @@ -1256,8 +1361,8 @@ class Store { // the reactedTo event's id, blank if invalid reaction etc static String processReaction(Event event, Map tempChildEventsMap) { - if( gDebug > 0 && event.eventData.id == "e8a8a1f526af1023ba85ab3874d2310871e034eb8a0bcb3c289be671065ad03e") - print("in processReaction: 0 got reaction e8a8a1f526af1023ba85ab3874d2310871e034eb8a0bcb3c289be671065ad03e"); + if( gDebug > 0 && event.eventData.id == gCheckEventId) + print("in processReaction: 0 got reaction $gCheckEventId"); List validReactionList = ["+", "!"]; // TODO support opposite reactions List opppositeReactions = ['-', "~"]; @@ -1271,8 +1376,8 @@ class Store { int lastEIndex = event.eventData.eTags.length - 1; String reactedTo = event.eventData.eTags[lastEIndex]; - if( gDebug > 0 && event.eventData.id == "e8a8a1f526af1023ba85ab3874d2310871e034eb8a0bcb3c289be671065ad03e") - print("in processReaction: 1 got reaction e8a8a1f526af1023ba85ab3874d2310871e034eb8a0bcb3c289be671065ad03e"); + if( gDebug > 0 && event.eventData.id == gCheckEventId) + print("in processReaction: 1 got reaction $gCheckEventId"); if( !validReactionList.any((element) => element == comment)) { return ""; @@ -1368,41 +1473,6 @@ 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) { @@ -1439,7 +1509,9 @@ int sortTreeNewestReply(Tree a, Tree b) { Store getTree(Set events) { if( events.isEmpty) { if(gDebug > 0) log.info("Warning: In printEventsAsTree: events length = 0"); - return Store([], {}, [], {}, {}); + + List temp =[]; + return Store([], {}, [], {}, temp); } // remove all events other than kind 0 (meta data), 1(posts replies likes), 3 (contact list), 7(reactions), 40 and 42 (chat rooms)