[fractal/fractal-next] content: Move ItemRow's Event actions to its own trait
- From: Julian Sparber <jsparber src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [fractal/fractal-next] content: Move ItemRow's Event actions to its own trait
- Date: Tue, 30 Nov 2021 14:00:12 +0000 (UTC)
commit a92c21770a07066c837ef8886fc03cb26123ffa1
Author: Kévin Commaille <zecakeh tedomum fr>
Date: Tue Nov 30 14:36:56 2021 +0100
content: Move ItemRow's Event actions to its own trait
This will allow to use the same actions on other widgets.
data/resources/resources.gresource.xml | 2 +-
data/resources/ui/content-message-file.ui | 4 +-
.../ui/{content-item-row-menu.ui => event-menu.ui} | 23 ++-
po/POTFILES.in | 3 +-
src/meson.build | 1 +
src/session/content/room_history/item_row.rs | 185 +--------------------
.../content/room_history/message_row/text.rs | 14 +-
src/session/room/event.rs | 38 ++++-
src/session/room/event_actions.rs | 182 ++++++++++++++++++++
src/session/room/mod.rs | 2 +
10 files changed, 251 insertions(+), 203 deletions(-)
---
diff --git a/data/resources/resources.gresource.xml b/data/resources/resources.gresource.xml
index 0aa5e481..607d1544 100644
--- a/data/resources/resources.gresource.xml
+++ b/data/resources/resources.gresource.xml
@@ -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>
diff --git a/data/resources/ui/content-message-file.ui b/data/resources/ui/content-message-file.ui
index 5dc22bfe..fc7dff38 100644
--- a/data/resources/ui/content-message-file.ui
+++ b/data/resources/ui/content-message-file.ui
@@ -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>
diff --git a/data/resources/ui/content-item-row-menu.ui b/data/resources/ui/event-menu.ui
similarity index 71%
rename from data/resources/ui/content-item-row-menu.ui
rename to data/resources/ui/event-menu.ui
index 54e3c418..30c68073 100644
--- a/data/resources/ui/content-item-row-menu.ui
+++ b/data/resources/ui/event-menu.ui
@@ -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>
-
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 3f25f606..c47338ca 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -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
diff --git a/src/meson.build b/src/meson.build
index d3ccd1cd..ccc4245e 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -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',
diff --git a/src/session/content/room_history/item_row.rs b/src/session/content/room_history/item_row.rs
index 8b013544..5f48008f 100644
--- a/src/session/content/room_history/item_row.rs
+++ b/src/session/content/room_history/item_row.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 {}
diff --git a/src/session/content/room_history/message_row/text.rs
b/src/session/content/room_history/message_row/text.rs
index 3117ead3..c28ce502 100644
--- a/src/session/content/room_history/message_row/text.rs
+++ b/src/session/content/room_history/message_row/text.rs
@@ -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 {
diff --git a/src/session/room/event.rs b/src/session/room/event.rs
index abe6c0f9..57440472 100644
--- a/src/session/room/event.rs
+++ b/src/session/room/event.rs
@@ -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");
+ }
}
diff --git a/src/session/room/event_actions.rs b/src/session/room/event_actions.rs
new file mode 100644
index 00000000..ba93fe0e
--- /dev/null
+++ b/src/session/room/event_actions.rs
@@ -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);
+ }
+ })
+ );
+ }
+}
diff --git a/src/session/room/mod.rs b/src/session/room/mod.rs
index 15c6929f..d16edfc9 100644
--- a/src/session/room/mod.rs
+++ b/src/session/room/mod.rs
@@ -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;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]