fractal/session/view/content/room_details/history_viewer/
file_row.rsuse adw::{prelude::*, subclass::prelude::*};
use gettextrs::gettext;
use gtk::{gio, glib, CompositeTemplate};
use tracing::error;
use super::HistoryViewerEvent;
use crate::{gettext_f, prelude::*, toast, utils::matrix::MediaMessage};
mod imp {
use std::cell::RefCell;
use glib::subclass::InitializingObject;
use super::*;
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
#[template(
resource = "/org/gnome/Fractal/ui/session/view/content/room_details/history_viewer/file_row.ui"
)]
#[properties(wrapper_type = super::FileRow)]
pub struct FileRow {
#[property(get, set = Self::set_event, explicit_notify, nullable)]
pub event: RefCell<Option<HistoryViewerEvent>>,
pub file: RefCell<Option<gio::File>>,
#[template_child]
pub button: TemplateChild<gtk::Button>,
#[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);
Self::Type::bind_template_callbacks(klass);
}
fn instance_init(obj: &InitializingObject<Self>) {
obj.init_template();
}
}
#[glib::derived_properties]
impl ObjectImpl for FileRow {}
impl WidgetImpl for FileRow {}
impl BinImpl for FileRow {}
impl FileRow {
fn set_event(&self, event: Option<HistoryViewerEvent>) {
if *self.event.borrow() == event {
return;
}
if let Some(event) = &event {
let media_message = event.media_message();
if let MediaMessage::File(file) = &media_message {
let filename = media_message.filename();
self.title_label.set_label(&filename);
self.button
.update_property(&[gtk::accessible::Property::Label(&gettext_f(
"Save {filename}",
&[("filename", &filename)],
))]);
if let Some(size) = file.info.as_ref().and_then(|i| i.size) {
let size = glib::format_size(size.into());
self.size_label.set_label(&size);
} else {
self.size_label.set_label(&gettext("Unknown size"));
}
}
}
self.event.replace(event);
self.file.take();
self.update_button();
self.obj().notify_event();
}
pub(super) fn update_button(&self) {
if self.file.borrow().is_some() {
self.button.set_icon_name("document-symbolic");
self.button.set_tooltip_text(Some(&gettext("Open File")));
} else {
self.button.set_icon_name("save-symbolic");
self.button.set_tooltip_text(Some(&gettext("Save File")));
}
}
}
}
glib::wrapper! {
pub struct FileRow(ObjectSubclass<imp::FileRow>)
@extends gtk::Widget, adw::Bin;
}
#[gtk::template_callbacks]
impl FileRow {
pub fn new() -> Self {
glib::Object::new()
}
#[template_callback]
async fn button_clicked(&self) {
let file = self.imp().file.borrow().clone();
if let Some(file) = file {
if let Err(error) =
gio::AppInfo::launch_default_for_uri(&file.uri(), gio::AppLaunchContext::NONE)
{
error!("Could not open file: {error}");
}
} else {
self.save_file().await;
}
}
async fn save_file(&self) {
let Some(event) = self.event() else {
return;
};
let data = match event.get_file_content().await {
Ok(res) => res,
Err(error) => {
error!("Could not get file: {error}");
toast!(self, error.to_user_facing());
return;
}
};
let filename = event.media_message().filename();
let parent_window = self.root().and_downcast::<gtk::Window>().unwrap();
let dialog = gtk::FileDialog::builder()
.title(gettext("Save File"))
.accept_label(gettext("Save"))
.initial_name(filename)
.build();
if let Ok(file) = dialog.save_future(Some(&parent_window)).await {
file.replace_contents(
&data,
None,
false,
gio::FileCreateFlags::REPLACE_DESTINATION,
gio::Cancellable::NONE,
)
.unwrap();
let imp = self.imp();
imp.file.replace(Some(file));
imp.update_button();
}
}
}