diff --git a/lib/console_ui.dart b/lib/console_ui.dart index 42bd09a..a5c43bf 100644 --- a/lib/console_ui.dart +++ b/lib/console_ui.dart @@ -30,6 +30,7 @@ Future processAnyIncomingEvents(Store node, [bool printNotifications = tru List numPrinted1 = [0,0,0]; if( printNotifications) { + // print all the new trees, the ones that we want to print numPrinted1 = node.printTreeNotifications(newEventIds); // need to clear because only top 20 events in each thread are printed or cleared with above @@ -303,7 +304,7 @@ void printProfile(Store node, String profilePubkey) { node.printStoreTrees(0, DateTime.now().subtract(Duration(hours:gHoursDefaultPrint)), onlyUserPostAndLike); // if contact list was found, get user's feed, and keep the contact list for later use - String authorName = getAuthorName(profilePubkey, addTickForWellKnown: false ); + String authorName = getAuthorName(profilePubkey); String pronoun = ""; if( profilePubkey == userPublicKey) { printUnderlined("\nYour profile - $authorName:"); @@ -1280,9 +1281,9 @@ Future socialMenuUi(Store node) async { // the main menu int option = showMenu([ - 'All Posts', // 1 + 'Your Feed', // 1 'Post/Reply/Like', // 2 - 'Your notifications',// 3 + 'Replies/Likes to you',// 3 'Your Posts', // 4 'Your Replies/Likes',//5 'Follows\' Posts/Replies/Likes', // 6 @@ -1295,7 +1296,8 @@ Future socialMenuUi(Store node) async { switch(option) { case 1: - node.printStoreTrees(0, DateTime.now().subtract(Duration(hours:gHoursDefaultPrint)), selectorTrees_all); + bool selectorTrees_followActionsNoNotifications (Tree t) => t.treeSelectorUserPostAndLike(getFollows( userPublicKey), enableNotifications: false); + node.printStoreTrees(0, DateTime.now().subtract(Duration(hours:gHoursDefaultPrint)), selectorTrees_followActionsNoNotifications); await processAnyIncomingEvents(node, true); break; @@ -1341,9 +1343,9 @@ Future socialMenuUi(Store node) async { int notificationHours = gHoursDefaultPrint>24? gHoursDefaultPrint: 24; // minimum 24 List numPrinted = node.printStoreTrees(0, DateTime.now().subtract(Duration(hours:notificationHours)), selectorTrees_userNotifications); if( numPrinted[2] > 0) { - print("Showed ${numPrinted[2]} notifications.\n"); + print("Showed ${numPrinted[2]} replies/likes that were made to your posts.\n"); } else { - print("No notifications."); + print("No replies or likes."); } await processAnyIncomingEvents(node, true); @@ -1351,8 +1353,8 @@ Future socialMenuUi(Store node) async { case 4: clearScreen(); List numPrinted = node.printStoreTrees(0, DateTime.now().subtract(Duration(hours:gHoursDefaultPrint)), selectorTrees_selfPosts); - if( numPrinted[2] > 0) { - print("Showed ${numPrinted[2]} posts made by you in last $gHoursDefaultPrint hours.\n"); + if( numPrinted[0] > 0) { + print("Showed ${numPrinted[0]} posts made by you in last $gHoursDefaultPrint hours.\n"); } else { print("No posts made by you in last $gHoursDefaultPrint hours."); } @@ -1373,8 +1375,8 @@ Future socialMenuUi(Store node) async { case 6: clearScreen(); - bool selectorTrees_followActions (Tree t) => t.treeSelectorUserPostAndLike(getFollows( userPublicKey)); - List numPrinted = node.printStoreTrees(0, DateTime.now().subtract(Duration(hours:gHoursDefaultPrint)), selectorTrees_followActions); + bool selectorTrees_followActionsWithNotifications (Tree t) => t.treeSelectorUserPostAndLike(getFollows( userPublicKey), enableNotifications: true); + List numPrinted = node.printStoreTrees(0, DateTime.now().subtract(Duration(hours:gHoursDefaultPrint)), selectorTrees_followActionsWithNotifications); if( numPrinted[0] > 0) { print("Showed ${numPrinted[0]} threads where your follows participated.\n"); } else { @@ -1580,8 +1582,8 @@ Future mainMenuUi(Store node) async { firstTime = false; // the main menu - int option = showMenu(['Home Page', // 1 - 'Social Network', // 2 + int option = showMenu(['Global Feed', // 1 + 'Social Network', // 2 'Public Channels', // 3 'Encrypted Channels',// 4 'Private Messages', // 5 diff --git a/lib/event_ds.dart b/lib/event_ds.dart index 5eef2c7..b4d2f18 100644 --- a/lib/event_ds.dart +++ b/lib/event_ds.dart @@ -4,6 +4,7 @@ import 'dart:math'; import 'package:bip340/bip340.dart'; import 'package:intl/intl.dart'; import 'package:nostr_console/tree_ds.dart'; +import 'package:nostr_console/user.dart'; import 'package:nostr_console/utils.dart'; import 'package:translator/translator.dart'; import 'package:crypto/crypto.dart'; @@ -1246,7 +1247,12 @@ String getNip05Name( String pubkey) { } // returns name by looking up global list gKindONames, which is populated by kind 0 events -String getAuthorName(String pubkey, {bool addTickForWellKnown = true, int maxDisplayLen = gMaxInteger, int pubkeyLenShown = 5}) { +String getAuthorName(String pubkey, {int maxDisplayLen = gMaxInteger, int pubkeyLenShown = 5}) { + + if( gFollowList.length == 0) { + gFollowList = getFollows(userPublicKey); + } + bool isFollow = gFollowList.contains(pubkey) && (pubkey != userPublicKey); String maxLen(String pubkey) => pubkey.length > pubkeyLenShown? pubkey.substring(0,pubkeyLenShown) : pubkey.substring(0, pubkey.length); String name = ""; @@ -1256,19 +1262,19 @@ String getAuthorName(String pubkey, {bool addTickForWellKnown = true, int maxDis name = (gKindONames[pubkey]?.name)??maxLen(pubkey); } - if( addTickForWellKnown) { - // first remove the check mark if its in any name + // then add valid check mark in default follows + if( isFollow) { + if( name.length >= maxDisplayLen ) { + name = name.substring(0, maxDisplayLen-1) + gValidCheckMark; + } else { + name = name + gValidCheckMark; + } + } else { + // make this tick a specil character name = name.replaceAll(gValidCheckMark, ""); - // then add valid check mark in default follows - if( gDefaultFollows.contains(pubkey)) { - if( name.length >= maxDisplayLen ) { - name = name.substring(0, maxDisplayLen-1) + gValidCheckMark; - } else { - name = name + gValidCheckMark; - } - } } + return name; } diff --git a/lib/settings.dart b/lib/settings.dart index b211d8d..43d0a6d 100644 --- a/lib/settings.dart +++ b/lib/settings.dart @@ -3,7 +3,7 @@ import 'package:logging/logging.dart'; // name of executable const String exename = "nostr_console"; -const String version = "0.3.1-beta-b"; +const String version = "0.3.2-beta"; int gDebug = 0; int gSpecificDebug = 0; diff --git a/lib/tree_ds.dart b/lib/tree_ds.dart index c7a13e6..6a5dc97 100644 --- a/lib/tree_ds.dart +++ b/lib/tree_ds.dart @@ -559,8 +559,39 @@ class Tree { return false; } - // returns true if the tree or its children has a post or like by user; and notification flags are set for such events - bool treeSelectorUserPostAndLike(Set pubkeys) { + // returns true if the tree has a reply by any of the pubkeys sent + // only used by writefile + bool treeSelectorUserPosted(Set pubkeys, [bool checkChildrenToo = false]) { + + if( pubkeys.contains(event.eventData.pubkey)) { + return true; + } + + for( int i = 0; i < children.length; i++ ) { + if( children[i].treeSelectorUserPosted(pubkeys)) { + return true; + } + } + + return false; + } + + // returns true if the tree has a reply by any of the pubkeys sent + // only used by writefile + bool treeSelectorUserReplies(Set pubkeys) { + + for( int i = 0; i < children.length; i++ ) { + if( children[i].treeSelectorUserPosted(pubkeys)) { + return true; + } + } + + return false; + } + + + // returns true if the tree (or its children, depending on flag) has a post or like by user; and notification flags are set for such events + bool treeSelectorUserPostAndLike(Set pubkeys, { bool enableNotifications = true, bool checkChildrenToo = true}) { bool hasReacted = false; if( gReactions.containsKey(event.eventData.id)) { @@ -568,7 +599,8 @@ class Tree { if( reactions != null) { for( int i = 0; i < reactions.length; i++) { if( pubkeys.contains(reactions[i][0]) ) { - event.eventData.newLikes.add(reactions[i][0]); + if( enableNotifications) + event.eventData.newLikes.add(reactions[i][0]); hasReacted = true; } } @@ -576,15 +608,19 @@ class Tree { } bool childMatches = false; - for( int i = 0; i < children.length; i++ ) { - if( children[i].treeSelectorUserPostAndLike(pubkeys)) { - childMatches = true; + + if( checkChildrenToo ) { + for( int i = 0; i < children.length; i++ ) { + if( children[i].treeSelectorUserPostAndLike(pubkeys)) { + childMatches = true; + } } } // if event is by user(s) if( pubkeys.contains(event.eventData.pubkey)) { - event.eventData.isNotification = true; + if( enableNotifications) + event.eventData.isNotification = true; return true; } if( hasReacted || childMatches) { @@ -1605,13 +1641,19 @@ class Store { Store.reCalculateMarkerStr(); + // update this list, because it is internally used by printEvent + gFollowList = getFollows(userPublicKey); + List ret = [0,0,0]; - topNotificationTree.forEach( (t) { - List temp = Store.printTopPost(t, 0, DateTime(0)); - ret[0] += temp[0]; - ret[1] += temp[1]; - ret[2] += temp[2]; - print("\n"); + topNotificationTree.forEach( (t) { + bool selectorTrees_followActionsWithNotifications (Tree t) => t.treeSelectorUserPostAndLike(getFollows( userPublicKey), enableNotifications: true); + if( selectorTrees_followActionsWithNotifications(t)) { + List temp = Store.printTopPost(t, 0, DateTime(0)); + ret[0] += temp[0]; + ret[1] += temp[1]; + ret[2] += temp[2]; + print("\n"); + } }); return ret; @@ -1642,6 +1684,9 @@ class Store { */ List printStoreTrees(int depth, DateTime newerThan, fTreeSelector treeSelector, [int maxToPrint = gMaxEventsInThreadPrinted]) { + // update this list, because it is internally used by printEvent + gFollowList = getFollows(userPublicKey); + topPosts.sort(sortTreeNewestReply); // sorting done only for top most threads. Lower threads aren't sorted so save cpu etc TODO improve top sorting // https://gist.github.com/dsample/79a97f38bf956f37a0f99ace9df367b9 @@ -2046,18 +2091,26 @@ class Store { return room; } - - // TODO to be finished + // threads where the user and follows have involved themselves are returnes as true ( relevant) bool isRelevant(Tree tree) { - //Set contacts = getContactList(userPublicKey); - //contacts = contacts.union(gDefaultFollows); + if( tree.treeSelectorUserPostAndLike(gFollowList) + || tree.treeSelectorUserPostAndLike({userPublicKey}) + || tree.treeSelectorUserReplies(gFollowList)) { + return true; + } - return true; + return false; } // Write the tree's events to file as one event's json per line Future writeEventsToFile(String filename) async { + + // this variable will be used later; update it if needed + if( gFollowList.length == 0) { + gFollowList = getFollows(userPublicKey); + } + if( gDebug > 0) print("opening $filename to write to."); try { final File file = File(filename); @@ -2075,7 +2128,9 @@ class Store { int linesWritten = 0; for( var tree in allChildEventsMap.values) { - if( tree.event.eventData.isDeleted) { // dont write those deleted + if( tree.event.eventData.isDeleted // dont write those deleted + || gDummyAccountPubkey == tree.event.eventData.pubkey // dont write dummy events + || tree.event.originalJson.length < 10) { continue; } @@ -2085,18 +2140,9 @@ class Store { } } - if( gDummyAccountPubkey == tree.event.eventData.pubkey) { - continue; // dont write dummy events - } - - if( tree.event.originalJson.length < 10) { - continue; - } - if( !isRelevant(tree)) { continue; } - String temp = tree.event.originalJson.trim(); String line = "${temp}\n"; diff --git a/lib/user.dart b/lib/user.dart index 80e9e84..b8e048e 100644 --- a/lib/user.dart +++ b/lib/user.dart @@ -3,6 +3,10 @@ import 'package:nostr_console/event_ds.dart'; import 'package:nostr_console/settings.dart'; import 'package:nostr_console/utils.dart'; + +// is set intermittently by functions. and used as required. Should be kept in sync as the kind 3 for user are received. +Set gFollowList = {}; + // From the list of events provided, lookup the lastst contact information for the given user/pubkey Event? getContactEvent(String pubkey) { @@ -28,6 +32,7 @@ Set getFollows(String pubkey) { return followPubkeys; } + Set getUserChannels(Set userEvents, String userPublicKey) { Set userChannels = {}; @@ -94,21 +99,3 @@ Set getOnlyUserEvents(Set initialEvents, String userPubkey) { return userEvents; } - -Set getContactList(String pubkey) { - Set contacts = {}; - - if( pubkey != "") { - // get the latest kind 3 event for the user, which has the 'follows' list - Event? contactEvent = getContactEvent(userPublicKey); - - // if contact list was found, get user's feed; also get some default contacts - if (contactEvent != null ) { - contactEvent.eventData.contactList.forEach((contact) { - contacts.add(contact.contactPubkey); - }); - } else { - } - } - return contacts; -} \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 74da5e9..8b3bddc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,12 +1,17 @@ name: nostr_console description: A multi-platform nostr client built for terminal/console -version: 0.3.1-beta-b +version: 0.3.2-beta homepage: https://github.com/vishalxl/nostr_console - + +# 0.3.2 # added build for ubuntu arm 64, and mac arm 64 -# matrix change , removed old build entries in dart.yml files -# one off build with num channel messages = 40 +# fixed or improved mention expansion +# displyed global feed. which has all latest in last 2 hours +# in incoming notifications, only showed notifications for follows. +# In writing events, only writing follow's events. and the ones they interact with. +# now friends have a tick; no tick for defaults +# fixed likes colors issue for notification likes # 0.3.1 # added nostr.ch as another default relay to sync with anigma