Skip to content

Commit 96981cd

Browse files
committed
WIP: content: Add pinned messages bar and view
1 parent e707eaa commit 96981cd

File tree

10 files changed

+280
-15
lines changed

10 files changed

+280
-15
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>

data/resources/ui/content.blp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,13 @@ template Content : Adw.Bin {
3131
}
3232
}
3333

34-
.ContentChatHistory chat_history {
35-
compact: bind Content.compact;
36-
chat: bind Content.chat;
34+
Adw.Leaflet chat_leaflet {
35+
can-unfold: false;
36+
37+
.ContentChatHistory chat_history {
38+
compact: bind Content.compact;
39+
chat: bind Content.chat;
40+
}
3741
}
3842
}
3943
}

src/components/message_list_view/mod.rs

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

2323
const MIN_N_ITEMS: u32 = 20;
2424

25+
#[derive(Debug, Default, Clone, Copy)]
26+
pub(crate) enum MessageListViewType {
27+
#[default]
28+
ChatHistory,
29+
PinnedMessages,
30+
}
31+
2532
mod imp {
2633
use super::*;
2734
use once_cell::unsync::OnceCell;
@@ -146,9 +153,9 @@ glib::wrapper! {
146153
}
147154

148155
impl MessageListView {
149-
pub(crate) fn load_messages(&self, chat: &Chat) {
156+
pub(crate) fn load_messages(&self, type_: MessageListViewType, chat: &Chat) {
150157
let imp = self.imp();
151-
let model = MessageListViewModel::new(chat);
158+
let model = MessageListViewModel::new(type_, chat);
152159

153160
// Request sponsored message, if needed
154161
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
@@ -6,6 +6,6 @@ mod sticker;
66

77
pub(crate) use self::avatar::Avatar;
88
pub(crate) use self::message_entry::MessageEntry;
9-
pub(crate) use self::message_list_view::MessageListView;
9+
pub(crate) use self::message_list_view::{MessageListView, MessageListViewType};
1010
pub(crate) use self::snow::Snow;
1111
pub(crate) use self::sticker::Sticker;

src/session/content/chat_history.rs

Lines changed: 6 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};
9+
use crate::components::{MessageListView, MessageListViewType};
1010
use crate::expressions;
1111
use crate::tdlib::{Chat, ChatType};
1212

@@ -24,6 +24,8 @@ mod imp {
2424
#[template_child]
2525
pub(super) window_title: TemplateChild<adw::WindowTitle>,
2626
#[template_child]
27+
pub(super) pinned_messages_bar: TemplateChild<PinnedMessagesBar>,
28+
#[template_child]
2729
pub(super) message_list_view: TemplateChild<MessageListView>,
2830
#[template_child]
2931
pub(super) chat_action_bar: TemplateChild<ChatActionBar>,
@@ -202,7 +204,8 @@ impl ChatHistory {
202204
},
203205
);
204206

205-
imp.message_list_view.load_messages(chat);
207+
imp.message_list_view
208+
.load_messages(MessageListViewType::ChatHistory, chat);
206209
}
207210

208211
imp.chat.replace(chat);

src/session/content/mod.rs

Lines changed: 30 additions & 2 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_view;
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_view::PinnedMessagesView;
913
use self::send_photo_dialog::SendPhotoDialog;
1014

1115
use gtk::glib;
@@ -31,6 +35,8 @@ mod imp {
3135
#[template_child]
3236
pub(super) unselected_chat: TemplateChild<gtk::Box>,
3337
#[template_child]
38+
pub(super) chat_leaflet: TemplateChild<adw::Leaflet>,
39+
#[template_child]
3440
pub(super) chat_history: TemplateChild<ChatHistory>,
3541
}
3642

@@ -41,8 +47,11 @@ mod imp {
4147
type ParentType = adw::Bin;
4248

4349
fn class_init(klass: &mut Self::Class) {
44-
ChatHistory::static_type();
4550
klass.bind_template();
51+
52+
klass.install_action("content.show-pinned-messages", None, move |widget, _, _| {
53+
widget.show_pinned_messages();
54+
});
4655
}
4756

4857
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
@@ -114,6 +123,16 @@ impl Content {
114123
self.imp().chat_history.handle_paste_action();
115124
}
116125

126+
fn show_pinned_messages(&self) {
127+
if let Some(chat) = self.chat() {
128+
let imp = self.imp();
129+
let pinned_messages = PinnedMessagesView::new(&chat);
130+
131+
imp.chat_leaflet.append(&pinned_messages);
132+
imp.chat_leaflet.navigate(adw::NavigationDirection::Forward);
133+
}
134+
}
135+
117136
pub(crate) fn chat(&self) -> Option<Chat> {
118137
self.imp().chat.borrow().clone()
119138
}
@@ -125,7 +144,16 @@ impl Content {
125144

126145
let imp = self.imp();
127146
if chat.is_some() {
128-
imp.stack.set_visible_child(&imp.chat_history.get());
147+
// Remove every leaflet page except the first one (the first chat history)
148+
imp.chat_leaflet
149+
.pages()
150+
.iter::<adw::LeafletPage>()
151+
.map(|p| p.unwrap())
152+
.enumerate()
153+
.filter(|(i, _)| i > &0)
154+
.for_each(|(_, p)| imp.chat_leaflet.remove(&p.child()));
155+
156+
imp.stack.set_visible_child(&imp.chat_leaflet.get());
129157
} else {
130158
imp.stack.set_visible_child(&imp.unselected_chat.get());
131159
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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: "content.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+
}
49+
50+
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
51+
obj.init_template();
52+
}
53+
}
54+
55+
impl ObjectImpl for PinnedMessagesBar {
56+
fn dispose(&self) {
57+
self.dispose_template();
58+
}
59+
}
60+
61+
impl WidgetImpl for PinnedMessagesBar {}
62+
}
63+
64+
glib::wrapper! {
65+
pub(crate) struct PinnedMessagesBar(ObjectSubclass<imp::PinnedMessagesBar>)
66+
@extends gtk::Widget;
67+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
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 PinnedMessagesView {
16+
Box content_box {
17+
orientation: vertical;
18+
19+
HeaderBar {
20+
[start]
21+
Button {
22+
icon-name: "go-previous-symbolic";
23+
action-name: "content.go-back";
24+
}
25+
}
26+
27+
.MessageListView message_list_view {
28+
vexpand: true;
29+
}
30+
31+
Separator {}
32+
33+
Box {
34+
styles ["toolbar"]
35+
36+
Button {
37+
label: _("Unpin All Messages");
38+
hexpand: true;
39+
40+
styles [
41+
"flat",
42+
"accent",
43+
]
44+
}
45+
}
46+
}
47+
}
48+
"#)]
49+
pub(crate) struct PinnedMessagesView {
50+
#[template_child]
51+
pub(super) content_box: TemplateChild<gtk::Box>,
52+
#[template_child]
53+
pub(super) message_list_view: TemplateChild<MessageListView>,
54+
}
55+
56+
#[glib::object_subclass]
57+
impl ObjectSubclass for PinnedMessagesView {
58+
const NAME: &'static str = "PinnedMessagesView";
59+
type Type = super::PinnedMessagesView;
60+
type ParentType = gtk::Widget;
61+
62+
fn class_init(klass: &mut Self::Class) {
63+
klass.bind_template();
64+
klass.set_layout_manager_type::<gtk::BinLayout>();
65+
}
66+
67+
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
68+
obj.init_template();
69+
}
70+
}
71+
72+
impl ObjectImpl for PinnedMessagesView {
73+
fn dispose(&self) {
74+
self.dispose_template();
75+
}
76+
}
77+
78+
impl WidgetImpl for PinnedMessagesView {}
79+
}
80+
81+
glib::wrapper! {
82+
pub(crate) struct PinnedMessagesView(ObjectSubclass<imp::PinnedMessagesView>)
83+
@extends gtk::Widget;
84+
}
85+
86+
impl PinnedMessagesView {
87+
pub(crate) fn new(chat: &Chat) -> Self {
88+
let obj: Self = glib::Object::new();
89+
90+
obj.imp()
91+
.message_list_view
92+
.load_messages(MessageListViewType::PinnedMessages, chat);
93+
94+
obj
95+
}
96+
}

0 commit comments

Comments
 (0)