10 changed files with 381 additions and 2 deletions
@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<interface> |
||||
<template class="ContentFileHistoryViewerRow" parent="AdwBin"> |
||||
<child> |
||||
<object class="GtkBox"> |
||||
<property name="spacing">12</property> |
||||
<child> |
||||
<object class="GtkButton"> |
||||
<property name="icon-name">folder-download-symbolic</property> |
||||
<property name="valign">center</property> |
||||
<style> |
||||
<class name="circular"/> |
||||
<class name="suggested-action"/> |
||||
</style> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkBox"> |
||||
<property name="orientation">vertical</property> |
||||
<property name="homogeneous">True</property> |
||||
<child> |
||||
<object class="GtkLabel" id="title_label"> |
||||
<property name="ellipsize">end</property> |
||||
<property name="xalign">0</property> |
||||
<style> |
||||
<class name="heading"/> |
||||
</style> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkLabel" id="size_label"> |
||||
<property name="ellipsize">end</property> |
||||
<property name="xalign">0</property> |
||||
<style> |
||||
<class name="dim-label"/> |
||||
<class name="numeric"/> |
||||
</style> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
</template> |
||||
</interface> |
||||
@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<interface> |
||||
<template class="ContentFileHistoryViewer" parent="AdwBin"> |
||||
<child> |
||||
<object class="GtkBox"> |
||||
<property name="orientation">vertical</property> |
||||
<child> |
||||
<object class="GtkHeaderBar"> |
||||
<property name="title-widget"> |
||||
<object class="AdwWindowTitle"> |
||||
<property name="title" translatable="yes">Files</property> |
||||
</object> |
||||
</property> |
||||
<child type="start"> |
||||
<object class="GtkButton"> |
||||
<property name="action-name">details.previous-page</property> |
||||
<property name="icon-name">go-previous-symbolic</property> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkScrolledWindow"> |
||||
<property name="hscrollbar-policy">never</property> |
||||
<property name="vexpand">True</property> |
||||
<child> |
||||
<object class="AdwClampScrollable"> |
||||
<property name="maximum-size">400</property> |
||||
<property name="tightening-threshold">400</property> |
||||
<child> |
||||
<object class="GtkListView" id="list_view"> |
||||
<property name="show-separators">True</property> |
||||
<property name="factory"> |
||||
<object class="GtkBuilderListItemFactory"> |
||||
<property name="bytes"><![CDATA[ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<interface> |
||||
<template class="GtkListItem"> |
||||
<property name="activatable">False</property> |
||||
<property name="child"> |
||||
<object class="ContentFileHistoryViewerRow"> |
||||
<property name="margin-top">6</property> |
||||
<property name="margin-bottom">6</property> |
||||
<binding name="event"> |
||||
<lookup name="item">GtkListItem</lookup> |
||||
</binding> |
||||
</object> |
||||
</property> |
||||
</template> |
||||
</interface> |
||||
]]></property> |
||||
</object> |
||||
</property> |
||||
<style> |
||||
<class name="navigation-sidebar"/> |
||||
</style> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
</template> |
||||
</interface> |
||||
@ -0,0 +1,108 @@
|
||||
use adw::{prelude::*, subclass::prelude::*}; |
||||
use gtk::{glib, glib::clone, CompositeTemplate}; |
||||
|
||||
use crate::{ |
||||
session::{ |
||||
content::room_details::history_viewer::{FileRow, Timeline, TimelineFilter}, |
||||
Room, |
||||
}, |
||||
spawn, |
||||
}; |
||||
|
||||
const MIN_N_ITEMS: u32 = 20; |
||||
|
||||
mod imp { |
||||
use glib::subclass::InitializingObject; |
||||
use once_cell::{sync::Lazy, unsync::OnceCell}; |
||||
|
||||
use super::*; |
||||
|
||||
#[derive(Debug, Default, CompositeTemplate)] |
||||
#[template(resource = "/org/gnome/Fractal/content-file-history-viewer.ui")] |
||||
pub struct FileHistoryViewer { |
||||
pub room_timeline: OnceCell<Timeline>, |
||||
#[template_child] |
||||
pub list_view: TemplateChild<gtk::ListView>, |
||||
} |
||||
|
||||
#[glib::object_subclass] |
||||
impl ObjectSubclass for FileHistoryViewer { |
||||
const NAME: &'static str = "ContentFileHistoryViewer"; |
||||
type Type = super::FileHistoryViewer; |
||||
type ParentType = adw::Bin; |
||||
|
||||
fn class_init(klass: &mut Self::Class) { |
||||
FileRow::static_type(); |
||||
Self::bind_template(klass); |
||||
|
||||
klass.set_css_name("filehistoryviewer"); |
||||
} |
||||
|
||||
fn instance_init(obj: &InitializingObject<Self>) { |
||||
obj.init_template(); |
||||
} |
||||
} |
||||
|
||||
impl ObjectImpl for FileHistoryViewer { |
||||
fn properties() -> &'static [glib::ParamSpec] { |
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { |
||||
vec![glib::ParamSpecObject::builder::<Room>("room") |
||||
.construct_only() |
||||
.build()] |
||||
}); |
||||
|
||||
PROPERTIES.as_ref() |
||||
} |
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) { |
||||
match pspec.name() { |
||||
"room" => self.obj().set_room(value.get().unwrap()), |
||||
_ => unimplemented!(), |
||||
} |
||||
} |
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { |
||||
match pspec.name() { |
||||
"room" => self.obj().room().to_value(), |
||||
_ => unimplemented!(), |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl WidgetImpl for FileHistoryViewer {} |
||||
impl BinImpl for FileHistoryViewer {} |
||||
} |
||||
|
||||
glib::wrapper! { |
||||
pub struct FileHistoryViewer(ObjectSubclass<imp::FileHistoryViewer>) |
||||
@extends gtk::Widget, adw::Bin; |
||||
} |
||||
|
||||
impl FileHistoryViewer { |
||||
pub fn new(room: &Room) -> Self { |
||||
glib::Object::builder().property("room", room).build() |
||||
} |
||||
|
||||
fn set_room(&self, room: &Room) { |
||||
let imp = self.imp(); |
||||
|
||||
let timeline = Timeline::new(room, TimelineFilter::Files); |
||||
let model = gtk::NoSelection::new(Some(timeline.clone())); |
||||
imp.list_view.set_model(Some(&model)); |
||||
|
||||
// Load an initial number of items
|
||||
spawn!(clone!(@weak timeline => async move { |
||||
while timeline.n_items() < MIN_N_ITEMS { |
||||
if !timeline.load().await { |
||||
break; |
||||
} |
||||
} |
||||
})); |
||||
|
||||
imp.room_timeline.set(timeline).unwrap(); |
||||
} |
||||
|
||||
pub fn room(&self) -> &Room { |
||||
self.imp().room_timeline.get().unwrap().room() |
||||
} |
||||
} |
||||
@ -0,0 +1,109 @@
|
||||
use adw::{prelude::*, subclass::prelude::*}; |
||||
use gettextrs::gettext; |
||||
use gtk::{glib, CompositeTemplate}; |
||||
use matrix_sdk::ruma::events::{room::message::MessageType, AnyMessageLikeEventContent}; |
||||
|
||||
use crate::session::content::room_details::history_viewer::HistoryViewerEvent; |
||||
|
||||
mod imp { |
||||
use std::cell::RefCell; |
||||
|
||||
use glib::subclass::InitializingObject; |
||||
use once_cell::sync::Lazy; |
||||
|
||||
use super::*; |
||||
|
||||
#[derive(Debug, Default, CompositeTemplate)] |
||||
#[template(resource = "/org/gnome/Fractal/content-file-history-viewer-row.ui")] |
||||
pub struct FileRow { |
||||
pub event: RefCell<Option<HistoryViewerEvent>>, |
||||
#[template_child] |
||||
pub title_label: TemplateChild<gtk::Label>, |
||||
#[template_child] |
||||
pub size_label: TemplateChild<gtk::Label>, |
||||
} |
||||
|
||||
#[glib::object_subclass] |
||||
impl ObjectSubclass for FileRow { |
||||
const NAME: &'static str = "ContentFileHistoryViewerRow"; |
||||
type Type = super::FileRow; |
||||
type ParentType = adw::Bin; |
||||
|
||||
fn class_init(klass: &mut Self::Class) { |
||||
Self::bind_template(klass); |
||||
} |
||||
|
||||
fn instance_init(obj: &InitializingObject<Self>) { |
||||
obj.init_template(); |
||||
} |
||||
} |
||||
|
||||
impl ObjectImpl for FileRow { |
||||
fn properties() -> &'static [glib::ParamSpec] { |
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| { |
||||
vec![ |
||||
glib::ParamSpecObject::builder::<HistoryViewerEvent>("event") |
||||
.explicit_notify() |
||||
.build(), |
||||
] |
||||
}); |
||||
|
||||
PROPERTIES.as_ref() |
||||
} |
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) { |
||||
match pspec.name() { |
||||
"event" => self.obj().set_event(value.get().unwrap()), |
||||
_ => unimplemented!(), |
||||
} |
||||
} |
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { |
||||
match pspec.name() { |
||||
"event" => self.obj().event().to_value(), |
||||
_ => unimplemented!(), |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl WidgetImpl for FileRow {} |
||||
impl BinImpl for FileRow {} |
||||
} |
||||
|
||||
glib::wrapper! { |
||||
pub struct FileRow(ObjectSubclass<imp::FileRow>) |
||||
@extends gtk::Widget, adw::Bin; |
||||
} |
||||
|
||||
impl FileRow { |
||||
pub fn set_event(&self, event: Option<HistoryViewerEvent>) { |
||||
let imp = self.imp(); |
||||
|
||||
if self.event() == event { |
||||
return; |
||||
} |
||||
|
||||
if let Some(ref event) = event { |
||||
if let Some(AnyMessageLikeEventContent::RoomMessage(content)) = event.original_content() |
||||
{ |
||||
if let MessageType::File(file) = content.msgtype { |
||||
imp.title_label.set_label(&file.body); |
||||
|
||||
if let Some(size) = file.info.and_then(|i| i.size) { |
||||
let size = glib::format_size(size.into()); |
||||
imp.size_label.set_label(&size); |
||||
} else { |
||||
imp.size_label.set_label(&gettext("Unknown size")); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
imp.event.replace(event); |
||||
self.notify("event"); |
||||
} |
||||
|
||||
pub fn event(&self) -> Option<HistoryViewerEvent> { |
||||
self.imp().event.borrow().clone() |
||||
} |
||||
} |
||||
@ -1,11 +1,14 @@
|
||||
mod event; |
||||
mod file; |
||||
mod file_row; |
||||
mod media; |
||||
mod media_item; |
||||
mod timeline; |
||||
|
||||
pub use self::media::MediaHistoryViewer; |
||||
use self::{ |
||||
event::HistoryViewerEvent, |
||||
file_row::FileRow, |
||||
media_item::MediaItem, |
||||
timeline::{Timeline, TimelineFilter}, |
||||
}; |
||||
pub use self::{file::FileHistoryViewer, media::MediaHistoryViewer}; |
||||
|
||||
Loading…
Reference in new issue