mirror of
https://github.com/youwen5/iamb.git
synced 2025-06-20 05:39:52 -07:00
Add commands for viewing and clearing unreads (#332)
This commit is contained in:
parent
4fc05c7b40
commit
480888a1fc
5 changed files with 135 additions and 0 deletions
|
@ -61,6 +61,8 @@ Log out of
|
||||||
View a list of joined rooms.
|
View a list of joined rooms.
|
||||||
.It Sy ":spaces"
|
.It Sy ":spaces"
|
||||||
View a list of joined spaces.
|
View a list of joined spaces.
|
||||||
|
.It Sy ":unreads"
|
||||||
|
View a list of unread rooms.
|
||||||
.It Sy ":welcome"
|
.It Sy ":welcome"
|
||||||
View the startup Welcome window.
|
View the startup Welcome window.
|
||||||
.El
|
.El
|
||||||
|
@ -95,6 +97,8 @@ React to the selected message with an Emoji.
|
||||||
Redact the selected message.
|
Redact the selected message.
|
||||||
.It Sy ":reply"
|
.It Sy ":reply"
|
||||||
Reply to the selected message.
|
Reply to the selected message.
|
||||||
|
.It Sy ":unreads clear"
|
||||||
|
Mark all unread rooms as read.
|
||||||
.It Sy ":unreact [shortcode]"
|
.It Sy ":unreact [shortcode]"
|
||||||
Remove your reaction from the selected message.
|
Remove your reaction from the selected message.
|
||||||
When no arguments are given, remove all of your reactions from the message.
|
When no arguments are given, remove all of your reactions from the message.
|
||||||
|
|
45
src/base.rs
45
src/base.rs
|
@ -510,6 +510,9 @@ pub enum IambAction {
|
||||||
|
|
||||||
/// Toggle the focus within the focused room.
|
/// Toggle the focus within the focused room.
|
||||||
ToggleScrollbackFocus,
|
ToggleScrollbackFocus,
|
||||||
|
|
||||||
|
/// Clear all unread messages.
|
||||||
|
ClearUnreads,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IambAction {
|
impl IambAction {
|
||||||
|
@ -546,6 +549,7 @@ impl From<SendAction> for IambAction {
|
||||||
impl ApplicationAction for IambAction {
|
impl ApplicationAction for IambAction {
|
||||||
fn is_edit_sequence(&self, _: &EditContext) -> SequenceStatus {
|
fn is_edit_sequence(&self, _: &EditContext) -> SequenceStatus {
|
||||||
match self {
|
match self {
|
||||||
|
IambAction::ClearUnreads => SequenceStatus::Break,
|
||||||
IambAction::Homeserver(..) => SequenceStatus::Break,
|
IambAction::Homeserver(..) => SequenceStatus::Break,
|
||||||
IambAction::Keys(..) => SequenceStatus::Break,
|
IambAction::Keys(..) => SequenceStatus::Break,
|
||||||
IambAction::Message(..) => SequenceStatus::Break,
|
IambAction::Message(..) => SequenceStatus::Break,
|
||||||
|
@ -560,6 +564,7 @@ impl ApplicationAction for IambAction {
|
||||||
|
|
||||||
fn is_last_action(&self, _: &EditContext) -> SequenceStatus {
|
fn is_last_action(&self, _: &EditContext) -> SequenceStatus {
|
||||||
match self {
|
match self {
|
||||||
|
IambAction::ClearUnreads => SequenceStatus::Atom,
|
||||||
IambAction::Homeserver(..) => SequenceStatus::Atom,
|
IambAction::Homeserver(..) => SequenceStatus::Atom,
|
||||||
IambAction::Keys(..) => SequenceStatus::Atom,
|
IambAction::Keys(..) => SequenceStatus::Atom,
|
||||||
IambAction::Message(..) => SequenceStatus::Atom,
|
IambAction::Message(..) => SequenceStatus::Atom,
|
||||||
|
@ -574,6 +579,7 @@ impl ApplicationAction for IambAction {
|
||||||
|
|
||||||
fn is_last_selection(&self, _: &EditContext) -> SequenceStatus {
|
fn is_last_selection(&self, _: &EditContext) -> SequenceStatus {
|
||||||
match self {
|
match self {
|
||||||
|
IambAction::ClearUnreads => SequenceStatus::Ignore,
|
||||||
IambAction::Homeserver(..) => SequenceStatus::Ignore,
|
IambAction::Homeserver(..) => SequenceStatus::Ignore,
|
||||||
IambAction::Keys(..) => SequenceStatus::Ignore,
|
IambAction::Keys(..) => SequenceStatus::Ignore,
|
||||||
IambAction::Message(..) => SequenceStatus::Ignore,
|
IambAction::Message(..) => SequenceStatus::Ignore,
|
||||||
|
@ -588,6 +594,7 @@ impl ApplicationAction for IambAction {
|
||||||
|
|
||||||
fn is_switchable(&self, _: &EditContext) -> bool {
|
fn is_switchable(&self, _: &EditContext) -> bool {
|
||||||
match self {
|
match self {
|
||||||
|
IambAction::ClearUnreads => false,
|
||||||
IambAction::Homeserver(..) => false,
|
IambAction::Homeserver(..) => false,
|
||||||
IambAction::Message(..) => false,
|
IambAction::Message(..) => false,
|
||||||
IambAction::Room(..) => false,
|
IambAction::Room(..) => false,
|
||||||
|
@ -1148,6 +1155,14 @@ impl RoomInfo {
|
||||||
self.user_receipts.insert(user_id, event_id);
|
self.user_receipts.insert(user_id, event_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fully_read(&mut self, user_id: OwnedUserId) {
|
||||||
|
let Some(((_, event_id), _)) = self.messages.last_key_value() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.set_receipt(user_id, event_id.clone());
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_receipt(&self, user_id: &UserId) -> Option<&OwnedEventId> {
|
pub fn get_receipt(&self, user_id: &UserId) -> Option<&OwnedEventId> {
|
||||||
self.user_receipts.get(user_id)
|
self.user_receipts.get(user_id)
|
||||||
}
|
}
|
||||||
|
@ -1309,6 +1324,20 @@ pub struct SyncInfo {
|
||||||
pub dms: Vec<Arc<(MatrixRoom, Option<Tags>)>>,
|
pub dms: Vec<Arc<(MatrixRoom, Option<Tags>)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SyncInfo {
|
||||||
|
pub fn rooms(&self) -> impl Iterator<Item = &RoomId> {
|
||||||
|
self.rooms.iter().map(|r| r.0.room_id())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dms(&self) -> impl Iterator<Item = &RoomId> {
|
||||||
|
self.dms.iter().map(|r| r.0.room_id())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn chats(&self) -> impl Iterator<Item = &RoomId> {
|
||||||
|
self.rooms().chain(self.dms())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bitflags::bitflags! {
|
bitflags::bitflags! {
|
||||||
/// Load-needs
|
/// Load-needs
|
||||||
#[derive(Debug, Default, PartialEq)]
|
#[derive(Debug, Default, PartialEq)]
|
||||||
|
@ -1480,6 +1509,9 @@ pub enum IambId {
|
||||||
|
|
||||||
/// The `:chats` window.
|
/// The `:chats` window.
|
||||||
ChatList,
|
ChatList,
|
||||||
|
|
||||||
|
/// The `:unreads` window.
|
||||||
|
UnreadList,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for IambId {
|
impl Display for IambId {
|
||||||
|
@ -1500,6 +1532,7 @@ impl Display for IambId {
|
||||||
IambId::VerifyList => f.write_str("iamb://verify"),
|
IambId::VerifyList => f.write_str("iamb://verify"),
|
||||||
IambId::Welcome => f.write_str("iamb://welcome"),
|
IambId::Welcome => f.write_str("iamb://welcome"),
|
||||||
IambId::ChatList => f.write_str("iamb://chats"),
|
IambId::ChatList => f.write_str("iamb://chats"),
|
||||||
|
IambId::UnreadList => f.write_str("iamb://unreads"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1631,6 +1664,13 @@ impl<'de> Visitor<'de> for IambIdVisitor {
|
||||||
|
|
||||||
Ok(IambId::ChatList)
|
Ok(IambId::ChatList)
|
||||||
},
|
},
|
||||||
|
Some("unreads") => {
|
||||||
|
if url.path() != "" {
|
||||||
|
return Err(E::custom("iamb://unreads takes no path"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(IambId::UnreadList)
|
||||||
|
},
|
||||||
Some(s) => Err(E::custom(format!("{s:?} is not a valid window"))),
|
Some(s) => Err(E::custom(format!("{s:?} is not a valid window"))),
|
||||||
None => Err(E::custom("Invalid iamb window URL")),
|
None => Err(E::custom("Invalid iamb window URL")),
|
||||||
}
|
}
|
||||||
|
@ -1691,6 +1731,9 @@ pub enum IambBufferId {
|
||||||
|
|
||||||
/// The `:chats` window.
|
/// The `:chats` window.
|
||||||
ChatList,
|
ChatList,
|
||||||
|
|
||||||
|
/// The `:unreads` window.
|
||||||
|
UnreadList,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IambBufferId {
|
impl IambBufferId {
|
||||||
|
@ -1706,6 +1749,7 @@ impl IambBufferId {
|
||||||
IambBufferId::VerifyList => IambId::VerifyList,
|
IambBufferId::VerifyList => IambId::VerifyList,
|
||||||
IambBufferId::Welcome => IambId::Welcome,
|
IambBufferId::Welcome => IambId::Welcome,
|
||||||
IambBufferId::ChatList => IambId::ChatList,
|
IambBufferId::ChatList => IambId::ChatList,
|
||||||
|
IambBufferId::UnreadList => IambId::UnreadList,
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(id)
|
Some(id)
|
||||||
|
@ -1740,6 +1784,7 @@ impl ApplicationInfo for IambInfo {
|
||||||
IambBufferId::VerifyList => vec![],
|
IambBufferId::VerifyList => vec![],
|
||||||
IambBufferId::Welcome => vec![],
|
IambBufferId::Welcome => vec![],
|
||||||
IambBufferId::ChatList => vec![],
|
IambBufferId::ChatList => vec![],
|
||||||
|
IambBufferId::UnreadList => vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -307,6 +307,30 @@ fn iamb_chats(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult {
|
||||||
return Ok(step);
|
return Ok(step);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn iamb_unreads(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult {
|
||||||
|
let mut args = desc.arg.strings()?;
|
||||||
|
|
||||||
|
if args.len() > 1 {
|
||||||
|
return Result::Err(CommandError::InvalidArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
match args.pop().as_deref() {
|
||||||
|
Some("clear") => {
|
||||||
|
let clear = IambAction::ClearUnreads;
|
||||||
|
let step = CommandStep::Continue(clear.into(), ctx.context.clone());
|
||||||
|
|
||||||
|
return Ok(step);
|
||||||
|
},
|
||||||
|
Some(_) => return Result::Err(CommandError::InvalidArgument),
|
||||||
|
None => {
|
||||||
|
let open = ctx.switch(OpenTarget::Application(IambId::UnreadList));
|
||||||
|
let step = CommandStep::Continue(open, ctx.context.clone());
|
||||||
|
|
||||||
|
return Ok(step);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn iamb_spaces(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult {
|
fn iamb_spaces(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult {
|
||||||
if !desc.arg.text.is_empty() {
|
if !desc.arg.text.is_empty() {
|
||||||
return Result::Err(CommandError::InvalidArgument);
|
return Result::Err(CommandError::InvalidArgument);
|
||||||
|
@ -648,6 +672,11 @@ fn add_iamb_commands(cmds: &mut ProgramCommands) {
|
||||||
aliases: vec![],
|
aliases: vec![],
|
||||||
f: iamb_spaces,
|
f: iamb_spaces,
|
||||||
});
|
});
|
||||||
|
cmds.add_command(ProgramCommand {
|
||||||
|
name: "unreads".into(),
|
||||||
|
aliases: vec![],
|
||||||
|
f: iamb_unreads,
|
||||||
|
});
|
||||||
cmds.add_command(ProgramCommand {
|
cmds.add_command(ProgramCommand {
|
||||||
name: "unreact".into(),
|
name: "unreact".into(),
|
||||||
aliases: vec![],
|
aliases: vec![],
|
||||||
|
|
12
src/main.rs
12
src/main.rs
|
@ -529,6 +529,18 @@ impl Application {
|
||||||
}
|
}
|
||||||
|
|
||||||
let info = match action {
|
let info = match action {
|
||||||
|
IambAction::ClearUnreads => {
|
||||||
|
let user_id = &store.application.settings.profile.user_id;
|
||||||
|
|
||||||
|
for room_id in store.application.sync_info.chats() {
|
||||||
|
if let Some(room) = store.application.rooms.get_mut(room_id) {
|
||||||
|
room.fully_read(user_id.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
},
|
||||||
|
|
||||||
IambAction::ToggleScrollbackFocus => {
|
IambAction::ToggleScrollbackFocus => {
|
||||||
self.screen.current_window_mut()?.focus_toggle();
|
self.screen.current_window_mut()?.focus_toggle();
|
||||||
|
|
||||||
|
|
|
@ -315,6 +315,7 @@ macro_rules! delegate {
|
||||||
IambWindow::VerifyList($id) => $e,
|
IambWindow::VerifyList($id) => $e,
|
||||||
IambWindow::Welcome($id) => $e,
|
IambWindow::Welcome($id) => $e,
|
||||||
IambWindow::ChatList($id) => $e,
|
IambWindow::ChatList($id) => $e,
|
||||||
|
IambWindow::UnreadList($id) => $e,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -328,6 +329,7 @@ pub enum IambWindow {
|
||||||
SpaceList(SpaceListState),
|
SpaceList(SpaceListState),
|
||||||
Welcome(WelcomeState),
|
Welcome(WelcomeState),
|
||||||
ChatList(ChatListState),
|
ChatList(ChatListState),
|
||||||
|
UnreadList(UnreadListState),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IambWindow {
|
impl IambWindow {
|
||||||
|
@ -383,6 +385,7 @@ pub type DirectListState = ListState<DirectItem, IambInfo>;
|
||||||
pub type MemberListState = ListState<MemberItem, IambInfo>;
|
pub type MemberListState = ListState<MemberItem, IambInfo>;
|
||||||
pub type RoomListState = ListState<RoomItem, IambInfo>;
|
pub type RoomListState = ListState<RoomItem, IambInfo>;
|
||||||
pub type ChatListState = ListState<GenericChatItem, IambInfo>;
|
pub type ChatListState = ListState<GenericChatItem, IambInfo>;
|
||||||
|
pub type UnreadListState = ListState<GenericChatItem, IambInfo>;
|
||||||
pub type SpaceListState = ListState<SpaceItem, IambInfo>;
|
pub type SpaceListState = ListState<SpaceItem, IambInfo>;
|
||||||
pub type VerifyListState = ListState<VerifyItem, IambInfo>;
|
pub type VerifyListState = ListState<VerifyItem, IambInfo>;
|
||||||
|
|
||||||
|
@ -579,6 +582,39 @@ impl WindowOps<IambInfo> for IambWindow {
|
||||||
.focus(focused)
|
.focus(focused)
|
||||||
.render(area, buf, state);
|
.render(area, buf, state);
|
||||||
},
|
},
|
||||||
|
IambWindow::UnreadList(state) => {
|
||||||
|
let mut items = store
|
||||||
|
.application
|
||||||
|
.sync_info
|
||||||
|
.rooms
|
||||||
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.map(|room_info| GenericChatItem::new(room_info, store, false))
|
||||||
|
.filter(RoomLikeItem::is_unread)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let dms = store
|
||||||
|
.application
|
||||||
|
.sync_info
|
||||||
|
.dms
|
||||||
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.map(|room_info| GenericChatItem::new(room_info, store, true))
|
||||||
|
.filter(RoomLikeItem::is_unread);
|
||||||
|
|
||||||
|
items.extend(dms);
|
||||||
|
|
||||||
|
let fields = &store.application.settings.tunables.sort.chats;
|
||||||
|
items.sort_by(|a, b| room_fields_cmp(a, b, fields));
|
||||||
|
|
||||||
|
state.set(items);
|
||||||
|
|
||||||
|
List::new(store)
|
||||||
|
.empty_message("You do not have rooms or dms yet")
|
||||||
|
.empty_alignment(Alignment::Center)
|
||||||
|
.focus(focused)
|
||||||
|
.render(area, buf, state);
|
||||||
|
},
|
||||||
IambWindow::SpaceList(state) => {
|
IambWindow::SpaceList(state) => {
|
||||||
let mut items = store
|
let mut items = store
|
||||||
.application
|
.application
|
||||||
|
@ -630,6 +666,7 @@ impl WindowOps<IambInfo> for IambWindow {
|
||||||
IambWindow::VerifyList(w) => w.dup(store).into(),
|
IambWindow::VerifyList(w) => w.dup(store).into(),
|
||||||
IambWindow::Welcome(w) => w.dup(store).into(),
|
IambWindow::Welcome(w) => w.dup(store).into(),
|
||||||
IambWindow::ChatList(w) => w.dup(store).into(),
|
IambWindow::ChatList(w) => w.dup(store).into(),
|
||||||
|
IambWindow::UnreadList(w) => w.dup(store).into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -670,6 +707,7 @@ impl Window<IambInfo> for IambWindow {
|
||||||
IambWindow::VerifyList(_) => IambId::VerifyList,
|
IambWindow::VerifyList(_) => IambId::VerifyList,
|
||||||
IambWindow::Welcome(_) => IambId::Welcome,
|
IambWindow::Welcome(_) => IambId::Welcome,
|
||||||
IambWindow::ChatList(_) => IambId::ChatList,
|
IambWindow::ChatList(_) => IambId::ChatList,
|
||||||
|
IambWindow::UnreadList(_) => IambId::UnreadList,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -681,6 +719,7 @@ impl Window<IambInfo> for IambWindow {
|
||||||
IambWindow::VerifyList(_) => bold_spans("Verifications"),
|
IambWindow::VerifyList(_) => bold_spans("Verifications"),
|
||||||
IambWindow::Welcome(_) => bold_spans("Welcome to iamb"),
|
IambWindow::Welcome(_) => bold_spans("Welcome to iamb"),
|
||||||
IambWindow::ChatList(_) => bold_spans("DMs & Rooms"),
|
IambWindow::ChatList(_) => bold_spans("DMs & Rooms"),
|
||||||
|
IambWindow::UnreadList(_) => bold_spans("Unread Messages"),
|
||||||
|
|
||||||
IambWindow::Room(w) => {
|
IambWindow::Room(w) => {
|
||||||
let title = store.application.get_room_title(w.id());
|
let title = store.application.get_room_title(w.id());
|
||||||
|
@ -708,6 +747,7 @@ impl Window<IambInfo> for IambWindow {
|
||||||
IambWindow::VerifyList(_) => bold_spans("Verifications"),
|
IambWindow::VerifyList(_) => bold_spans("Verifications"),
|
||||||
IambWindow::Welcome(_) => bold_spans("Welcome to iamb"),
|
IambWindow::Welcome(_) => bold_spans("Welcome to iamb"),
|
||||||
IambWindow::ChatList(_) => bold_spans("DMs & Rooms"),
|
IambWindow::ChatList(_) => bold_spans("DMs & Rooms"),
|
||||||
|
IambWindow::UnreadList(_) => bold_spans("Unread Messages"),
|
||||||
|
|
||||||
IambWindow::Room(w) => w.get_title(store),
|
IambWindow::Room(w) => w.get_title(store),
|
||||||
IambWindow::MemberList(state, room_id, _) => {
|
IambWindow::MemberList(state, room_id, _) => {
|
||||||
|
@ -769,6 +809,11 @@ impl Window<IambInfo> for IambWindow {
|
||||||
|
|
||||||
Ok(list.into())
|
Ok(list.into())
|
||||||
},
|
},
|
||||||
|
IambId::UnreadList => {
|
||||||
|
let list = UnreadListState::new(IambBufferId::UnreadList, vec![]);
|
||||||
|
|
||||||
|
Ok(IambWindow::UnreadList(list))
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue