Skip to content

Commit

Permalink
Merge pull request #62 from squidowl/feat/unread
Browse files Browse the repository at this point in the history
Feat: unread
  • Loading branch information
tarkah authored Jun 29, 2023
2 parents 23cf21c + 3175a3e commit 046b24e
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 91 deletions.
32 changes: 23 additions & 9 deletions data/src/history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use tokio::fs;
use tokio::time::Instant;

pub use self::manager::{Manager, Resource};
use crate::time::Posix;
use crate::user::Nick;
use crate::{compression, environment, message, server, Message};

Expand Down Expand Up @@ -120,29 +121,27 @@ pub enum History {
kind: Kind,
messages: Vec<Message>,
last_received_at: Option<Instant>,
unread_message_count: usize,
opened_at: Posix,
},
Full {
server: server::Server,
kind: Kind,
messages: Vec<Message>,
last_received_at: Option<Instant>,
opened_at: Posix,
},
}

impl History {
fn partial(server: server::Server, kind: Kind) -> Self {
fn partial(server: server::Server, kind: Kind, opened_at: Posix) -> Self {
Self::Partial {
server,
kind,
messages: vec![],
last_received_at: None,
}
}

fn messages(&self) -> &[Message] {
match self {
History::Partial { messages, .. } => messages,
History::Full { messages, .. } => messages,
unread_message_count: 0,
opened_at,
}
}

Expand All @@ -151,8 +150,13 @@ impl History {
History::Partial {
messages,
last_received_at,
unread_message_count,
..
} => {
if message.triggers_unread() {
*unread_message_count += 1;
}

messages.push(message);
*last_received_at = Some(Instant::now());
}
Expand All @@ -174,6 +178,7 @@ impl History {
kind,
messages,
last_received_at,
..
} => {
if let Some(last_received) = *last_received_at {
let since = now.duration_since(last_received);
Expand All @@ -195,6 +200,7 @@ impl History {
kind,
messages,
last_received_at,
..
} => {
if let Some(last_received) = *last_received_at {
let since = now.duration_since(last_received);
Expand Down Expand Up @@ -234,7 +240,7 @@ impl History {
let kind = kind.clone();
let messages = std::mem::take(messages);

*self = Self::partial(server.clone(), kind.clone());
*self = Self::partial(server.clone(), kind.clone(), Posix::now());

Some(async move { overwrite(&server, &kind, &messages).await })
}
Expand All @@ -258,6 +264,14 @@ impl History {
}
}
}

#[derive(Debug)]
pub struct View<'a> {
pub total: usize,
pub old_messages: Vec<&'a Message>,
pub new_messages: Vec<&'a Message>,
}

#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("can't resolve data directory")]
Expand Down
85 changes: 55 additions & 30 deletions data/src/history/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use tokio::time::Instant;

use crate::history::{self, History};
use crate::message::{self, Limit};
use crate::time::Posix;
use crate::user::Nick;
use crate::{server, Server};

Expand Down Expand Up @@ -133,46 +134,28 @@ impl Manager {
server: &Server,
channel: &str,
limit: Option<Limit>,
) -> (usize, Vec<&crate::Message>) {
) -> Option<history::View<'_>> {
self.data
.messages(server, &history::Kind::Channel(channel.to_string()))
.map(|messages| {
let total = messages.len();

(total, with_limit(limit, messages.iter()))
})
.unwrap_or_else(|| (0, vec![]))
.history_view(server, &history::Kind::Channel(channel.to_string()), limit)
}

pub fn get_server_messages(
&self,
server: &Server,
limit: Option<Limit>,
) -> (usize, Vec<&crate::Message>) {
) -> Option<history::View<'_>> {
self.data
.messages(server, &history::Kind::Server)
.map(|messages| {
let total = messages.len();

(total, with_limit(limit, messages.iter()))
})
.unwrap_or_else(|| (0, vec![]))
.history_view(server, &history::Kind::Server, limit)
}

pub fn get_query_messages(
&self,
server: &Server,
nick: &Nick,
limit: Option<Limit>,
) -> (usize, Vec<&crate::Message>) {
) -> Option<history::View<'_>> {
self.data
.messages(server, &history::Kind::Query(nick.clone()))
.map(|messages| {
let total = messages.len();

(total, with_limit(limit, messages.iter()))
})
.unwrap_or_else(|| (0, vec![]))
.history_view(server, &history::Kind::Query(nick.clone()), limit)
}

pub fn get_unique_queries(&self, server: &Server) -> Vec<&Nick> {
Expand All @@ -192,6 +175,23 @@ impl Manager {
queries
}

pub fn has_unread(&self, server: &Server, kind: &history::Kind) -> bool {
self.data
.map
.get(server)
.and_then(|map| map.get(kind))
.map(|history| {
matches!(
history,
History::Partial {
unread_message_count,
..
} if *unread_message_count > 0
)
})
.unwrap_or_default()
}

pub fn broadcast(&mut self, server: &Server, broadcast: Broadcast) {
let Some(map) = self.data.map.get(server) else {
return;
Expand Down Expand Up @@ -275,15 +275,18 @@ impl Data {
History::Partial {
messages: new_messages,
last_received_at,
opened_at,
..
} => {
let last_received_at = *last_received_at;
let opened_at = *opened_at;
messages.extend(std::mem::take(new_messages));
entry.insert(History::Full {
server,
kind,
messages,
last_received_at,
opened_at,
});
}
_ => {
Expand All @@ -292,6 +295,7 @@ impl Data {
kind,
messages,
last_received_at: None,
opened_at: Posix::now(),
});
}
},
Expand All @@ -301,16 +305,37 @@ impl Data {
kind,
messages,
last_received_at: None,
opened_at: Posix::now(),
});
}
}
}

fn messages(&self, server: &server::Server, kind: &history::Kind) -> Option<&[crate::Message]> {
self.map
.get(server)
.and_then(|map| map.get(kind))
.map(History::messages)
fn history_view(
&self,
server: &server::Server,
kind: &history::Kind,
limit: Option<Limit>,
) -> Option<history::View> {
let History::Full { messages, opened_at, .. } = self.map.get(server)?.get(kind)? else {
return None;
};

let total = messages.len();
let limited = with_limit(limit, messages.iter());

let split_at = limited
.iter()
.position(|message| message.received_at >= *opened_at)
.unwrap_or(limited.len());

let (old, new) = limited.split_at(split_at);

Some(history::View {
total,
old_messages: old.to_vec(),
new_messages: new.to_vec(),
})
}

fn add_message(
Expand All @@ -323,7 +348,7 @@ impl Data {
.entry(server.clone())
.or_default()
.entry(kind.clone())
.or_insert_with(|| History::partial(server, kind))
.or_insert_with(|| History::partial(server, kind, message.received_at))
.add_message(message)
}

Expand Down
15 changes: 15 additions & 0 deletions data/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@ pub enum Source {
Query(Nick, Sender),
}

impl Source {
pub fn sender(&self) -> Option<&Sender> {
match self {
Source::Server => None,
Source::Channel(_, sender) => Some(sender),
Source::Query(_, sender) => Some(sender),
}
}
}

#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Sender {
User(User),
Expand Down Expand Up @@ -92,6 +102,11 @@ impl Message {
}
}

pub fn triggers_unread(&self) -> bool {
matches!(self.direction, Direction::Received)
&& matches!(self.source.sender(), Some(Sender::User(_) | Sender::Action))
}

pub fn received(encoded: Encoded, our_nick: &Nick) -> Option<Message> {
let server_time = server_time(&encoded);
let text = text(&encoded, our_nick)?;
Expand Down
75 changes: 50 additions & 25 deletions src/buffer/scroll_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ use data::message::Limit;
use data::server::Server;
use data::user::Nick;
use data::{history, time};
use iced::widget::scrollable;
use iced::widget::{column, container, horizontal_rule, scrollable};
use iced::{Command, Length};

use crate::widget::{Column, Element};
use crate::theme;
use crate::widget::Element;

#[derive(Debug, Clone)]
pub enum Message {
Expand All @@ -31,42 +32,66 @@ pub fn view<'a>(
history: &'a history::Manager,
format: impl Fn(&'a data::Message) -> Option<Element<'a, Message>> + 'a,
) -> Element<'a, Message> {
let (total, messages) = match kind {
let Some(history::View {
total,
old_messages,
new_messages,
}) = (match kind {
Kind::Server(server) => history.get_server_messages(server, Some(state.limit)),
Kind::Channel(server, channel) => {
history.get_channel_messages(server, channel, Some(state.limit))
}
Kind::Query(server, user) => history.get_query_messages(server, user, Some(state.limit)),
}) else {
return column![].into();
};

let count = messages.len();
let count = old_messages.len() + new_messages.len();
let remaining = count < total;
let oldest = messages
.first()
let oldest = old_messages
.iter()
.chain(&new_messages)
.next()
.map(|message| message.received_at)
.unwrap_or_else(time::Posix::now);
let status = state.status;

scrollable(
Column::with_children(messages.into_iter().filter_map(format).collect())
let old = old_messages
.into_iter()
.filter_map(&format)
.collect::<Vec<_>>();
let new = new_messages
.into_iter()
.filter_map(format)
.collect::<Vec<_>>();

let show_divider = !new.is_empty() || matches!(status, Status::Idle(Anchor::Bottom));

let content = if show_divider {
let divider = container(horizontal_rule(1).style(theme::Rule::Unread))
.width(Length::Fill)
.padding([0, 8]),
)
.vertical_scroll(
scrollable::Properties::default()
.alignment(status.alignment())
.width(5)
.scroller_width(5),
)
.on_scroll(move |viewport| Message::Scrolled {
count,
remaining,
oldest,
status,
viewport,
})
.id(state.scrollable.clone())
.into()
.padding(5);
column![column(old), divider, column(new)]
} else {
column![column(old), column(new)]
};

scrollable(container(content).width(Length::Fill).padding([0, 8]))
.vertical_scroll(
scrollable::Properties::default()
.alignment(status.alignment())
.width(5)
.scroller_width(5),
)
.on_scroll(move |viewport| Message::Scrolled {
count,
remaining,
oldest,
status,
viewport,
})
.id(state.scrollable.clone())
.into()
}

#[derive(Debug, Clone)]
Expand Down
Loading

0 comments on commit 046b24e

Please sign in to comment.