From 6ca5486efebdecb571a91324800c096fcc6ca035 Mon Sep 17 00:00:00 2001 From: Matt Gabrenya Date: Fri, 8 Sep 2023 15:58:12 -0700 Subject: [PATCH 1/4] fix(dna): notifications include responses to mews I responded to that occured only *after* my response --- .../mews/src/agent_to_notifications.rs | 68 ++++++++++++++----- 1 file changed, 50 insertions(+), 18 deletions(-) diff --git a/dnas/mewsfeed/zomes/coordinator/mews/src/agent_to_notifications.rs b/dnas/mewsfeed/zomes/coordinator/mews/src/agent_to_notifications.rs index 2d71a17a..dede5bce 100644 --- a/dnas/mewsfeed/zomes/coordinator/mews/src/agent_to_notifications.rs +++ b/dnas/mewsfeed/zomes/coordinator/mews/src/agent_to_notifications.rs @@ -91,30 +91,47 @@ pub fn get_notifications_for_agent( .collect(); // Responses to Mews I have responded to - let mew_hashes_i_responded_to: Vec = agent_mews + let mew_hashes_i_responded_to: Vec<(ActionHash, Record)> = agent_mews .iter() .filter_map(|record| match record.entry().to_app_option::().ok() { Some(Some(mew)) => match mew.mew_type { - MewType::Reply(ah) | MewType::Quote(ah) | MewType::Mewmew(ah) => Some(ah), + MewType::Reply(ah) | MewType::Quote(ah) | MewType::Mewmew(ah) => Some((ah, record.clone())), _ => None, }, _ => None, }) .collect(); - let mews_responding_to_mews_i_responded_to: Vec = mew_hashes_i_responded_to + + let mews_responding_to_mews_i_responded_to: Vec<(Record, Vec)> = mew_hashes_i_responded_to .iter() - .map(|ah| { - get_responses_for_mew(GetResponsesForMewInput { + .map(|(ah, record)| { + // Still have to use a get_links here because we cannot filter count_links by excluding an author + let responses_result = get_responses_for_mew(GetResponsesForMewInput { original_mew_hash: ah.clone(), response_type: None, page: None, - }) + }); + + match responses_result { + Ok(responses) => Ok((record.clone(), responses)), + Err(e) => Err(e) + } }) - .collect::>>>()? + .collect::)>>>()?; + + let mews_responding_to_mews_i_responded_to = mews_responding_to_mews_i_responded_to .iter() + .map(|(my_response, other_responses)| -> Vec { + other_responses + .iter() + .filter(|other_response| + other_response.action().author().clone() != input.agent.clone() && + other_response.action().timestamp() >= my_response.action().timestamp() + ) + .cloned() + .collect() + }) .flatten() - .cloned() - .filter(|r| r.action().author().clone() != input.agent.clone()) .collect(); let mut n = make_notifications_for_records( @@ -223,32 +240,47 @@ pub fn count_notifications_for_agent(agent: AgentPubKey) -> ExternResult .sum(); // Responses to Mews I have responded to - let mew_hashes_i_responded_to: Vec = agent_mews + let mew_hashes_i_responded_to: Vec<(ActionHash, Record)> = agent_mews .iter() .filter_map(|record| match record.entry().to_app_option::().ok() { Some(Some(mew)) => match mew.mew_type { - MewType::Reply(ah) | MewType::Quote(ah) | MewType::Mewmew(ah) => Some(ah), + MewType::Reply(ah) | MewType::Quote(ah) | MewType::Mewmew(ah) => Some((ah, record.clone())), _ => None, }, _ => None, }) .collect(); - let mews_responding_to_mews_i_responded_to_count: usize = mew_hashes_i_responded_to + let mews_responding_to_mews_i_responded_to: Vec<(Record, Vec)> = mew_hashes_i_responded_to .iter() - .map(|ah| { + .map(|(ah, record)| { // Still have to use a get_links here because we cannot filter count_links by excluding an author - get_responses_for_mew(GetResponsesForMewInput { + let responses_result = get_responses_for_mew(GetResponsesForMewInput { original_mew_hash: ah.clone(), response_type: None, page: None, - }) + }); + + match responses_result { + Ok(responses) => Ok((record.clone(), responses)), + Err(e) => Err(e) + } }) - .collect::>>>()? + .collect::)>>>()?; + + let mews_responding_to_mews_i_responded_to_count = mews_responding_to_mews_i_responded_to .iter() + .map(|(my_response, other_responses)| -> Vec { + other_responses + .iter() + .filter(|other_response| + other_response.action().author().clone() != agent.clone() && + other_response.action().timestamp() >= my_response.action().timestamp() + ) + .cloned() + .collect() + }) .flatten() - .cloned() - .filter(|r| r.action().author().clone() != agent.clone()) .count(); Ok(notifications_count + mews_responding_to_mews_i_responded_to_count) From 80e713873c08ae5ef93912b89964399c920ef7fe Mon Sep 17 00:00:00 2001 From: Matt Gabrenya Date: Fri, 8 Sep 2023 17:06:52 -0700 Subject: [PATCH 2/4] chore(dna): fmt+clippy --- .../mews/src/agent_to_notifications.rs | 98 ++++++++++--------- 1 file changed, 51 insertions(+), 47 deletions(-) diff --git a/dnas/mewsfeed/zomes/coordinator/mews/src/agent_to_notifications.rs b/dnas/mewsfeed/zomes/coordinator/mews/src/agent_to_notifications.rs index dede5bce..b5a59c50 100644 --- a/dnas/mewsfeed/zomes/coordinator/mews/src/agent_to_notifications.rs +++ b/dnas/mewsfeed/zomes/coordinator/mews/src/agent_to_notifications.rs @@ -95,43 +95,45 @@ pub fn get_notifications_for_agent( .iter() .filter_map(|record| match record.entry().to_app_option::().ok() { Some(Some(mew)) => match mew.mew_type { - MewType::Reply(ah) | MewType::Quote(ah) | MewType::Mewmew(ah) => Some((ah, record.clone())), + MewType::Reply(ah) | MewType::Quote(ah) | MewType::Mewmew(ah) => { + Some((ah, record.clone())) + } _ => None, }, _ => None, }) .collect(); - let mews_responding_to_mews_i_responded_to: Vec<(Record, Vec)> = mew_hashes_i_responded_to - .iter() - .map(|(ah, record)| { - // Still have to use a get_links here because we cannot filter count_links by excluding an author - let responses_result = get_responses_for_mew(GetResponsesForMewInput { - original_mew_hash: ah.clone(), - response_type: None, - page: None, - }); - - match responses_result { - Ok(responses) => Ok((record.clone(), responses)), - Err(e) => Err(e) - } - }) - .collect::)>>>()?; + let mews_responding_to_mews_i_responded_to: Vec<(Record, Vec)> = + mew_hashes_i_responded_to + .iter() + .map(|(ah, record)| { + // Still have to use a get_links here because we cannot filter count_links by excluding an author + let responses_result = get_responses_for_mew(GetResponsesForMewInput { + original_mew_hash: ah.clone(), + response_type: None, + page: None, + }); + + match responses_result { + Ok(responses) => Ok((record.clone(), responses)), + Err(e) => Err(e), + } + }) + .collect::)>>>()?; let mews_responding_to_mews_i_responded_to = mews_responding_to_mews_i_responded_to .iter() - .map(|(my_response, other_responses)| -> Vec { + .flat_map(|(my_response, other_responses)| -> Vec { other_responses .iter() - .filter(|other_response| - other_response.action().author().clone() != input.agent.clone() && - other_response.action().timestamp() >= my_response.action().timestamp() - ) + .filter(|other_response| { + other_response.action().author().clone() != input.agent.clone() + && other_response.action().timestamp() >= my_response.action().timestamp() + }) .cloned() .collect() }) - .flatten() .collect(); let mut n = make_notifications_for_records( @@ -244,43 +246,45 @@ pub fn count_notifications_for_agent(agent: AgentPubKey) -> ExternResult .iter() .filter_map(|record| match record.entry().to_app_option::().ok() { Some(Some(mew)) => match mew.mew_type { - MewType::Reply(ah) | MewType::Quote(ah) | MewType::Mewmew(ah) => Some((ah, record.clone())), + MewType::Reply(ah) | MewType::Quote(ah) | MewType::Mewmew(ah) => { + Some((ah, record.clone())) + } _ => None, }, _ => None, }) .collect(); - let mews_responding_to_mews_i_responded_to: Vec<(Record, Vec)> = mew_hashes_i_responded_to - .iter() - .map(|(ah, record)| { - // Still have to use a get_links here because we cannot filter count_links by excluding an author - let responses_result = get_responses_for_mew(GetResponsesForMewInput { - original_mew_hash: ah.clone(), - response_type: None, - page: None, - }); - - match responses_result { - Ok(responses) => Ok((record.clone(), responses)), - Err(e) => Err(e) - } - }) - .collect::)>>>()?; + let mews_responding_to_mews_i_responded_to: Vec<(Record, Vec)> = + mew_hashes_i_responded_to + .iter() + .map(|(ah, record)| { + // Still have to use a get_links here because we cannot filter count_links by excluding an author + let responses_result = get_responses_for_mew(GetResponsesForMewInput { + original_mew_hash: ah.clone(), + response_type: None, + page: None, + }); + + match responses_result { + Ok(responses) => Ok((record.clone(), responses)), + Err(e) => Err(e), + } + }) + .collect::)>>>()?; let mews_responding_to_mews_i_responded_to_count = mews_responding_to_mews_i_responded_to .iter() - .map(|(my_response, other_responses)| -> Vec { + .flat_map(|(my_response, other_responses)| -> Vec { other_responses .iter() - .filter(|other_response| - other_response.action().author().clone() != agent.clone() && - other_response.action().timestamp() >= my_response.action().timestamp() - ) + .filter(|other_response| { + other_response.action().author().clone() != agent.clone() + && other_response.action().timestamp() >= my_response.action().timestamp() + }) .cloned() .collect() - }) - .flatten() + }) .count(); Ok(notifications_count + mews_responding_to_mews_i_responded_to_count) From 94e475899b1c79331931da8920d958b1deb286a4 Mon Sep 17 00:00:00 2001 From: Matt Gabrenya Date: Sat, 9 Sep 2023 22:06:01 -0700 Subject: [PATCH 3/4] fix(dna): prevent redundant notifications when an agent has responded to a mew you responsded to multiple times --- .../mews/src/agent_to_notifications.rs | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/dnas/mewsfeed/zomes/coordinator/mews/src/agent_to_notifications.rs b/dnas/mewsfeed/zomes/coordinator/mews/src/agent_to_notifications.rs index b5a59c50..584bcd82 100644 --- a/dnas/mewsfeed/zomes/coordinator/mews/src/agent_to_notifications.rs +++ b/dnas/mewsfeed/zomes/coordinator/mews/src/agent_to_notifications.rs @@ -91,32 +91,34 @@ pub fn get_notifications_for_agent( .collect(); // Responses to Mews I have responded to - let mew_hashes_i_responded_to: Vec<(ActionHash, Record)> = agent_mews + let mut mew_hashes_i_responded_to: Vec<(Record, ActionHash)> = agent_mews .iter() - .filter_map(|record| match record.entry().to_app_option::().ok() { + .filter_map(|response_record| match response_record.entry().to_app_option::().ok() { Some(Some(mew)) => match mew.mew_type { - MewType::Reply(ah) | MewType::Quote(ah) | MewType::Mewmew(ah) => { - Some((ah, record.clone())) + MewType::Reply(original_ah) | MewType::Quote(original_ah) | MewType::Mewmew(original_ah) => { + Some((response_record.clone(), original_ah)) } _ => None, }, _ => None, }) .collect(); + mew_hashes_i_responded_to.sort_by_key(|(response_record, _)| response_record.action().timestamp()); + mew_hashes_i_responded_to.dedup_by_key(|(_, original_ah)| original_ah.clone()); let mews_responding_to_mews_i_responded_to: Vec<(Record, Vec)> = mew_hashes_i_responded_to .iter() - .map(|(ah, record)| { + .map(|(my_response, original_ah)| { // Still have to use a get_links here because we cannot filter count_links by excluding an author let responses_result = get_responses_for_mew(GetResponsesForMewInput { - original_mew_hash: ah.clone(), + original_mew_hash: original_ah.clone(), response_type: None, page: None, }); match responses_result { - Ok(responses) => Ok((record.clone(), responses)), + Ok(all_responses) => Ok((my_response.clone(), all_responses)), Err(e) => Err(e), } }) @@ -124,8 +126,8 @@ pub fn get_notifications_for_agent( let mews_responding_to_mews_i_responded_to = mews_responding_to_mews_i_responded_to .iter() - .flat_map(|(my_response, other_responses)| -> Vec { - other_responses + .flat_map(|(my_response, all_responses)| -> Vec { + all_responses .iter() .filter(|other_response| { other_response.action().author().clone() != input.agent.clone() @@ -242,32 +244,34 @@ pub fn count_notifications_for_agent(agent: AgentPubKey) -> ExternResult .sum(); // Responses to Mews I have responded to - let mew_hashes_i_responded_to: Vec<(ActionHash, Record)> = agent_mews + let mut mew_hashes_i_responded_to: Vec<(Record, ActionHash)> = agent_mews .iter() - .filter_map(|record| match record.entry().to_app_option::().ok() { + .filter_map(|response_record| match response_record.entry().to_app_option::().ok() { Some(Some(mew)) => match mew.mew_type { - MewType::Reply(ah) | MewType::Quote(ah) | MewType::Mewmew(ah) => { - Some((ah, record.clone())) + MewType::Reply(original_ah) | MewType::Quote(original_ah) | MewType::Mewmew(original_ah) => { + Some((response_record.clone(), original_ah)) } _ => None, }, _ => None, }) .collect(); + mew_hashes_i_responded_to.sort_by_key(|(response_record, _)| response_record.action().timestamp()); + mew_hashes_i_responded_to.dedup_by_key(|(_, original_ah)| original_ah.clone()); let mews_responding_to_mews_i_responded_to: Vec<(Record, Vec)> = mew_hashes_i_responded_to .iter() - .map(|(ah, record)| { + .map(|(my_response, original_ah)| { // Still have to use a get_links here because we cannot filter count_links by excluding an author let responses_result = get_responses_for_mew(GetResponsesForMewInput { - original_mew_hash: ah.clone(), + original_mew_hash: original_ah.clone(), response_type: None, page: None, }); match responses_result { - Ok(responses) => Ok((record.clone(), responses)), + Ok(all_responses) => Ok((my_response.clone(), all_responses)), Err(e) => Err(e), } }) @@ -275,8 +279,8 @@ pub fn count_notifications_for_agent(agent: AgentPubKey) -> ExternResult let mews_responding_to_mews_i_responded_to_count = mews_responding_to_mews_i_responded_to .iter() - .flat_map(|(my_response, other_responses)| -> Vec { - other_responses + .flat_map(|(my_response, all_responses)| -> Vec { + all_responses .iter() .filter(|other_response| { other_response.action().author().clone() != agent.clone() From 0a343a3ae546b6732d05ec9a9f14fb968bedadf4 Mon Sep 17 00:00:00 2001 From: Matt Gabrenya Date: Mon, 11 Sep 2023 09:59:41 -0700 Subject: [PATCH 4/4] fix(dna): de-duplicate notifications for both 'reply to my mew' and 'replied to a yarn you participated in' when yarn author also replied to themselves --- .../mews/src/agent_to_notifications.rs | 54 ++++++++++++++----- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/dnas/mewsfeed/zomes/coordinator/mews/src/agent_to_notifications.rs b/dnas/mewsfeed/zomes/coordinator/mews/src/agent_to_notifications.rs index 584bcd82..aae66cac 100644 --- a/dnas/mewsfeed/zomes/coordinator/mews/src/agent_to_notifications.rs +++ b/dnas/mewsfeed/zomes/coordinator/mews/src/agent_to_notifications.rs @@ -93,17 +93,31 @@ pub fn get_notifications_for_agent( // Responses to Mews I have responded to let mut mew_hashes_i_responded_to: Vec<(Record, ActionHash)> = agent_mews .iter() - .filter_map(|response_record| match response_record.entry().to_app_option::().ok() { - Some(Some(mew)) => match mew.mew_type { - MewType::Reply(original_ah) | MewType::Quote(original_ah) | MewType::Mewmew(original_ah) => { - Some((response_record.clone(), original_ah)) - } + // Filter only mew types that are responses + .filter_map( + |response_record| match response_record.entry().to_app_option::().ok() { + Some(Some(mew)) => match mew.mew_type { + MewType::Reply(original_ah) + | MewType::Quote(original_ah) + | MewType::Mewmew(original_ah) => Some((response_record.clone(), original_ah)), + _ => None, + }, _ => None, }, - _ => None, + ) + // Exclude responses to agent's mews to avoid duplicate notifications for both "responded to your mew" and "responded to a yarn you participated in" + .filter(|(_, original_ah)| { + agent_mews + .iter() + .filter(|authored_record| { + authored_record.action_hashed().hash == original_ah.clone() + }) + .count() + == 0 }) .collect(); - mew_hashes_i_responded_to.sort_by_key(|(response_record, _)| response_record.action().timestamp()); + mew_hashes_i_responded_to + .sort_by_key(|(response_record, _)| response_record.action().timestamp()); mew_hashes_i_responded_to.dedup_by_key(|(_, original_ah)| original_ah.clone()); let mews_responding_to_mews_i_responded_to: Vec<(Record, Vec)> = @@ -246,17 +260,29 @@ pub fn count_notifications_for_agent(agent: AgentPubKey) -> ExternResult // Responses to Mews I have responded to let mut mew_hashes_i_responded_to: Vec<(Record, ActionHash)> = agent_mews .iter() - .filter_map(|response_record| match response_record.entry().to_app_option::().ok() { - Some(Some(mew)) => match mew.mew_type { - MewType::Reply(original_ah) | MewType::Quote(original_ah) | MewType::Mewmew(original_ah) => { - Some((response_record.clone(), original_ah)) - } + .filter_map( + |response_record| match response_record.entry().to_app_option::().ok() { + Some(Some(mew)) => match mew.mew_type { + MewType::Reply(original_ah) + | MewType::Quote(original_ah) + | MewType::Mewmew(original_ah) => Some((response_record.clone(), original_ah)), + _ => None, + }, _ => None, }, - _ => None, + ) + .filter(|(_, original_ah)| { + agent_mews + .iter() + .filter(|authored_record| { + authored_record.action_hashed().hash == original_ah.clone() + }) + .count() + == 0 }) .collect(); - mew_hashes_i_responded_to.sort_by_key(|(response_record, _)| response_record.action().timestamp()); + mew_hashes_i_responded_to + .sort_by_key(|(response_record, _)| response_record.action().timestamp()); mew_hashes_i_responded_to.dedup_by_key(|(_, original_ah)| original_ah.clone()); let mews_responding_to_mews_i_responded_to: Vec<(Record, Vec)> =