content: Move ItemRow's Event actions to its own trait
This will allow to use the same actions on other widgets.
This commit is contained in:
parent
e2da077328
commit
a92c21770a
10 changed files with 251 additions and 203 deletions
|
@ -10,7 +10,6 @@
|
|||
<file compressed="true" preprocess="xml-stripblanks" alias="content-explore-item.ui">ui/content-explore-item.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks" alias="content-public-room-row.ui">ui/content-public-room-row.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks" alias="content-item.ui">ui/content-item.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks" alias="content-item-row-menu.ui">ui/content-item-row-menu.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks" alias="content-message-file.ui">ui/content-message-file.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks" alias="content-member-page.ui">ui/content-member-page.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks" alias="content-member-row.ui">ui/content-member-row.ui</file>
|
||||
|
@ -20,6 +19,7 @@
|
|||
<file compressed="true" preprocess="xml-stripblanks" alias="content-state-row.ui">ui/content-state-row.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks" alias="content-markdown-popover.ui">ui/content-markdown-popover.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks" alias="content-invite.ui">ui/content-invite.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks" alias="event-menu.ui">ui/event-menu.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks" alias="event-source-dialog.ui">ui/event-source-dialog.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks" alias="login.ui">ui/login.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks" alias="session.ui">ui/session.ui</file>
|
||||
|
|
|
@ -20,14 +20,14 @@
|
|||
<object class="GtkButton" id="open">
|
||||
<property name="icon-name">document-open-symbolic</property>
|
||||
<property name="tooltip-text" translatable="yes">Open</property>
|
||||
<property name="action-name">item-row.file-open</property>
|
||||
<property name="action-name">event.file-open</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="save">
|
||||
<property name="icon-name">document-save-symbolic</property>
|
||||
<property name="tooltip-text" translatable="yes">Save</property>
|
||||
<property name="action-name">item-row.file-save</property>
|
||||
<property name="action-name">event.file-save</property>
|
||||
</object>
|
||||
</child>
|
||||
<style>
|
||||
|
|
|
@ -4,64 +4,61 @@
|
|||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">_Reply</attribute>
|
||||
<attribute name="action">item-row.reply</attribute>
|
||||
<attribute name="action">event.reply</attribute>
|
||||
<attribute name="hidden-when">action-missing</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">_Edit</attribute>
|
||||
<attribute name="action">item-row.edit</attribute>
|
||||
<attribute name="action">event.edit</attribute>
|
||||
<attribute name="hidden-when">action-missing</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">_Forward</attribute>
|
||||
<attribute name="action">item-row.forward</attribute>
|
||||
<attribute name="action">event.forward</attribute>
|
||||
<attribute name="hidden-when">action-missing</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">_Select</attribute>
|
||||
<attribute name="action">item-row.select</attribute>
|
||||
<attribute name="action">event.select</attribute>
|
||||
<attribute name="hidden-when">action-missing</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">_Copy Text</attribute>
|
||||
<attribute name="action">item-row.copy-text</attribute>
|
||||
<attribute name="action">event.copy-text</attribute>
|
||||
<attribute name="hidden-when">action-disabled</attribute>
|
||||
<attribute name="hidden-when">action-missing</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">_Copy Image</attribute>
|
||||
<attribute name="action">item-row.copy-image</attribute>
|
||||
<attribute name="hidden-when">action-disabled</attribute>
|
||||
<attribute name="action">event.copy-image</attribute>
|
||||
<attribute name="hidden-when">action-missing</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">S_ave Image</attribute>
|
||||
<attribute name="action">item-row.save-image</attribute>
|
||||
<attribute name="hidden-when">action-disabled</attribute>
|
||||
<attribute name="action">event.save-image</attribute>
|
||||
<attribute name="hidden-when">action-missing</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">_Permalink</attribute>
|
||||
<attribute name="action">item-row.permalink</attribute>
|
||||
<attribute name="action">event.permalink</attribute>
|
||||
<attribute name="hidden-when">action-missing</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">_View Source</attribute>
|
||||
<attribute name="action">item-row.view-source</attribute>
|
||||
<attribute name="action">event.view-source</attribute>
|
||||
<attribute name="hidden-when">action-missing</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Re_move</attribute>
|
||||
<attribute name="action">item-row.remove</attribute>
|
||||
<attribute name="action">event.remove</attribute>
|
||||
<attribute name="hidden-when">action-missing</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</menu>
|
||||
</interface>
|
||||
|
|
@ -14,7 +14,6 @@ data/resources/ui/components-avatar.ui
|
|||
data/resources/ui/components-loading-listbox-row.ui
|
||||
data/resources/ui/avatar-with-selection.ui
|
||||
data/resources/ui/content-divider-row.ui
|
||||
data/resources/ui/content-item-row-menu.ui
|
||||
data/resources/ui/content-item.ui
|
||||
data/resources/ui/content-invite.ui
|
||||
data/resources/ui/content-markdown-popover.ui
|
||||
|
@ -25,6 +24,7 @@ data/resources/ui/content-room-history.ui
|
|||
data/resources/ui/content-state-row.ui
|
||||
data/resources/ui/content.ui
|
||||
data/resources/ui/context-menu-bin.ui
|
||||
data/resources/ui/event-menu.ui
|
||||
data/resources/ui/event-source-dialog.ui
|
||||
data/resources/ui/login.ui
|
||||
data/resources/ui/in-app-notification.ui
|
||||
|
@ -89,6 +89,7 @@ src/session/content/room_history/state_row.rs
|
|||
src/session/mod.rs
|
||||
src/session/room_creation/mod.rs
|
||||
src/session/room_list.rs
|
||||
src/session/room/event_actions.rs
|
||||
src/session/room/event.rs
|
||||
src/session/room/highlight_flags.rs
|
||||
src/session/room/item.rs
|
||||
|
|
|
@ -71,6 +71,7 @@ sources = files(
|
|||
'session/content/mod.rs',
|
||||
'session/content/room_details/member_page.rs',
|
||||
'session/content/room_details/mod.rs',
|
||||
'session/room/event_actions.rs',
|
||||
'session/room/event.rs',
|
||||
'session/room/highlight_flags.rs',
|
||||
'session/room/item.rs',
|
||||
|
|
|
@ -1,18 +1,11 @@
|
|||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use gettextrs::gettext;
|
||||
use gtk::{gio, glib, glib::clone, subclass::prelude::*, FileChooserAction, ResponseType};
|
||||
use log::error;
|
||||
use matrix_sdk::ruma::events::{
|
||||
room::message::MessageType, AnyMessageEventContent, AnySyncRoomEvent,
|
||||
};
|
||||
use gtk::{gio, glib, glib::clone, subclass::prelude::*};
|
||||
use matrix_sdk::ruma::events::AnySyncRoomEvent;
|
||||
|
||||
use crate::components::{ContextMenuBin, ContextMenuBinExt, ContextMenuBinImpl};
|
||||
use crate::matrix_error::UserFacingError;
|
||||
use crate::session::content::room_history::{message_row::MessageRow, DividerRow, StateRow};
|
||||
use crate::session::event_source_dialog::EventSourceDialog;
|
||||
use crate::session::room::{Event, Item, ItemType};
|
||||
use crate::utils::cache_dir;
|
||||
use crate::{spawn, spawn_tokio, Error, Window};
|
||||
use crate::session::room::{Event, EventActions, Item, ItemType};
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
|
@ -31,36 +24,6 @@ mod imp {
|
|||
const NAME: &'static str = "ContentItemRow";
|
||||
type Type = super::ItemRow;
|
||||
type ParentType = ContextMenuBin;
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
// View Event Source
|
||||
klass.install_action("item-row.view-source", None, move |widget, _, _| {
|
||||
let window = widget.root().unwrap().downcast().unwrap();
|
||||
let dialog =
|
||||
EventSourceDialog::new(&window, widget.item().unwrap().event().unwrap());
|
||||
dialog.show();
|
||||
});
|
||||
|
||||
// Save message's file
|
||||
klass.install_action("item-row.file-save", None, move |widget, _, _| {
|
||||
spawn!(
|
||||
glib::PRIORITY_LOW,
|
||||
clone!(@weak widget as obj => async move {
|
||||
obj.save_file().await;
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// Open message's file
|
||||
klass.install_action("item-row.file-open", None, move |widget, _, _| {
|
||||
spawn!(
|
||||
glib::PRIORITY_LOW,
|
||||
clone!(@weak widget as obj => async move {
|
||||
obj.open_file().await;
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for ItemRow {
|
||||
|
@ -136,14 +99,6 @@ impl ItemRow {
|
|||
priv_.item.borrow().clone()
|
||||
}
|
||||
|
||||
fn enable_gactions(&self) {
|
||||
self.action_set_enabled("item-row.view-source", true);
|
||||
}
|
||||
|
||||
fn disable_gactions(&self) {
|
||||
self.action_set_enabled("item-row.view-source", false);
|
||||
}
|
||||
|
||||
/// This method sets this row to a new `Item`.
|
||||
///
|
||||
/// It tries to reuse the widget and only update the content whenever possible, but it will
|
||||
|
@ -162,14 +117,9 @@ impl ItemRow {
|
|||
match item.type_() {
|
||||
ItemType::Event(event) => {
|
||||
if self.context_menu().is_none() {
|
||||
let menu_model = gtk::Builder::from_resource(
|
||||
"/org/gnome/FractalNext/content-item-row-menu.ui",
|
||||
)
|
||||
.object("menu_model");
|
||||
self.set_context_menu(menu_model);
|
||||
|
||||
self.enable_gactions();
|
||||
self.set_context_menu(Some(Self::event_menu_model()));
|
||||
}
|
||||
self.set_event_actions(Some(event));
|
||||
|
||||
let event_notify_handler = event.connect_notify_local(
|
||||
Some("event"),
|
||||
|
@ -188,7 +138,7 @@ impl ItemRow {
|
|||
ItemType::DayDivider(date) => {
|
||||
if self.context_menu().is_some() {
|
||||
self.set_context_menu(None);
|
||||
self.disable_gactions();
|
||||
self.set_event_actions(None);
|
||||
}
|
||||
|
||||
let fmt = if date.year() == glib::DateTime::new_now_local().unwrap().year() {
|
||||
|
@ -210,7 +160,7 @@ impl ItemRow {
|
|||
ItemType::NewMessageDivider => {
|
||||
if self.context_menu().is_some() {
|
||||
self.set_context_menu(None);
|
||||
self.disable_gactions();
|
||||
self.set_event_actions(None);
|
||||
}
|
||||
|
||||
let label = gettext("New Messages");
|
||||
|
@ -266,125 +216,6 @@ impl ItemRow {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn save_file(&self) {
|
||||
let (filename, data) = match self.get_media_content().await {
|
||||
Ok(res) => res,
|
||||
Err(err) => {
|
||||
error!("Could not get file: {}", err);
|
||||
|
||||
let error_message = err.to_user_facing();
|
||||
let error = Error::new(move |_| {
|
||||
let error_label = gtk::LabelBuilder::new()
|
||||
.label(&error_message)
|
||||
.wrap(true)
|
||||
.build();
|
||||
Some(error_label.upcast())
|
||||
});
|
||||
if let Some(window) = self.root().and_then(|root| root.downcast::<Window>().ok()) {
|
||||
window.append_error(&error);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let window: gtk::Window = self.root().unwrap().downcast().unwrap();
|
||||
let dialog = gtk::FileChooserDialog::new(
|
||||
Some(&gettext("Save File")),
|
||||
Some(&window),
|
||||
FileChooserAction::Save,
|
||||
&[
|
||||
(&gettext("Save"), ResponseType::Accept),
|
||||
(&gettext("Cancel"), ResponseType::Cancel),
|
||||
],
|
||||
);
|
||||
dialog.set_current_name(&filename);
|
||||
|
||||
let response = dialog.run_future().await;
|
||||
if response == ResponseType::Accept {
|
||||
if let Some(file) = dialog.file() {
|
||||
file.replace_contents(
|
||||
&data,
|
||||
None,
|
||||
false,
|
||||
gio::FileCreateFlags::REPLACE_DESTINATION,
|
||||
gio::NONE_CANCELLABLE,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
dialog.close();
|
||||
}
|
||||
|
||||
pub async fn open_file(&self) {
|
||||
let (filename, data) = match self.get_media_content().await {
|
||||
Ok(res) => res,
|
||||
Err(err) => {
|
||||
error!("Could not get file: {}", err);
|
||||
|
||||
let error_message = err.to_user_facing();
|
||||
let error = Error::new(move |_| {
|
||||
let error_label = gtk::LabelBuilder::new()
|
||||
.label(&error_message)
|
||||
.wrap(true)
|
||||
.build();
|
||||
Some(error_label.upcast())
|
||||
});
|
||||
if let Some(window) = self.root().and_then(|root| root.downcast::<Window>().ok()) {
|
||||
window.append_error(&error);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut path = cache_dir();
|
||||
path.push(filename);
|
||||
let file = gio::File::for_path(path);
|
||||
|
||||
file.replace_contents(
|
||||
&data,
|
||||
None,
|
||||
false,
|
||||
gio::FileCreateFlags::REPLACE_DESTINATION,
|
||||
gio::NONE_CANCELLABLE,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
if let Err(error) = gio::AppInfo::launch_default_for_uri_async_future(
|
||||
&file.uri(),
|
||||
gio::NONE_APP_LAUNCH_CONTEXT,
|
||||
)
|
||||
.await
|
||||
{
|
||||
error!("Error opening file '{}': {}", file.uri(), error);
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_media_content(&self) -> Result<(String, Vec<u8>), matrix_sdk::Error> {
|
||||
let item = self.item().unwrap();
|
||||
let event = item.event().unwrap();
|
||||
|
||||
if let AnySyncRoomEvent::Message(message_event) = event.matrix_event().unwrap() {
|
||||
if let AnyMessageEventContent::RoomMessage(content) = message_event.content() {
|
||||
let client = event.room().session().client();
|
||||
match content.msgtype {
|
||||
MessageType::File(file_content) => {
|
||||
let content = file_content.clone();
|
||||
let handle =
|
||||
spawn_tokio!(async move { client.get_file(content, true).await });
|
||||
let data = handle.await.unwrap()?.unwrap();
|
||||
return Ok((file_content.filename.unwrap_or(file_content.body), data));
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
panic!("Trying to get the media content of an event of incompatible type");
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ItemRow {
|
||||
|
@ -392,3 +223,5 @@ impl Default for ItemRow {
|
|||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl EventActions for ItemRow {}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use adw::{prelude::BinExt, subclass::prelude::*};
|
||||
use gtk::{gio, glib, pango, prelude::*, subclass::prelude::*};
|
||||
use gtk::{glib, pango, prelude::*, subclass::prelude::*};
|
||||
use html2pango::{
|
||||
block::{markup_html, HtmlBlock},
|
||||
html_escape, markup_links,
|
||||
|
@ -7,7 +7,11 @@ use html2pango::{
|
|||
use matrix_sdk::ruma::events::room::message::{FormattedBody, MessageFormat};
|
||||
use sourceview::prelude::*;
|
||||
|
||||
use crate::session::{room::Member, UserExt};
|
||||
use crate::session::{
|
||||
content::room_history::ItemRow,
|
||||
room::{EventActions, Member},
|
||||
UserExt,
|
||||
};
|
||||
|
||||
#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy, glib::GEnum)]
|
||||
#[repr(u32)]
|
||||
|
@ -320,10 +324,8 @@ fn set_label_styles(w: >k::Label) {
|
|||
w.set_valign(gtk::Align::Start);
|
||||
w.set_halign(gtk::Align::Fill);
|
||||
w.set_selectable(true);
|
||||
let menu_model: Option<gio::MenuModel> =
|
||||
gtk::Builder::from_resource("/org/gnome/FractalNext/content-item-row-menu.ui")
|
||||
.object("menu_model");
|
||||
w.set_extra_menu(menu_model.as_ref());
|
||||
let menu_model = ItemRow::event_menu_model();
|
||||
w.set_extra_menu(Some(&menu_model));
|
||||
}
|
||||
|
||||
fn create_widget_for_html_block(block: &HtmlBlock) -> gtk::Widget {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use gtk::{glib, glib::DateTime, prelude::*, subclass::prelude::*};
|
||||
use log::warn;
|
||||
use matrix_sdk::{
|
||||
deserialized_responses::SyncRoomEvent,
|
||||
ruma::{
|
||||
|
@ -12,9 +13,10 @@ use matrix_sdk::{
|
|||
},
|
||||
};
|
||||
|
||||
use crate::session::room::Member;
|
||||
use crate::session::Room;
|
||||
use log::warn;
|
||||
use crate::{
|
||||
session::{room::Member, Room},
|
||||
spawn_tokio,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, glib::GBoxed)]
|
||||
#[gboxed(type_name = "BoxedSyncRoomEvent")]
|
||||
|
@ -438,7 +440,10 @@ impl Event {
|
|||
priv_.show_header.get()
|
||||
}
|
||||
|
||||
fn message_content(&self) -> Option<AnyMessageEventContent> {
|
||||
/// The content of this message.
|
||||
///
|
||||
/// Returns `None` if this is not a message.
|
||||
pub fn message_content(&self) -> Option<AnyMessageEventContent> {
|
||||
match self.matrix_event() {
|
||||
Some(AnySyncRoomEvent::Message(message)) => Some(message.content()),
|
||||
_ => None,
|
||||
|
@ -495,4 +500,29 @@ impl Event {
|
|||
) -> glib::SignalHandlerId {
|
||||
self.connect_notify_local(Some("show-header"), f)
|
||||
}
|
||||
|
||||
/// The content of a media message.
|
||||
///
|
||||
/// Compatible events:
|
||||
///
|
||||
/// - File message (`MessageType::File`).
|
||||
///
|
||||
/// Returns `Ok((filename, binary_content))` on success, `Err` if an error occured while
|
||||
/// fetching the content. Panics on an incompatible event.
|
||||
pub async fn get_media_content(&self) -> Result<(String, Vec<u8>), matrix_sdk::Error> {
|
||||
if let AnyMessageEventContent::RoomMessage(content) = self.message_content().unwrap() {
|
||||
let client = self.room().session().client();
|
||||
match content.msgtype {
|
||||
MessageType::File(file_content) => {
|
||||
let content = file_content.clone();
|
||||
let handle = spawn_tokio!(async move { client.get_file(content, true).await });
|
||||
let data = handle.await.unwrap()?.unwrap();
|
||||
return Ok((file_content.filename.unwrap_or(file_content.body), data));
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
};
|
||||
|
||||
panic!("Trying to get the media content of an event of incompatible type");
|
||||
}
|
||||
}
|
||||
|
|
182
src/session/room/event_actions.rs
Normal file
182
src/session/room/event_actions.rs
Normal file
|
@ -0,0 +1,182 @@
|
|||
use gettextrs::gettext;
|
||||
use gtk::{gio, glib, glib::clone, prelude::*};
|
||||
use log::error;
|
||||
use matrix_sdk::ruma::events::{room::message::MessageType, AnyMessageEventContent};
|
||||
|
||||
use crate::{
|
||||
matrix_error::UserFacingError,
|
||||
session::{event_source_dialog::EventSourceDialog, room::Event},
|
||||
spawn,
|
||||
utils::cache_dir,
|
||||
Error, Window,
|
||||
};
|
||||
|
||||
pub trait EventActions
|
||||
where
|
||||
Self: IsA<gtk::Widget>,
|
||||
Self: glib::clone::Downgrade,
|
||||
<Self as glib::clone::Downgrade>::Weak: glib::clone::Upgrade<Strong = Self>,
|
||||
{
|
||||
/// The `MenuModel` for common event actions.
|
||||
fn event_menu_model() -> gio::MenuModel {
|
||||
gtk::Builder::from_resource("/org/gnome/FractalNext/event-menu.ui")
|
||||
.object("menu_model")
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Set the actions available on `self` for `event`.
|
||||
///
|
||||
/// Unsets the actions if `event` is `None`.
|
||||
///
|
||||
/// Should be used with the compatible model from `event_menu_model`.
|
||||
fn set_event_actions(&self, event: Option<&Event>) {
|
||||
if event.is_none() {
|
||||
self.insert_action_group("event", gio::NONE_ACTION_GROUP);
|
||||
}
|
||||
|
||||
let event = event.unwrap();
|
||||
let action_group = gio::SimpleActionGroup::new();
|
||||
|
||||
// View Event Source
|
||||
let view_source = gio::SimpleAction::new("view-source", None);
|
||||
view_source.connect_activate(clone!(@weak self as widget, @weak event => move |_, _| {
|
||||
let window = widget.root().unwrap().downcast().unwrap();
|
||||
let dialog = EventSourceDialog::new(&window, &event);
|
||||
dialog.show();
|
||||
}));
|
||||
action_group.add_action(&view_source);
|
||||
|
||||
if let Some(AnyMessageEventContent::RoomMessage(message)) = event.message_content() {
|
||||
if let MessageType::File(_) = message.msgtype {
|
||||
// Save message's file
|
||||
let file_save = gio::SimpleAction::new("file-save", None);
|
||||
file_save.connect_activate(
|
||||
clone!(@weak self as widget, @weak event => move |_, _| {
|
||||
widget.save_event_file(event);
|
||||
}),
|
||||
);
|
||||
action_group.add_action(&file_save);
|
||||
|
||||
// Open message's file
|
||||
let file_open = gio::SimpleAction::new("file-open", None);
|
||||
file_open.connect_activate(
|
||||
clone!(@weak self as widget, @weak event => move |_, _| {
|
||||
widget.open_event_file(event);
|
||||
}),
|
||||
);
|
||||
action_group.add_action(&file_open);
|
||||
}
|
||||
}
|
||||
|
||||
self.insert_action_group("event", Some(&action_group));
|
||||
}
|
||||
|
||||
/// Save the file in `event`.
|
||||
///
|
||||
/// See `Event::get_media_content` for compatible events. Panics on an incompatible event.
|
||||
fn save_event_file(&self, event: Event) {
|
||||
let window: Window = self.root().unwrap().downcast().unwrap();
|
||||
spawn!(
|
||||
glib::PRIORITY_LOW,
|
||||
clone!(@weak window => async move {
|
||||
let (filename, data) = match event.get_media_content().await {
|
||||
Ok(res) => res,
|
||||
Err(err) => {
|
||||
error!("Could not get file: {}", err);
|
||||
|
||||
let error_message = err.to_user_facing();
|
||||
let error = Error::new(move |_| {
|
||||
let error_label = gtk::LabelBuilder::new()
|
||||
.label(&error_message)
|
||||
.wrap(true)
|
||||
.build();
|
||||
Some(error_label.upcast())
|
||||
});
|
||||
window.append_error(&error);
|
||||
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let dialog = gtk::FileChooserDialog::new(
|
||||
Some(&gettext("Save File")),
|
||||
Some(&window),
|
||||
gtk::FileChooserAction::Save,
|
||||
&[
|
||||
(&gettext("Save"), gtk::ResponseType::Accept),
|
||||
(&gettext("Cancel"), gtk::ResponseType::Cancel),
|
||||
],
|
||||
);
|
||||
dialog.set_current_name(&filename);
|
||||
|
||||
let response = dialog.run_future().await;
|
||||
if response == gtk::ResponseType::Accept {
|
||||
if let Some(file) = dialog.file() {
|
||||
file.replace_contents(
|
||||
&data,
|
||||
None,
|
||||
false,
|
||||
gio::FileCreateFlags::REPLACE_DESTINATION,
|
||||
gio::NONE_CANCELLABLE,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
dialog.close();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/// Open the file in `event`.
|
||||
///
|
||||
/// See `Event::get_media_content` for compatible events. Panics on an incompatible event.
|
||||
fn open_event_file(&self, event: Event) {
|
||||
let window: Window = self.root().unwrap().downcast().unwrap();
|
||||
spawn!(
|
||||
glib::PRIORITY_LOW,
|
||||
clone!(@weak window => async move {
|
||||
let (filename, data) = match event.get_media_content().await {
|
||||
Ok(res) => res,
|
||||
Err(err) => {
|
||||
error!("Could not get file: {}", err);
|
||||
|
||||
let error_message = err.to_user_facing();
|
||||
let error = Error::new(move |_| {
|
||||
let error_label = gtk::LabelBuilder::new()
|
||||
.label(&error_message)
|
||||
.wrap(true)
|
||||
.build();
|
||||
Some(error_label.upcast())
|
||||
});
|
||||
window.append_error(&error);
|
||||
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut path = cache_dir();
|
||||
path.push(filename);
|
||||
let file = gio::File::for_path(path);
|
||||
|
||||
file.replace_contents(
|
||||
&data,
|
||||
None,
|
||||
false,
|
||||
gio::FileCreateFlags::REPLACE_DESTINATION,
|
||||
gio::NONE_CANCELLABLE,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
if let Err(error) = gio::AppInfo::launch_default_for_uri_async_future(
|
||||
&file.uri(),
|
||||
gio::NONE_APP_LAUNCH_CONTEXT,
|
||||
)
|
||||
.await
|
||||
{
|
||||
error!("Error opening file '{}': {}", file.uri(), error);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
mod event;
|
||||
mod event_actions;
|
||||
mod highlight_flags;
|
||||
mod item;
|
||||
mod member;
|
||||
|
@ -9,6 +10,7 @@ mod room_type;
|
|||
mod timeline;
|
||||
|
||||
pub use self::event::Event;
|
||||
pub use self::event_actions::EventActions;
|
||||
pub use self::highlight_flags::HighlightFlags;
|
||||
pub use self::item::Item;
|
||||
pub use self::item::ItemType;
|
||||
|
|
Loading…
Reference in a new issue