Skip to content

Commit 8776167

Browse files
committed
WIP: chat-history: Add pinned messages bar and window
1 parent a6eb7b8 commit 8776167

File tree

9 files changed

+242
-10
lines changed

9 files changed

+242
-10
lines changed

data/resources/ui/content-chat-history.ui

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@
3434
</child>
3535
</object>
3636
</child>
37+
<child>
38+
<object class="PinnedMessagesBar" id="pinned_messages_bar"/>
39+
</child>
40+
<child>
41+
<object class="GtkSeparator"/>
42+
</child>
3743
<child>
3844
<object class="MessageListView" id="message_list_view">
3945
<property name="vexpand">True</property>

src/components/message_list_view/mod.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ use crate::Session;
2121

2222
const MIN_N_ITEMS: u32 = 20;
2323

24+
#[derive(Debug, Default, Clone, Copy)]
25+
pub(crate) enum MessageListViewType {
26+
#[default]
27+
ChatHistory,
28+
PinnedMessages,
29+
}
30+
2431
mod imp {
2532
use super::*;
2633
use std::cell::{Cell, RefCell};
@@ -105,9 +112,9 @@ glib::wrapper! {
105112
}
106113

107114
impl MessageListView {
108-
pub(crate) fn load_messages(&self, chat: &Chat) {
115+
pub(crate) fn load_messages(&self, type_: MessageListViewType, chat: &Chat) {
109116
let imp = self.imp();
110-
let model = MessageListViewModel::new(chat);
117+
let model = MessageListViewModel::new(type_, chat);
111118

112119
// Request sponsored message, if needed
113120
let list_view_model: gio::ListModel = if matches!(chat.type_(), ChatType::Supergroup(supergroup) if supergroup.is_channel())

src/components/message_list_view/model.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ use gtk::prelude::*;
33
use gtk::subclass::prelude::*;
44
use gtk::{gio, glib};
55
use std::cmp::Ordering;
6+
use tdlib::enums::SearchMessagesFilter;
67
use thiserror::Error;
78

8-
use super::{MessageListViewItem, MessageListViewItemType};
9+
use super::{MessageListViewItem, MessageListViewItemType, MessageListViewType};
910
use crate::tdlib::{Chat, Message};
1011

1112
#[derive(Error, Debug)]
@@ -25,6 +26,7 @@ mod imp {
2526

2627
#[derive(Debug, Default)]
2728
pub(crate) struct MessageListViewModel {
29+
pub(super) type_: Cell<MessageListViewType>,
2830
pub(super) chat: WeakRef<Chat>,
2931
pub(super) is_loading: Cell<bool>,
3032
pub(super) list: RefCell<VecDeque<MessageListViewItem>>,
@@ -82,9 +84,10 @@ glib::wrapper! {
8284
}
8385

8486
impl MessageListViewModel {
85-
pub(crate) fn new(chat: &Chat) -> Self {
87+
pub(crate) fn new(type_: MessageListViewType, chat: &Chat) -> Self {
8688
let obj: MessageListViewModel = glib::Object::new();
8789

90+
obj.imp().type_.set(type_);
8891
obj.imp().chat.set(Some(chat));
8992

9093
chat.connect_new_message(clone!(@weak obj => move |_, message| {
@@ -121,7 +124,21 @@ impl MessageListViewModel {
121124

122125
imp.is_loading.set(true);
123126

124-
let result = self.chat().get_chat_history(oldest_message_id, limit).await;
127+
let result = match imp.type_.get() {
128+
MessageListViewType::ChatHistory => {
129+
self.chat().get_chat_history(oldest_message_id, limit).await
130+
}
131+
MessageListViewType::PinnedMessages => {
132+
self.chat()
133+
.search_messages(
134+
String::new(),
135+
oldest_message_id,
136+
limit,
137+
Some(SearchMessagesFilter::Pinned),
138+
)
139+
.await
140+
}
141+
};
125142

126143
imp.is_loading.set(false);
127144

src/components/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ mod snow;
55

66
pub(crate) use self::avatar::Avatar;
77
pub(crate) use self::message_entry::MessageEntry;
8-
pub(crate) use self::message_list_view::MessageListView;
8+
pub(crate) use self::message_list_view::{MessageListView, MessageListViewType};
99
pub(crate) use self::snow::Snow;

src/session/content/chat_history.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ use gtk::{glib, CompositeTemplate};
55
use tdlib::enums::ChatMemberStatus;
66
use tdlib::functions;
77

8-
use super::{ChatActionBar, ChatInfoWindow};
9-
use crate::components::MessageListView;
8+
use super::{ChatActionBar, ChatInfoWindow, PinnedMessagesBar, PinnedMessagesWindow};
9+
use crate::components::{MessageListView, MessageListViewType};
1010
use crate::expressions;
1111
use crate::tdlib::{Chat, ChatType};
1212

@@ -26,6 +26,8 @@ mod imp {
2626
#[template_child]
2727
pub(super) window_title: TemplateChild<adw::WindowTitle>,
2828
#[template_child]
29+
pub(super) pinned_messages_bar: TemplateChild<PinnedMessagesBar>,
30+
#[template_child]
2931
pub(super) message_list_view: TemplateChild<MessageListView>,
3032
#[template_child]
3133
pub(super) chat_action_bar: TemplateChild<ChatActionBar>,
@@ -62,6 +64,13 @@ mod imp {
6264
widget.show_leave_chat_dialog().await;
6365
},
6466
);
67+
klass.install_action(
68+
"chat-history.show-pinned-messages",
69+
None,
70+
move |widget, _, _| {
71+
widget.open_pinned_messages_window();
72+
},
73+
);
6574
}
6675

6776
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
@@ -168,6 +177,12 @@ impl ChatHistory {
168177
}
169178
}
170179

180+
fn open_pinned_messages_window(&self) {
181+
if let Some(chat) = self.chat() {
182+
PinnedMessagesWindow::new(self.parent_window().as_ref(), &chat).present();
183+
}
184+
}
185+
171186
async fn show_leave_chat_dialog(&self) {
172187
if let Some(chat) = self.chat() {
173188
let dialog = adw::MessageDialog::new(
@@ -238,7 +253,8 @@ impl ChatHistory {
238253
},
239254
);
240255

241-
imp.message_list_view.load_messages(chat);
256+
imp.message_list_view
257+
.load_messages(MessageListViewType::ChatHistory, chat);
242258
}
243259

244260
imp.chat.replace(chat);

src/session/content/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
mod chat_action_bar;
22
mod chat_history;
33
mod chat_info_window;
4+
mod pinned_messages_bar;
5+
mod pinned_messages_window;
46
mod send_photo_dialog;
57

68
use self::chat_action_bar::ChatActionBar;
79
use self::chat_history::ChatHistory;
810
use self::chat_info_window::ChatInfoWindow;
11+
use self::pinned_messages_bar::PinnedMessagesBar;
12+
use self::pinned_messages_window::PinnedMessagesWindow;
913
use self::send_photo_dialog::SendPhotoDialog;
1014

1115
use gtk::glib;
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
use gtk::prelude::*;
2+
use gtk::subclass::prelude::*;
3+
use gtk::{glib, CompositeTemplate};
4+
5+
mod imp {
6+
use super::*;
7+
8+
#[derive(Debug, Default, CompositeTemplate)]
9+
#[template(string = r#"
10+
template PinnedMessagesBar {
11+
Box content_box {
12+
styles ["toolbar"]
13+
14+
Box {
15+
orientation: vertical;
16+
hexpand: true;
17+
18+
Inscription {
19+
20+
}
21+
22+
Inscription {
23+
24+
}
25+
}
26+
27+
Button {
28+
icon-name: "view-list-symbolic";
29+
action-name: "chat-history.show-pinned-messages";
30+
}
31+
}
32+
}
33+
"#)]
34+
pub(crate) struct PinnedMessagesBar {
35+
#[template_child]
36+
pub(super) content_box: TemplateChild<gtk::Box>,
37+
}
38+
39+
#[glib::object_subclass]
40+
impl ObjectSubclass for PinnedMessagesBar {
41+
const NAME: &'static str = "PinnedMessagesBar";
42+
type Type = super::PinnedMessagesBar;
43+
type ParentType = gtk::Widget;
44+
45+
fn class_init(klass: &mut Self::Class) {
46+
klass.bind_template();
47+
klass.set_layout_manager_type::<gtk::BinLayout>();
48+
klass.set_css_name("messagelistview");
49+
}
50+
51+
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
52+
obj.init_template();
53+
}
54+
}
55+
56+
impl ObjectImpl for PinnedMessagesBar {
57+
fn dispose(&self) {
58+
self.dispose_template();
59+
}
60+
}
61+
62+
impl WidgetImpl for PinnedMessagesBar {}
63+
}
64+
65+
glib::wrapper! {
66+
pub(crate) struct PinnedMessagesBar(ObjectSubclass<imp::PinnedMessagesBar>)
67+
@extends gtk::Widget;
68+
}
69+
70+
impl PinnedMessagesBar {}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use adw::subclass::prelude::*;
2+
use gtk::prelude::*;
3+
use gtk::{glib, CompositeTemplate};
4+
5+
use crate::components::{MessageListView, MessageListViewType};
6+
use crate::tdlib::Chat;
7+
8+
mod imp {
9+
use super::*;
10+
11+
#[derive(Debug, Default, CompositeTemplate)]
12+
#[template(string = r#"
13+
using Adw 1;
14+
15+
template PinnedMessagesWindow : Adw.Window {
16+
title: _("Pinned Messages");
17+
modal: true;
18+
default-width: 360;
19+
default-height: 600;
20+
21+
Box {
22+
orientation: vertical;
23+
24+
Adw.HeaderBar {}
25+
26+
.MessageListView message_list_view {
27+
vexpand: true;
28+
}
29+
}
30+
}
31+
"#)]
32+
pub(crate) struct PinnedMessagesWindow {
33+
#[template_child]
34+
pub(super) message_list_view: TemplateChild<MessageListView>,
35+
}
36+
37+
#[glib::object_subclass]
38+
impl ObjectSubclass for PinnedMessagesWindow {
39+
const NAME: &'static str = "PinnedMessagesWindow";
40+
type Type = super::PinnedMessagesWindow;
41+
type ParentType = adw::Window;
42+
43+
fn class_init(klass: &mut Self::Class) {
44+
klass.bind_template();
45+
}
46+
47+
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
48+
obj.init_template();
49+
}
50+
}
51+
52+
impl ObjectImpl for PinnedMessagesWindow {}
53+
impl WidgetImpl for PinnedMessagesWindow {}
54+
impl WindowImpl for PinnedMessagesWindow {}
55+
impl AdwWindowImpl for PinnedMessagesWindow {}
56+
}
57+
58+
glib::wrapper! {
59+
pub(crate) struct PinnedMessagesWindow(ObjectSubclass<imp::PinnedMessagesWindow>)
60+
@extends gtk::Widget, gtk::Window, adw::Window;
61+
}
62+
63+
impl PinnedMessagesWindow {
64+
pub(crate) fn new(parent: Option<&gtk::Window>, chat: &Chat) -> Self {
65+
let obj: Self = glib::Object::builder()
66+
.property("transient-for", parent)
67+
.build();
68+
69+
obj.imp()
70+
.message_list_view
71+
.load_messages(MessageListViewType::PinnedMessages, chat);
72+
73+
obj
74+
}
75+
}

src/tdlib/chat.rs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use gtk::glib;
22
use gtk::prelude::*;
33
use gtk::subclass::prelude::*;
4-
use tdlib::enums::{ChatType as TdChatType, Update};
4+
use tdlib::enums::{ChatType as TdChatType, SearchMessagesFilter, Update};
55
use tdlib::types::Chat as TelegramChat;
66
use tdlib::{functions, types};
77

@@ -571,6 +571,43 @@ impl Chat {
571571
Ok(loaded_messages)
572572
}
573573

574+
pub(crate) async fn search_messages(
575+
&self,
576+
query: String,
577+
from_message_id: i64,
578+
limit: i32,
579+
filter: Option<SearchMessagesFilter>,
580+
) -> Result<Vec<Message>, types::Error> {
581+
let client_id = self.session().client_id();
582+
let result = functions::search_chat_messages(
583+
self.id(),
584+
query,
585+
None,
586+
from_message_id,
587+
0,
588+
limit,
589+
filter,
590+
0,
591+
client_id,
592+
)
593+
.await;
594+
595+
let tdlib::enums::FoundChatMessages::FoundChatMessages(data) = result?;
596+
597+
let mut messages = self.imp().messages.borrow_mut();
598+
let loaded_messages: Vec<Message> = data
599+
.messages
600+
.into_iter()
601+
.map(|m| Message::new(m, self))
602+
.collect();
603+
604+
for message in &loaded_messages {
605+
messages.insert(message.id(), message.clone());
606+
}
607+
608+
Ok(loaded_messages)
609+
}
610+
574611
pub(crate) async fn mark_as_read(&self) -> Result<(), types::Error> {
575612
if let Some(message) = self.last_message() {
576613
functions::view_messages(

0 commit comments

Comments
 (0)