support for delete and hiding, fixed and new code

This commit is contained in:
Vishal
2022-09-03 21:18:50 +05:30
parent e21cf63d49
commit 2d070cc8ce
4 changed files with 150 additions and 103 deletions

View File

@@ -81,8 +81,8 @@ nostr_console.exe --file=eventsFile.txt --prikey=K
![Showing Tree with re-shifting to left](https://pbs.twimg.com/media/Fao3E1bUUAAIti1?format=png&name=4096x4096) showing re-alignment of threads for easier reading. ![Showing Tree with re-shifting to left](https://pbs.twimg.com/media/Fao3E1bUUAAIti1?format=png&name=4096x4096) showing re-alignment of threads for easier reading.
# Contact
[Nostr Telegram Channel](https://t.me/nostr_protocol) or at Nostr Pulic Channel 52cab2e3e504ad6447d284b85b5cc601ca0613b151641e77facfec851c2ca816

View File

@@ -673,6 +673,9 @@ Future<void> mainMenuUi(Store node) async {
if( content == "+") { if( content == "+") {
print("Sending a like to given post."); print("Sending a like to given post.");
replyKind = "7"; replyKind = "7";
} else if( content == "!") {
print("Hiding the given post.");
replyKind = "7";
} }
await sendReplyPostLike(node, replyToId, replyKind, content); await sendReplyPostLike(node, replyToId, replyKind, content);

View File

@@ -50,7 +50,7 @@ class EventData {
int createdAt; int createdAt;
int kind; int kind;
String content; String content;
List<String> eTagsRest;// rest of e tags List<String> eTags;// rest of e tags
List<String> pTags;// list of p tags for kind:1 List<String> pTags;// list of p tags for kind:1
List<List<String>> tags; List<List<String>> tags;
bool isNotification; // whether its to be highlighted using highlight color bool isNotification; // whether its to be highlighted using highlight color
@@ -63,13 +63,13 @@ class EventData {
bool isDeleted; // deleted by kind 5 event bool isDeleted; // deleted by kind 5 event
String getParent() { String getParent() {
if( eTagsRest.isNotEmpty) { if( eTags.isNotEmpty) {
return eTagsRest[eTagsRest.length - 1]; return eTags[eTags.length - 1];
} }
return ""; return "";
} }
EventData(this.id, this.pubkey, this.createdAt, this.kind, this.content, this.eTagsRest, this.pTags, EventData(this.id, this.pubkey, this.createdAt, this.kind, this.content, this.eTags, this.pTags,
this.contactList, this.tags, this.newLikes, {this.isNotification = false, this.evaluatedContent = "", this.isHidden = false, this.isDeleted = false}); this.contactList, this.tags, this.newLikes, {this.isNotification = false, this.evaluatedContent = "", this.isHidden = false, this.isDeleted = false});
factory EventData.fromJson(dynamic json) { factory EventData.fromJson(dynamic json) {
@@ -205,19 +205,15 @@ class EventData {
if(!isUserDirectMessage(this)) { if(!isUserDirectMessage(this)) {
break; break;
} }
//print("in translateAndExpandMentions() for a dm \npubkey = $pubkey \ncontent = $content");
//print("tags = $tags\n");
int ivIndex = content.indexOf("?iv="); int ivIndex = content.indexOf("?iv=");
var enc_str = content.substring(0, ivIndex); var enc_str = content.substring(0, ivIndex);
var iv = content.substring( ivIndex + 4, content.length); var iv = content.substring( ivIndex + 4, content.length);
//print("enc_str = $enc_str ; iv = $iv");
String userKey = userPrivateKey ; String userKey = userPrivateKey ;
String otherUserPubKey = "02" + pubkey; String otherUserPubKey = "02" + pubkey;
if( pubkey == userPublicKey) { if( pubkey == userPublicKey) {
userKey = userPrivateKey; userKey = userPrivateKey;
otherUserPubKey = "02" + pubkey; otherUserPubKey = "02" + pubkey;
//iv = "";
} }
var decryptd = myPrivateDecrypt( userKey, otherUserPubKey, enc_str, iv); // use bob's privatekey and alic's publickey means bob can read message from alic var decryptd = myPrivateDecrypt( userKey, otherUserPubKey, enc_str, iv); // use bob's privatekey and alic's publickey means bob can read message from alic
evaluatedContent = decryptd; evaluatedContent = decryptd;
@@ -236,28 +232,41 @@ class EventData {
// prints event data in the format that allows it to be shown in tree form by the Tree class // prints event data in the format that allows it to be shown in tree form by the Tree class
void printEventData(int depth) { void printEventData(int depth) {
if( id == gCheckEventId) { if( !(kind == 1 || kind == 4 || kind == 42)) {
if(gDebug > 0) { return; // only print kind 1 and 42 and 4
print("In Event printEventData: got message: $gCheckEventId");
isNotification = true;
}
} }
int n = 4; int n = 4;
String maxN(String v) => v.length > n? v.substring(0,n) : v.substring(0, v.length); String maxN(String v) => v.length > n? v.substring(0,n) : v.substring(0, v.length);
void printInColor(String s, String commentColor) => stdout.supportsAnsiEscapes ?stdout.write("$commentColor$s$gColorEndMarker"):stdout.write(s); void printInColor(String s, String commentColor) => stdout.supportsAnsiEscapes ?stdout.write("$commentColor$s$gColorEndMarker"):stdout.write(s);
String name = getAuthorName(pubkey);
String strDate = getPrintableDate(createdAt); String strDate = getPrintableDate(createdAt);
String tempEvaluatedContent = evaluatedContent;
String tempContent = content;
if( isHidden) {
name = "<hidden>";
strDate = "<hidden>";
tempEvaluatedContent = tempContent = "<You have hidden this post>";
}
// delete supercedes hidden
if( isDeleted) {
name = "<deleted>";
strDate = "<deleted>";
tempEvaluatedContent = tempContent = content; // content would be changed so show that
}
if( createdAt == 0) { if( createdAt == 0) {
print("debug: createdAt == 0 for event $content"); print("debug: createdAt == 0 for event $content");
} }
String contentShifted = rightShiftContent(evaluatedContent==""?content: evaluatedContent, gSpacesPerDepth * depth + 10); String contentShifted = rightShiftContent(tempEvaluatedContent==""?tempContent: tempEvaluatedContent, gSpacesPerDepth * depth + 10);
printDepth(depth); printDepth(depth);
stdout.write("+-------+\n"); stdout.write("+-------+\n");
printDepth(depth); printDepth(depth);
String name = getAuthorName(pubkey);
stdout.write("|Author : $name id: ${maxN(id)} Time: $strDate\n"); stdout.write("|Author : $name id: ${maxN(id)} Time: $strDate\n");
printReaction(depth); // only prints if there are any likes/reactions printReaction(depth); // only prints if there are any likes/reactions
printDepth(depth); printDepth(depth);
@@ -283,24 +292,32 @@ class EventData {
void printReaction(int depth) { void printReaction(int depth) {
if( gReactions.containsKey(id)) { if( gReactions.containsKey(id)) {
String reactorNames = "|Likes : "; String reactorNames = "|Likes : ";
printDepth(depth);
//print("All Likes:");
int numReactions = gReactions[id]?.length??0; int numReactions = gReactions[id]?.length??0;
List<List<String>> reactors = gReactions[id]??[]; List<List<String>> reactors = gReactions[id]??[];
bool firstEntry = true;
for( int i = 0; i <numReactions; i++) { for( int i = 0; i <numReactions; i++) {
String comma = (firstEntry)?"":", ";
String reactorId = reactors[i][0]; String reactorId = reactors[i][0];
if( newLikes.contains(reactorId)) { if( newLikes.contains(reactorId) && reactors[i][1] == "+") {
// colorify // this is a notifications, print it and then later empty newLikes
reactorNames += gNotificationColor + getAuthorName(reactorId) + gColorEndMarker; reactorNames += gNotificationColor + getAuthorName(reactorId) + gColorEndMarker + comma;
firstEntry = false;
} else { } else {
// this is normal printing of the reaction. only print for + for now
if( reactors[i][1] == "+")
reactorNames += getAuthorName(reactorId); reactorNames += getAuthorName(reactorId);
firstEntry = false;
} }
if( i < numReactions -1) { } // end for
reactorNames += ", ";
} if( !isHidden && !isDeleted) {
} printDepth(depth);
print(reactorNames); print(reactorNames);
} else {
}
newLikes.clear(); newLikes.clear();
} }
} }

View File

@@ -65,7 +65,8 @@ class ChatRoom extends ScrollableMessages {
String picture; String picture;
ChatRoom(this.chatRoomId, this.internalChatRoomName, this.about, this.picture, List<String> messageIds) : ChatRoom(this.chatRoomId, this.internalChatRoomName, this.about, this.picture, List<String> messageIds) :
super ( "${internalChatRoomName} ( ${chatRoomId.substring(0, 6)}", messageIds); super ( internalChatRoomName.isEmpty? chatRoomId: internalChatRoomName + "( " + chatRoomId + " )" ,
messageIds);
String get chatRoomName { String get chatRoomName {
return internalChatRoomName; return internalChatRoomName;
@@ -459,6 +460,9 @@ class Store {
processDeleteEvents(tempChildEventsMap); // handle returned values perhaps later processDeleteEvents(tempChildEventsMap); // handle returned values perhaps later
processReactions(events, tempChildEventsMap);
// once tempChildEventsMap has been created, create connections between them so we get a tree structure from all these events. // once tempChildEventsMap has been created, create connections between them so we get a tree structure from all these events.
List<Tree> topLevelTrees = [];// this will become the children of the main top node. These are events without parents, which are printed at top. List<Tree> topLevelTrees = [];// this will become the children of the main top node. These are events without parents, which are printed at top.
List<String> tempWithoutParent = []; List<String> tempWithoutParent = [];
@@ -480,14 +484,13 @@ class Store {
handleDirectMessages(tempDirectRooms, tempChildEventsMap, tree.event); handleDirectMessages(tempDirectRooms, tempChildEventsMap, tree.event);
} }
// only posts, of kind 1, are added to the main tree structure // only posts, of kind 1, are added to the main tree structure
if( eKind != 1) { if( eKind != 1) {
numEventsNotPosts++; numEventsNotPosts++;
return; return;
} }
if(tree.event.eventData.eTagsRest.isNotEmpty ) { if(tree.event.eventData.eTags.isNotEmpty ) {
// is not a parent, find its parent and then add this element to that parent Tree // is not a parent, find its parent and then add this element to that parent Tree
String parentId = tree.event.eventData.getParent(); String parentId = tree.event.eventData.getParent();
if( tree.event.eventData.id == gCheckEventId) { if( tree.event.eventData.id == gCheckEventId) {
@@ -518,7 +521,7 @@ class Store {
// add parent trees as top level child trees of this tree // add parent trees as top level child trees of this tree
for( var tree in tempChildEventsMap.values) { for( var tree in tempChildEventsMap.values) {
if( tree.event.eventData.kind == 1 && tree.event.eventData.eTagsRest.isEmpty) { // only posts which are parents if( tree.event.eventData.kind == 1 && tree.event.eventData.eTags.isEmpty) { // only posts which are parents
topLevelTrees.add(tree); topLevelTrees.add(tree);
} }
} }
@@ -550,7 +553,8 @@ class Store {
// handle reaction events and return if we could not find the reacted to. Continue otherwise to add this to notification set newEventIdsSet // handle reaction events and return if we could not find the reacted to. Continue otherwise to add this to notification set newEventIdsSet
if( newEvent.eventData.kind == 7) { if( newEvent.eventData.kind == 7) {
if( processReaction(newEvent) == "") { //print("going to call processRreactin");
if( processReaction(newEvent, allChildEventsMap) == "") {
if(gDebug > 0) print("In insertEvents: For new reaction ${newEvent.eventData.id} could not find reactedTo or reaction was already present by this reactor"); if(gDebug > 0) print("In insertEvents: For new reaction ${newEvent.eventData.id} could not find reactedTo or reaction was already present by this reactor");
return; return;
} }
@@ -598,7 +602,7 @@ class Store {
switch(newTree.event.eventData.kind) { switch(newTree.event.eventData.kind) {
case 1: case 1:
// only kind 1 events are added to the overall tree structure // only kind 1 events are added to the overall tree structure
if( newTree.event.eventData.eTagsRest.isEmpty) { if( newTree.event.eventData.eTags.isEmpty) {
// if its a new parent event, then add it to the main top parents ( this.children) // if its a new parent event, then add it to the main top parents ( this.children)
topPosts.add(newTree); topPosts.add(newTree);
} else { } else {
@@ -719,17 +723,21 @@ class Store {
break; break;
case 7: case 7:
Event event = t.event; Event event = t.event;
if(gDebug >= 0) ("Got notification of type 7"); if(gDebug > 0) ("Got notification of type 7");
String reactorId = event.eventData.pubkey; String reactorId = event.eventData.pubkey;
int lastEIndex = event.eventData.eTagsRest.length - 1; int lastEIndex = event.eventData.eTags.length - 1;
String reactedTo = event.eventData.eTagsRest[lastEIndex]; String reactedTo = event.eventData.eTags[lastEIndex];
Event? reactedToEvent = allChildEventsMap[reactedTo]?.event; Event? reactedToEvent = allChildEventsMap[reactedTo]?.event;
if( reactedToEvent != null) { if( reactedToEvent != null) {
Tree? reactedToTree = allChildEventsMap[reactedTo]; Tree? reactedToTree = allChildEventsMap[reactedTo];
if( reactedToTree != null) { if( reactedToTree != null) {
if(event.eventData.content == "+" ) {
reactedToTree.event.eventData.newLikes.add( reactorId); reactedToTree.event.eventData.newLikes.add( reactorId);
Tree topTree = getTopTree(reactedToTree); Tree topTree = getTopTree(reactedToTree);
topNotificationTree.add(topTree); topNotificationTree.add(topTree);
} else if(event.eventData.content == "!" ) {
reactedToTree.event.eventData.isHidden = true;
}
} else { } else {
if(gDebug > 0) print("Could not find reactedTo tree"); if(gDebug > 0) print("Could not find reactedTo tree");
} }
@@ -961,7 +969,7 @@ class Store {
int linesWritten = 0; int linesWritten = 0;
for( var tree in allChildEventsMap.values) { for( var tree in allChildEventsMap.values) {
if( tree.event.readFromFile) { // ignore those already in file; only the new ones are writen/appended to file if( tree.event.readFromFile || tree.event.eventData.isDeleted) { // ignore those already in file; only the new ones are writen/appended to file, or those deleted
continue; continue;
} }
@@ -1161,7 +1169,7 @@ class Store {
if( deletedEvent != null) { if( deletedEvent != null) {
if( deletedEvent.eventData.kind == 1 && deletedEvent.eventData.pubkey == deleterEvent.eventData.pubkey) { if( deletedEvent.eventData.kind == 1 && deletedEvent.eventData.pubkey == deleterEvent.eventData.pubkey) {
deletedEvent.eventData.isDeleted = true; deletedEvent.eventData.isDeleted = true;
deletedEvent.eventData.content = gDeletedEventMessage + " on ${getPrintableDate(deleterEvent.eventData.createdAt)}"; deletedEvent.eventData.content = gDeletedEventMessage;
deletedEvent.eventData.evaluatedContent = ""; deletedEvent.eventData.evaluatedContent = "";
EventData ed = deletedEvent.eventData; EventData ed = deletedEvent.eventData;
deletedEvent.originalJson = '["EVENT","none",{"id":${ed.id},"pubkey":${ed.pubkey},"createdAt":${ed.createdAt},"kind":1,"tags":[],"sig":"invalid","comment":"deleted"}]'; deletedEvent.originalJson = '["EVENT","none",{"id":${ed.id},"pubkey":${ed.pubkey},"createdAt":${ed.createdAt},"kind":1,"tags":[],"sig":"invalid","comment":"deleted"}]';
@@ -1201,7 +1209,80 @@ class Store {
return foundEventIds; return foundEventIds;
} }
} // end Store // for the given reaction event of kind 7, will update the global gReactions appropriately, returns
// the reactedTo event's id, blank if invalid reaction etc
static String processReaction(Event event, Map<String, Tree> tempChildEventsMap) {
if( gDebug > 0 && event.eventData.id == "e8a8a1f526af1023ba85ab3874d2310871e034eb8a0bcb3c289be671065ad03e")
print("in processReaction: 0 got reaction e8a8a1f526af1023ba85ab3874d2310871e034eb8a0bcb3c289be671065ad03e");
List<String> validReactionList = ["+", "!"]; // TODO support opposite reactions
List<String> opppositeReactions = ['-', "~"];
if( event.eventData.kind == 7
&& event.eventData.eTags.isNotEmpty) {
if(gDebug > 1) ("Got event of type 7"); // this can be + or !, which means 'hide' event for me
String reactorPubkey = event.eventData.pubkey;
String comment = event.eventData.content;
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( !validReactionList.any((element) => element == comment)) {
return "";
}
// check if the reaction already exists by this user
if( gReactions.containsKey(reactedTo)) {
for( int i = 0; i < ((gReactions[reactedTo]?.length)??0); i++) {
List<String> oldReaction = (gReactions[reactedTo]?[i])??[];
if( oldReaction.length == 2) {
//valid reaction
if(oldReaction[0] == reactorPubkey && oldReaction[1] == comment) {
return ""; // reaction by this user already exists so return
}
}
}
List<String> temp = [reactorPubkey, comment];
gReactions[reactedTo]?.add(temp);
} else {
// first reaction to this event, create the entry in global map
List<List<String>> newReactorList = [];
List<String> temp = [reactorPubkey, comment];
newReactorList.add(temp);
gReactions[reactedTo] = newReactorList;
}
// set isHidden for reactedTo if it exists in map
if( gDebug > 0 && event.eventData.id == "e8a8a1f526af1023ba85ab3874d2310871e034eb8a0bcb3c289be671065ad03e")
print("in processReaction: 2 got reaction e8a8a1f526af1023ba85ab3874d2310871e034eb8a0bcb3c289be671065ad03e");
if( comment == "!" && event.eventData.pubkey == userPublicKey) {
tempChildEventsMap[reactedTo]?.event.eventData.isHidden = true;
}
return reactedTo;
} else {
// case where its not a kind 7 event, or we can't find the reactedTo event due to absense of e tag.
}
return "";
}
// will go over the list of events, and update the global gReactions appropriately
static void processReactions(Set<Event> events, Map<String, Tree> tempChildEventsMap) {
print("in processReactions");
for (Event event in events) {
processReaction(event, tempChildEventsMap);
}
return;
}
} //================================================================================================================================ end Store
void addMessageToChannel(String channelId, String messageId, Map<String, Tree> tempChildEventsMap, var chatRooms) { void addMessageToChannel(String channelId, String messageId, Map<String, Tree> tempChildEventsMap, var chatRooms) {
int newEventTime = (tempChildEventsMap[messageId]?.event.eventData.createdAt??0); int newEventTime = (tempChildEventsMap[messageId]?.event.eventData.createdAt??0);
@@ -1309,62 +1390,6 @@ int sortTreeNewestReply(Tree a, Tree b) {
} }
} }
// for the given reaction event of kind 7, will update the global gReactions appropriately, returns
// the reactedTo event's id, blank if invalid reaction etc
String processReaction(Event event) {
if( event.eventData.kind == 7
&& event.eventData.eTagsRest.isNotEmpty) {
if(gDebug > 1) ("Got event of type 7"); // this can be + or !, which means 'hide' event for me
String reactorId = event.eventData.pubkey;
String comment = event.eventData.content;
int lastEIndex = event.eventData.eTagsRest.length - 1;
String reactedTo = event.eventData.eTagsRest[lastEIndex];
if( event.eventData.content == "+") {
if( gReactions.containsKey(reactedTo)) {
// check if the reaction already exists by this user
for( int i = 0; i < ((gReactions[reactedTo]?.length)??0); i++) {
List<String> oldReaction = (gReactions[reactedTo]?[i])??[];
if( oldReaction.length == 2) {
//valid reaction
if(oldReaction[0] == reactorId) {
return ""; // reaction by this user already exists so return
}
}
}
List<String> temp = [reactorId, comment];
gReactions[reactedTo]?.add(temp);
} else {
// first reaction + to this event, create the entry in global map
List<List<String>> newReactorList = [];
List<String> temp = [reactorId, comment];
newReactorList.add(temp);
gReactions[reactedTo] = newReactorList;
}
} else {
if( event.eventData.content == "!") {
//reactedTo needs to ve hidden if we have it in the main tree map
// Tree? treeReactedTo =
}
}
return reactedTo;
} else {
// case where its not a kind 7 event, or we can't find the reactedTo event due to absense of e tag.
}
return "";
}
// will go over the list of events, and update the global gReactions appropriately
void processReactions(Set<Event> events) {
for (Event event in events) {
processReaction(event);
}
return;
}
/* /*
* @function getTree Creates a Tree out of these received List of events. * @function getTree Creates a Tree out of these received List of events.
@@ -1391,7 +1416,7 @@ Store getTree(Set<Event> events) {
if( gDebug > 0) print("In getTree: totalKind3Processed = $totalKind3Processed notProcessed = $notProcessed3 gKindONames.length = ${gKindONames.length}"); if( gDebug > 0) print("In getTree: totalKind3Processed = $totalKind3Processed notProcessed = $notProcessed3 gKindONames.length = ${gKindONames.length}");
// process kind 7 events or reactions // process kind 7 events or reactions
processReactions(events); //processReactions(events);
// remove bot events // remove bot events
events.removeWhere( (event) => gBots.contains(event.eventData.pubkey)); events.removeWhere( (event) => gBots.contains(event.eventData.pubkey));
@@ -1412,7 +1437,7 @@ Store getTree(Set<Event> events) {
return node; return node;
} }
// sort all participants by id; then create a large string with them together, thats the unique id for now // returns the id of event since only one p is expected in an event ( for future: sort all participants by id; then create a large string with them together, thats the unique id for now)
String getDirectRoomId(EventData eventData) { String getDirectRoomId(EventData eventData) {
List<String> participantIds = []; List<String> participantIds = [];
@@ -1427,8 +1452,10 @@ String getDirectRoomId(EventData eventData) {
participantIds.sort(); participantIds.sort();
String uniqueId = ""; String uniqueId = "";
participantIds.forEach((element) {uniqueId += element;}); participantIds.forEach((element) {uniqueId += element;}); // TODO ensure its only one thats added s
// send the other persons pubkey as identifier
if( eventData.pubkey == userPublicKey) { if( eventData.pubkey == userPublicKey) {
return uniqueId; return uniqueId;
} else { } else {