history-viewer: Implement FileHistoryViewer

Also add it as a RoomDetails' subpage.
This commit is contained in:
Marco Melorio 2022-08-11 19:16:41 +02:00 committed by Kévin Commaille
parent 063b1b318c
commit 89ed564822
10 changed files with 381 additions and 2 deletions

View file

@ -81,6 +81,8 @@
<file compressed="true" preprocess="xml-stripblanks" alias="content-explore-server-row.ui">ui/content-explore-server-row.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="content-explore-servers-popover.ui">ui/content-explore-servers-popover.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="content-explore.ui">ui/content-explore.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="content-file-history-viewer-row.ui">ui/content-file-history-viewer-row.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="content-file-history-viewer.ui">ui/content-file-history-viewer.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="content-invite-subpage.ui">ui/content-invite-subpage.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="content-invite.ui">ui/content-invite.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="content-invitee-item.ui">ui/content-invitee-item.ui</file>

View file

@ -659,6 +659,13 @@ mediahistoryvieweritem > overlay > image {
}
/* File History Viewer */
filehistoryviewer listview > row {
border-radius: 0;
}
/* Room Details */
.room-details listview {

View file

@ -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>

View file

@ -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>

View file

@ -156,6 +156,21 @@
</child>
</object>
</child>
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">File</property>
<property name="action-name">details.next-page</property>
<property name="action-target">'file-history'</property>
<property name="activatable">True</property>
<child type="suffix">
<object class="GtkImage">
<property name="valign">center</property>
<property name="halign">center</property>
<property name="icon-name">go-next-symbolic</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>

View file

@ -20,6 +20,7 @@ data/resources/ui/components-auth-dialog.ui
data/resources/ui/components-loading-listbox-row.ui
data/resources/ui/content-explore-servers-popover.ui
data/resources/ui/content-explore.ui
data/resources/ui/content-file-history-viewer.ui
data/resources/ui/content-invite-subpage.ui
data/resources/ui/content-invite.ui
data/resources/ui/content-media-history-viewer.ui
@ -73,6 +74,7 @@ src/session/account_settings/user_page/mod.rs
src/session/content/explore/public_room_row.rs
src/session/content/invite.rs
src/session/content/room_details/general_page/mod.rs
src/session/content/room_details/history_viewer/file_row.rs
src/session/content/room_details/invite_subpage/invitee_list.rs
src/session/content/room_details/member_page/mod.rs
src/session/content/room_details/mod.rs

View file

@ -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()
}
}

View file

@ -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()
}
}

View file

@ -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};

View file

@ -14,7 +14,10 @@ pub use self::{general_page::GeneralPage, invite_subpage::InviteSubpage, member_
use crate::{
components::ToastableWindow,
prelude::*,
session::{content::room_details::history_viewer::MediaHistoryViewer, Room},
session::{
content::room_details::history_viewer::{FileHistoryViewer, MediaHistoryViewer},
Room,
},
};
#[derive(Debug, Default, Hash, Eq, PartialEq, Clone, Copy, glib::Enum)]
@ -27,6 +30,7 @@ pub enum PageName {
Members,
Invite,
MediaHistory,
FileHistory,
}
impl glib::variant::StaticVariantType for PageName {
@ -42,6 +46,7 @@ impl glib::variant::FromVariant for PageName {
"members" => Some(PageName::Members),
"invite" => Some(PageName::Invite),
"media-history" => Some(PageName::MediaHistory),
"file-history" => Some(PageName::FileHistory),
"" => Some(PageName::None),
_ => None,
}
@ -56,6 +61,7 @@ impl glib::variant::ToVariant for PageName {
PageName::Members => "members",
PageName::Invite => "invite",
PageName::MediaHistory => "media-history",
PageName::FileHistory => "file-history",
}
.to_variant()
}
@ -257,6 +263,22 @@ impl RoomDetails {
self.set_title(Some(&gettext("Media")));
imp.main_stack.set_visible_child(&media_page);
}
PageName::FileHistory => {
let file_page = if let Some(file_page) = list_stack_children
.get(&PageName::FileHistory)
.and_then(glib::object::WeakRef::upgrade)
{
file_page
} else {
let file_page = FileHistoryViewer::new(self.room()).upcast::<gtk::Widget>();
list_stack_children.insert(PageName::FileHistory, file_page.downgrade());
imp.main_stack.add_child(&file_page);
file_page
};
self.set_title(Some(&gettext("File")));
imp.main_stack.set_visible_child(&file_page);
}
PageName::None => {
warn!("Can't switch to PageName::None");
}