Skip to content

Commit

Permalink
feat(term): add mark_as_done_all command
Browse files Browse the repository at this point in the history
  • Loading branch information
ymgyt committed Jul 5, 2024
1 parent 7d1b96c commit 4633d73
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 48 deletions.
4 changes: 3 additions & 1 deletion crates/synd_term/src/application/in_flight.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use std::{

use tokio::time::{Instant, Sleep};

use crate::types::github::NotificationId;

pub type RequestSequence = u64;

#[derive(Clone, Copy, PartialEq, Eq)]
Expand All @@ -19,7 +21,7 @@ pub enum RequestId {
FetchGithubSubject,
SubscribeFeed,
UnsubscribeFeed,
MarkGithubNotificationAsDone,
MarkGithubNotificationAsDone { id: NotificationId },
UnsubscribeGithubThread,
}

Expand Down
64 changes: 41 additions & 23 deletions crates/synd_term/src/application/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -697,8 +697,9 @@ impl Application {
self.components.gh_notifications.move_last();
self.should_render();
}
Command::OpenGhNotification => {
Command::OpenGhNotification { with_mark_as_done } => {
self.open_notification();
with_mark_as_done.then(|| self.mark_gh_notification_as_done(false));
}
Command::ReloadGhNotifications => {
let params = self.components.gh_notifications.reload();
Expand All @@ -707,16 +708,16 @@ impl Application {
Command::FetchGhNotificationDetails { contexts } => {
self.fetch_gh_notification_details(contexts);
}
Command::MarkGhNotificationAsDone => {
self.mark_gh_notification_as_done();
Command::MarkGhNotificationAsDone { all } => {
self.mark_gh_notification_as_done(all);
}
Command::UnsubscribeGhThread => {
// Unlike the web UI, simply unsubscribing does not mark it as done
// and it remains as unread.
// Therefore, when reloading, the unsubscribed notification is displayed again.
// To address this, we will implicitly mark it as done when unsubscribing.
self.unsubscribe_gh_thread();
self.mark_gh_notification_as_done();
self.mark_gh_notification_as_done(false);
}
Command::OpenGhNotificationFilterPopup => {
self.components.gh_notifications.open_filter_popup();
Expand Down Expand Up @@ -957,28 +958,45 @@ impl Application {
self.jobs.futures.push(fut);
}

fn mark_gh_notification_as_done(&mut self) {
let Some(id) = self.components.gh_notifications.marking_as_done() else {
return;
fn mark_gh_notification_as_done(&mut self, all: bool) {
let ids = if all {
Either::Left(
self.components
.gh_notifications
.marking_as_done_all()
.into_iter(),
)
} else {
let Some(id) = self.components.gh_notifications.marking_as_done() else {
return;
};
Either::Right(std::iter::once(id))
};
let client = self.github_client.as_ref().unwrap().clone();
let request_seq = self.in_flight.add(RequestId::MarkGithubNotificationAsDone);
let fut = async move {
match client.mark_thread_as_done(id).await {
Ok(()) => Ok(Command::HandleApiResponse {
request_seq,
response: ApiResponse::MarkGithubNotificationAsDone {
notification_id: id,
},
}),
Err(error) => Ok(Command::HandleGithubApiError {
error: Arc::new(error),
request_seq,
}),

for id in ids {
let request_seq = self
.in_flight
.add(RequestId::MarkGithubNotificationAsDone { id });
let Some(client) = self.github_client.clone() else {
return;
};
let fut = async move {
match client.mark_thread_as_done(id).await {
Ok(()) => Ok(Command::HandleApiResponse {
request_seq,
response: ApiResponse::MarkGithubNotificationAsDone {
notification_id: id,
},
}),
Err(error) => Ok(Command::HandleGithubApiError {
error: Arc::new(error),
request_seq,
}),
}
}
.boxed();
self.jobs.futures.push(fut);
}
.boxed();
self.jobs.futures.push(fut);
}

fn unsubscribe_gh_thread(&mut self) {
Expand Down
22 changes: 18 additions & 4 deletions crates/synd_term/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,13 @@ pub(crate) enum Command {
MoveGhNotification(Direction),
MoveGhNotificationFirst,
MoveGhNotificationLast,
OpenGhNotification,
OpenGhNotification {
with_mark_as_done: bool,
},
ReloadGhNotifications,
MarkGhNotificationAsDone,
MarkGhNotificationAsDone {
all: bool,
},
UnsubscribeGhThread,
OpenGhNotificationFilterPopup,
CloseGhNotificationFilterPopup,
Expand Down Expand Up @@ -324,13 +328,23 @@ impl Command {
Command::MoveGhNotificationLast
}
pub fn open_gh_notification() -> Self {
Command::OpenGhNotification
Command::OpenGhNotification {
with_mark_as_done: false,
}
}
pub fn open_gh_notification_with_done() -> Self {
Command::OpenGhNotification {
with_mark_as_done: true,
}
}
pub fn reload_gh_notifications() -> Self {
Command::ReloadGhNotifications
}
pub fn mark_gh_notification_as_done() -> Self {
Command::MarkGhNotificationAsDone
Command::MarkGhNotificationAsDone { all: false }
}
pub fn mark_gh_notification_as_done_all() -> Self {
Command::MarkGhNotificationAsDone { all: true }
}
pub fn unsubscribe_gh_thread() -> Self {
Command::UnsubscribeGhThread
Expand Down
2 changes: 2 additions & 0 deletions crates/synd_term/src/keymap/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@ pub fn default() -> KeymapsConfig {
"k" | "up" => move_up_gh_notification,
"j" | "down" => move_down_gh_notification,
"enter" => open_gh_notification,
"A-enter" => open_gh_notification_with_done,
"r" => reload_gh_notifications,
"d" => mark_gh_notification_as_done,
"S-d" => mark_gh_notification_as_done_all,
"u" => unsubscribe_gh_thread,
"g" => {
"g" => move_gh_notification_first,
Expand Down
1 change: 1 addition & 0 deletions crates/synd_term/src/keymap/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ fn parse(s: &str) -> anyhow::Result<KeyEvent> {
let modifier = match token {
"C" => KeyModifiers::CONTROL,
"S" => KeyModifiers::SHIFT,
"A" => KeyModifiers::ALT,
undefined => bail!("`{undefined}` modifier is not implemented yet"),
};
modifiers.insert(modifier);
Expand Down
33 changes: 27 additions & 6 deletions crates/synd_term/src/ui/components/collections/filterable.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use std::ops::ControlFlow;

use crate::{
application::{Direction, IndexOutOfRange, Populate},
ui::components::filter::{FilterResult, Filterable},
};

pub(crate) struct FilterableVec<T, F> {
pub(crate) items: Vec<T>,
items: Vec<T>,
effective_items: Vec<usize>,
selected_item_index: usize,
filterer: F,
Expand Down Expand Up @@ -66,10 +68,6 @@ impl<T, F> FilterableVec<T, F> {
.map(move |&idx| &self.items[idx])
}

pub(crate) fn unfiltered_iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
self.items.iter_mut()
}

pub(crate) fn as_unfiltered_slice(&self) -> &[T] {
self.items.as_slice()
}
Expand Down Expand Up @@ -98,6 +96,29 @@ where
self.refresh();
}

pub(crate) fn with_mut<F2>(&mut self, mut f: F2) -> Option<&T>
where
F2: FnMut(&mut T) -> ControlFlow<()>,
{
let mut found = None;
for (idx, item) in self.items.iter_mut().enumerate() {
match f(item) {
ControlFlow::Break(()) => {
found = Some(idx);
break;
}
ControlFlow::Continue(()) => continue,
}
}

if let Some(idx) = found {
self.refresh();
self.items.get(idx)
} else {
None
}
}

pub(crate) fn update_filter(&mut self, filterer: F) {
self.filterer = filterer;
self.refresh();
Expand All @@ -119,7 +140,7 @@ where
self.refresh();
}

fn refresh(&mut self) {
pub(crate) fn refresh(&mut self) {
self.effective_items = self
.items
.iter()
Expand Down
34 changes: 23 additions & 11 deletions crates/synd_term/src/ui/components/gh_notifications/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{borrow::Cow, collections::HashMap, fmt::Debug};
use std::{borrow::Cow, collections::HashMap, fmt::Debug, ops::ControlFlow};

use chrono_humanize::{Accuracy, HumanTime, Tense};
use itertools::Itertools;
Expand Down Expand Up @@ -226,14 +226,16 @@ impl GhNotifications {
issue: IssueContext,
config: &Categories,
) -> Option<&Notification> {
for n in self.notifications.unfiltered_iter_mut() {
let mut issue = Some(issue);
self.notifications.with_mut(|n| {
if n.id == notification_id {
n.subject_context = Some(SubjectContext::Issue(issue));
n.subject_context = Some(SubjectContext::Issue(issue.take().unwrap()));
n.update_categories(config);
return Some(n);
ControlFlow::Break(())
} else {
ControlFlow::Continue(())
}
}
None
})
}

pub(crate) fn update_pull_request(
Expand All @@ -242,14 +244,16 @@ impl GhNotifications {
pull_request: PullRequestContext,
config: &Categories,
) -> Option<&Notification> {
for n in self.notifications.unfiltered_iter_mut() {
let mut pull_request = Some(pull_request);
self.notifications.with_mut(|n| {
if n.id == notification_id {
n.subject_context = Some(SubjectContext::PullRequest(pull_request));
n.subject_context = Some(SubjectContext::PullRequest(pull_request.take().unwrap()));
n.update_categories(config);
return Some(n);
ControlFlow::Break(())
} else {
ControlFlow::Continue(())
}
}
None
})
}

pub(crate) fn marking_as_done(&mut self) -> Option<NotificationId> {
Expand All @@ -258,6 +262,14 @@ impl GhNotifications {
Some(id)
}

pub(crate) fn marking_as_done_all(&mut self) -> Vec<NotificationId> {
let ids: Vec<NotificationId> = self.notifications.iter().map(|n| n.id).collect();
for &id in &ids {
self.status.insert(id, NotificationStatus::MarkingAsDone);
}
ids
}

pub(crate) fn marked_as_done(&mut self, id: NotificationId) {
self.notifications.retain(|n| n.id != id);
}
Expand Down
4 changes: 2 additions & 2 deletions crates/synd_term/src/ui/components/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ impl StatusLine {
RequestId::FetchGithubSubject => Cow::Borrowed("Fetch github subject..."),
RequestId::SubscribeFeed => Cow::Borrowed("Subscribe feed..."),
RequestId::UnsubscribeFeed => Cow::Borrowed("Unsubscribe feed..."),
RequestId::MarkGithubNotificationAsDone => {
Cow::Borrowed("Mark notification as done...")
RequestId::MarkGithubNotificationAsDone { id } => {
Cow::Owned(format!("Mark notification({id}) as done..."))
}
RequestId::UnsubscribeGithubThread => Cow::Borrowed("Unsubscribe thread..."),
};
Expand Down
2 changes: 1 addition & 1 deletion crates/synd_term/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ mod test {
.already_logined();

let mut application = test_case.init_app().await?;
let (tx, mut event_stream) = helper::event_stream();
let (_tx, mut event_stream) = helper::event_stream();

{
application
Expand Down

0 comments on commit 4633d73

Please sign in to comment.