mirror of
https://github.com/youwen5/iamb.git
synced 2025-06-19 21:29:52 -07:00
Add a new :chats
window that lists both DMs and Rooms (#184)
Fixes #172
This commit is contained in:
parent
88af9bfec3
commit
b7ae01499b
7 changed files with 211 additions and 19 deletions
19
src/base.rs
19
src/base.rs
|
@ -1172,7 +1172,7 @@ pub enum IambId {
|
|||
/// A Matrix room.
|
||||
Room(OwnedRoomId),
|
||||
|
||||
/// The `:rooms` window.
|
||||
/// The `:dms` window.
|
||||
DirectList,
|
||||
|
||||
/// The `:members` window for a given Matrix room.
|
||||
|
@ -1189,6 +1189,9 @@ pub enum IambId {
|
|||
|
||||
/// The `:welcome` window.
|
||||
Welcome,
|
||||
|
||||
/// The `:chats` window.
|
||||
ChatList,
|
||||
}
|
||||
|
||||
impl Display for IambId {
|
||||
|
@ -1205,6 +1208,7 @@ impl Display for IambId {
|
|||
IambId::SpaceList => f.write_str("iamb://spaces"),
|
||||
IambId::VerifyList => f.write_str("iamb://verify"),
|
||||
IambId::Welcome => f.write_str("iamb://welcome"),
|
||||
IambId::ChatList => f.write_str("iamb://chats"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1317,6 +1321,13 @@ impl<'de> Visitor<'de> for IambIdVisitor {
|
|||
|
||||
Ok(IambId::Welcome)
|
||||
},
|
||||
Some("chats") => {
|
||||
if url.path() != "" {
|
||||
return Err(E::custom("iamb://chats takes no path"));
|
||||
}
|
||||
|
||||
Ok(IambId::ChatList)
|
||||
},
|
||||
Some(s) => Err(E::custom(format!("{s:?} is not a valid window"))),
|
||||
None => Err(E::custom("Invalid iamb window URL")),
|
||||
}
|
||||
|
@ -1374,6 +1385,9 @@ pub enum IambBufferId {
|
|||
|
||||
/// The buffer for the `:rooms` window.
|
||||
Welcome,
|
||||
|
||||
/// The `:chats` window.
|
||||
ChatList,
|
||||
}
|
||||
|
||||
impl IambBufferId {
|
||||
|
@ -1388,6 +1402,7 @@ impl IambBufferId {
|
|||
IambBufferId::SpaceList => Some(IambId::SpaceList),
|
||||
IambBufferId::VerifyList => Some(IambId::VerifyList),
|
||||
IambBufferId::Welcome => Some(IambId::Welcome),
|
||||
IambBufferId::ChatList => Some(IambId::ChatList),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1410,7 +1425,6 @@ impl ApplicationInfo for IambInfo {
|
|||
match content {
|
||||
IambBufferId::Command(CommandType::Command) => complete_cmdbar(text, cursor, store),
|
||||
IambBufferId::Command(CommandType::Search) => vec![],
|
||||
|
||||
IambBufferId::Room(_, RoomFocus::MessageBar) => complete_msgbar(text, cursor, store),
|
||||
IambBufferId::Room(_, RoomFocus::Scrollback) => vec![],
|
||||
|
||||
|
@ -1420,6 +1434,7 @@ impl ApplicationInfo for IambInfo {
|
|||
IambBufferId::SpaceList => vec![],
|
||||
IambBufferId::VerifyList => vec![],
|
||||
IambBufferId::Welcome => vec![],
|
||||
IambBufferId::ChatList => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -292,6 +292,17 @@ fn iamb_rooms(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult {
|
|||
return Ok(step);
|
||||
}
|
||||
|
||||
fn iamb_chats(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult {
|
||||
if !desc.arg.text.is_empty() {
|
||||
return Result::Err(CommandError::InvalidArgument);
|
||||
}
|
||||
|
||||
let open = ctx.switch(OpenTarget::Application(IambId::ChatList));
|
||||
let step = CommandStep::Continue(open, ctx.context.take());
|
||||
|
||||
return Ok(step);
|
||||
}
|
||||
|
||||
fn iamb_spaces(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult {
|
||||
if !desc.arg.text.is_empty() {
|
||||
return Result::Err(CommandError::InvalidArgument);
|
||||
|
@ -495,6 +506,11 @@ fn add_iamb_commands(cmds: &mut ProgramCommands) {
|
|||
aliases: vec![],
|
||||
f: iamb_create,
|
||||
});
|
||||
cmds.add_command(ProgramCommand {
|
||||
name: "chats".into(),
|
||||
aliases: vec![],
|
||||
f: iamb_chats,
|
||||
});
|
||||
cmds.add_command(ProgramCommand { name: "dms".into(), aliases: vec![], f: iamb_dms });
|
||||
cmds.add_command(ProgramCommand {
|
||||
name: "download".into(),
|
||||
|
|
|
@ -227,6 +227,7 @@ pub type UserOverrides = HashMap<OwnedUserId, UserDisplayTunables>;
|
|||
|
||||
fn merge_sorts(a: SortOverrides, b: SortOverrides) -> SortOverrides {
|
||||
SortOverrides {
|
||||
chats: b.chats.or(a.chats),
|
||||
dms: b.dms.or(a.dms),
|
||||
rooms: b.rooms.or(a.rooms),
|
||||
spaces: b.spaces.or(a.spaces),
|
||||
|
@ -308,6 +309,7 @@ pub struct ImagePreviewProtocolValues {
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct SortValues {
|
||||
pub chats: Vec<SortColumn<SortFieldRoom>>,
|
||||
pub dms: Vec<SortColumn<SortFieldRoom>>,
|
||||
pub rooms: Vec<SortColumn<SortFieldRoom>>,
|
||||
pub spaces: Vec<SortColumn<SortFieldRoom>>,
|
||||
|
@ -316,6 +318,7 @@ pub struct SortValues {
|
|||
|
||||
#[derive(Clone, Default, Deserialize)]
|
||||
pub struct SortOverrides {
|
||||
pub chats: Option<Vec<SortColumn<SortFieldRoom>>>,
|
||||
pub dms: Option<Vec<SortColumn<SortFieldRoom>>>,
|
||||
pub rooms: Option<Vec<SortColumn<SortFieldRoom>>>,
|
||||
pub spaces: Option<Vec<SortColumn<SortFieldRoom>>>,
|
||||
|
@ -325,11 +328,12 @@ pub struct SortOverrides {
|
|||
impl SortOverrides {
|
||||
pub fn values(self) -> SortValues {
|
||||
let rooms = self.rooms.unwrap_or_else(|| Vec::from(DEFAULT_ROOM_SORT));
|
||||
let chats = self.chats.unwrap_or_else(|| rooms.clone());
|
||||
let dms = self.dms.unwrap_or_else(|| rooms.clone());
|
||||
let spaces = self.spaces.unwrap_or_else(|| rooms.clone());
|
||||
let members = self.members.unwrap_or_else(|| Vec::from(DEFAULT_MEMBERS_SORT));
|
||||
|
||||
SortValues { rooms, members, dms, spaces }
|
||||
SortValues { rooms, members, chats, dms, spaces }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -196,7 +196,7 @@ impl Ord for MessageTimeStamp {
|
|||
|
||||
impl PartialOrd for MessageTimeStamp {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.cmp(other).into()
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -340,7 +340,7 @@ impl Ord for MessageCursor {
|
|||
|
||||
impl PartialOrd for MessageCursor {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.cmp(other).into()
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -215,28 +215,34 @@ fn user_fields_cmp(
|
|||
user_cmp(a, b, &SortFieldUser::UserId)
|
||||
}
|
||||
|
||||
fn append_tags<'a>(tags: &'a Tags, spans: &mut Vec<Span<'a>>, style: Style) {
|
||||
fn tag_to_span(tag: &TagName, style: Style) -> Vec<Span<'_>> {
|
||||
match tag {
|
||||
TagName::Favorite => vec![Span::styled("Favorite", style)],
|
||||
TagName::LowPriority => vec![Span::styled("Low Priority", style)],
|
||||
TagName::ServerNotice => vec![Span::styled("Server Notice", style)],
|
||||
TagName::User(tag) => {
|
||||
vec![
|
||||
Span::styled("User Tag: ", style),
|
||||
Span::styled(tag.as_ref(), style),
|
||||
]
|
||||
},
|
||||
tag => vec![Span::styled(format!("{tag:?}"), style)],
|
||||
}
|
||||
}
|
||||
|
||||
fn append_tags<'a>(tags: Vec<Vec<Span<'a>>>, spans: &mut Vec<Span<'a>>, style: Style) {
|
||||
if tags.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
spans.push(Span::styled(" (", style));
|
||||
|
||||
for (i, tag) in tags.keys().enumerate() {
|
||||
for (i, tag) in tags.into_iter().enumerate() {
|
||||
if i > 0 {
|
||||
spans.push(Span::styled(", ", style));
|
||||
}
|
||||
|
||||
match tag {
|
||||
TagName::Favorite => spans.push(Span::styled("Favorite", style)),
|
||||
TagName::LowPriority => spans.push(Span::styled("Low Priority", style)),
|
||||
TagName::ServerNotice => spans.push(Span::styled("Server Notice", style)),
|
||||
TagName::User(tag) => {
|
||||
spans.push(Span::styled("User Tag: ", style));
|
||||
spans.push(Span::styled(tag.as_ref(), style));
|
||||
},
|
||||
tag => spans.push(Span::styled(format!("{tag:?}"), style)),
|
||||
}
|
||||
spans.extend(tag);
|
||||
}
|
||||
|
||||
spans.push(Span::styled(")", style));
|
||||
|
@ -289,6 +295,7 @@ macro_rules! delegate {
|
|||
IambWindow::SpaceList($id) => $e,
|
||||
IambWindow::VerifyList($id) => $e,
|
||||
IambWindow::Welcome($id) => $e,
|
||||
IambWindow::ChatList($id) => $e,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -301,6 +308,7 @@ pub enum IambWindow {
|
|||
RoomList(RoomListState),
|
||||
SpaceList(SpaceListState),
|
||||
Welcome(WelcomeState),
|
||||
ChatList(ChatListState),
|
||||
}
|
||||
|
||||
impl IambWindow {
|
||||
|
@ -355,9 +363,16 @@ impl IambWindow {
|
|||
pub type DirectListState = ListState<DirectItem, IambInfo>;
|
||||
pub type MemberListState = ListState<MemberItem, IambInfo>;
|
||||
pub type RoomListState = ListState<RoomItem, IambInfo>;
|
||||
pub type ChatListState = ListState<GenericChatItem, IambInfo>;
|
||||
pub type SpaceListState = ListState<SpaceItem, IambInfo>;
|
||||
pub type VerifyListState = ListState<VerifyItem, IambInfo>;
|
||||
|
||||
impl From<ChatListState> for IambWindow {
|
||||
fn from(list: ChatListState) -> Self {
|
||||
IambWindow::ChatList(list)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RoomState> for IambWindow {
|
||||
fn from(room: RoomState) -> Self {
|
||||
IambWindow::Room(room)
|
||||
|
@ -514,6 +529,37 @@ impl WindowOps<IambInfo> for IambWindow {
|
|||
.focus(focused)
|
||||
.render(area, buf, state);
|
||||
},
|
||||
IambWindow::ChatList(state) => {
|
||||
let mut items = store
|
||||
.application
|
||||
.sync_info
|
||||
.rooms
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|room_info| GenericChatItem::new(room_info, store, false))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let dms = store
|
||||
.application
|
||||
.sync_info
|
||||
.dms
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|room_info| GenericChatItem::new(room_info, store, true));
|
||||
|
||||
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) => {
|
||||
let mut items = store
|
||||
.application
|
||||
|
@ -564,6 +610,7 @@ impl WindowOps<IambInfo> for IambWindow {
|
|||
IambWindow::SpaceList(w) => w.dup(store).into(),
|
||||
IambWindow::VerifyList(w) => w.dup(store).into(),
|
||||
IambWindow::Welcome(w) => w.dup(store).into(),
|
||||
IambWindow::ChatList(w) => w.dup(store).into(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -603,6 +650,7 @@ impl Window<IambInfo> for IambWindow {
|
|||
IambWindow::SpaceList(_) => IambId::SpaceList,
|
||||
IambWindow::VerifyList(_) => IambId::VerifyList,
|
||||
IambWindow::Welcome(_) => IambId::Welcome,
|
||||
IambWindow::ChatList(_) => IambId::ChatList,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -613,6 +661,7 @@ impl Window<IambInfo> for IambWindow {
|
|||
IambWindow::SpaceList(_) => bold_spans("Spaces"),
|
||||
IambWindow::VerifyList(_) => bold_spans("Verifications"),
|
||||
IambWindow::Welcome(_) => bold_spans("Welcome to iamb"),
|
||||
IambWindow::ChatList(_) => bold_spans("DMs & Rooms"),
|
||||
|
||||
IambWindow::Room(w) => {
|
||||
let title = store.application.get_room_title(w.id());
|
||||
|
@ -639,6 +688,7 @@ impl Window<IambInfo> for IambWindow {
|
|||
IambWindow::SpaceList(_) => bold_spans("Spaces"),
|
||||
IambWindow::VerifyList(_) => bold_spans("Verifications"),
|
||||
IambWindow::Welcome(_) => bold_spans("Welcome to iamb"),
|
||||
IambWindow::ChatList(_) => bold_spans("DMs & Rooms"),
|
||||
|
||||
IambWindow::Room(w) => w.get_title(store),
|
||||
IambWindow::MemberList(state, room_id, _) => {
|
||||
|
@ -695,6 +745,11 @@ impl Window<IambInfo> for IambWindow {
|
|||
|
||||
return Ok(win.into());
|
||||
},
|
||||
IambId::ChatList => {
|
||||
let list = ChatListState::new(IambBufferId::ChatList, vec![]);
|
||||
|
||||
Ok(list.into())
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -729,6 +784,105 @@ impl Window<IambInfo> for IambWindow {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GenericChatItem {
|
||||
room_info: MatrixRoomInfo,
|
||||
name: String,
|
||||
alias: Option<OwnedRoomAliasId>,
|
||||
is_dm: bool,
|
||||
}
|
||||
|
||||
impl GenericChatItem {
|
||||
fn new(room_info: MatrixRoomInfo, store: &mut ProgramStore, is_dm: bool) -> Self {
|
||||
let room = &room_info.deref().0;
|
||||
let room_id = room.room_id();
|
||||
|
||||
let info = store.application.get_room_info(room_id.to_owned());
|
||||
|
||||
let name = info.name.clone().unwrap_or_default();
|
||||
let alias = room.canonical_alias();
|
||||
info.tags = room_info.deref().1.clone();
|
||||
|
||||
if let Some(alias) = &alias {
|
||||
store.application.names.insert(alias.to_string(), room_id.to_owned());
|
||||
}
|
||||
|
||||
GenericChatItem { room_info, name, alias, is_dm }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn room(&self) -> &MatrixRoom {
|
||||
&self.room_info.deref().0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn tags(&self) -> &Option<Tags> {
|
||||
&self.room_info.deref().1
|
||||
}
|
||||
}
|
||||
|
||||
impl RoomLikeItem for GenericChatItem {
|
||||
fn name(&self) -> &str {
|
||||
self.name.as_str()
|
||||
}
|
||||
|
||||
fn alias(&self) -> Option<&RoomAliasId> {
|
||||
self.alias.as_deref()
|
||||
}
|
||||
|
||||
fn room_id(&self) -> &RoomId {
|
||||
self.room().room_id()
|
||||
}
|
||||
|
||||
fn has_tag(&self, tag: TagName) -> bool {
|
||||
if let Some(tags) = &self.room_info.deref().1 {
|
||||
tags.contains_key(&tag)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for GenericChatItem {
|
||||
fn to_string(&self) -> String {
|
||||
return self.name.clone();
|
||||
}
|
||||
}
|
||||
|
||||
impl ListItem<IambInfo> for GenericChatItem {
|
||||
fn show(&self, selected: bool, _: &ViewportContext<ListCursor>, _: &mut ProgramStore) -> Text {
|
||||
let style = selected_style(selected);
|
||||
let mut spans = vec![Span::styled(self.name.as_str(), style)];
|
||||
let mut labels = if self.is_dm {
|
||||
vec![vec![Span::styled("DM", style)]]
|
||||
} else {
|
||||
vec![vec![Span::styled("Room", style)]]
|
||||
};
|
||||
|
||||
if let Some(tags) = &self.tags() {
|
||||
labels.extend(tags.keys().map(|t| tag_to_span(t, style)));
|
||||
}
|
||||
|
||||
append_tags(labels, &mut spans, style);
|
||||
Text::from(Line::from(spans))
|
||||
}
|
||||
|
||||
fn get_word(&self) -> Option<String> {
|
||||
self.room_id().to_string().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Promptable<ProgramContext, ProgramStore, IambInfo> for GenericChatItem {
|
||||
fn prompt(
|
||||
&mut self,
|
||||
act: &PromptAction,
|
||||
ctx: &ProgramContext,
|
||||
_: &mut ProgramStore,
|
||||
) -> EditResult<Vec<(ProgramAction, ProgramContext)>, IambInfo> {
|
||||
room_prompt(self.room_id(), act, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RoomItem {
|
||||
room_info: MatrixRoomInfo,
|
||||
|
@ -797,6 +951,7 @@ impl ListItem<IambInfo> for RoomItem {
|
|||
if let Some(tags) = &self.tags() {
|
||||
let style = selected_style(selected);
|
||||
let mut spans = vec![Span::styled(self.name.as_str(), style)];
|
||||
let tags = tags.keys().map(|t| tag_to_span(t, style)).collect();
|
||||
|
||||
append_tags(tags, &mut spans, style);
|
||||
|
||||
|
@ -882,6 +1037,7 @@ impl ListItem<IambInfo> for DirectItem {
|
|||
if let Some(tags) = &self.tags() {
|
||||
let style = selected_style(selected);
|
||||
let mut spans = vec![Span::styled(self.name.as_str(), style)];
|
||||
let tags = tags.keys().map(|t| tag_to_span(t, style)).collect();
|
||||
|
||||
append_tags(tags, &mut spans, style);
|
||||
|
||||
|
@ -1069,7 +1225,7 @@ impl Ord for VerifyItem {
|
|||
|
||||
impl PartialOrd for VerifyItem {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.cmp(other).into()
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
- `:dms` will open a list of direct messages
|
||||
- `:rooms` will open a list of joined rooms
|
||||
- `:chats` will open a list containing both direct messages and rooms
|
||||
- `:members` will open a list of members for the currently focused room or space
|
||||
- `:spaces` will open a list of joined spaces
|
||||
- `:join` can be used to switch to join a new room or start a direct message
|
||||
|
|
|
@ -832,7 +832,7 @@ impl ClientWorker {
|
|||
let room_id = room.room_id().to_owned();
|
||||
let room_name = Some(room_name.to_string());
|
||||
let mut locked = store.lock().await;
|
||||
let mut info = locked.application.rooms.get_or_default(room_id.clone());
|
||||
let info = locked.application.rooms.get_or_default(room_id.clone());
|
||||
info.name = room_name;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue