Browse Source

room-details: Respect media previews safety setting for media history viewer

Do not show the previews if they are not shown in the room history.

This also adds support for blurhashes.
merge-requests/2003/head
Kévin Commaille 11 months ago
parent
commit
10776c0f52
No known key found for this signature in database
GPG Key ID: C971D9DBC9D678D
  1. 4
      data/resources/stylesheet/_room_details.scss
  2. 142
      src/session/view/content/room_details/history_viewer/visual_media_item.rs
  3. 16
      src/session/view/content/room_details/history_viewer/visual_media_item.ui

4
data/resources/stylesheet/_room_details.scss

@ -19,7 +19,7 @@ visual-media-history-viewer {
}
visual-media-history-viewer-item {
background: none;
background-color: var(--border-color);
transition: vendor.$ease-out-quad;
&:hover, &:focus {
@ -98,7 +98,7 @@ dragoverlay statuspage {
permissions-member-row {
padding: 8px;
border-radius: vendor.$card_radius;
@include vendor.focus-ring();
&:hover, &.has-open-popup {

142
src/session/view/content/room_details/history_viewer/visual_media_item.rs

@ -3,22 +3,40 @@ use ruma::api::client::media::get_content_thumbnail::v3::Method;
use super::{HistoryViewerEvent, VisualMediaHistoryViewer};
use crate::{
session::model::Session,
spawn,
utils::{
key_bindings,
matrix::VisualMediaMessage,
media::{
image::{ImageRequestPriority, ThumbnailSettings},
image::{Blurhash, ImageRequestPriority, ThumbnailSettings},
FrameDimensions,
},
},
};
/// The default size requested by a thumbnail.
const THUMBNAIL_SIZE: u32 = 300;
/// The default size for the preview.
const PREVIEW_SIZE: u32 = 300;
/// The default dimensions of the preview.
const PREVIEW_DIMENSIONS: FrameDimensions = FrameDimensions {
width: PREVIEW_SIZE,
height: PREVIEW_SIZE,
};
/// The possible sources of the preview of a visual media.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
enum MediaPreview {
/// There is no media preview.
#[default]
None,
/// The media preview is the low-quality placeholder.
Placeholder,
/// The media preview is the thumbnail.
Thumbnail,
}
mod imp {
use std::cell::RefCell;
use std::cell::{Cell, RefCell};
use glib::subclass::InitializingObject;
@ -34,10 +52,13 @@ mod imp {
overlay: TemplateChild<gtk::Overlay>,
#[template_child]
picture: TemplateChild<gtk::Picture>,
/// The file event.
#[template_child]
play_icon: TemplateChild<gtk::Image>,
/// The event that is previewed.
#[property(get, set = Self::set_event, explicit_notify, nullable)]
event: RefCell<Option<HistoryViewerEvent>>,
overlay_icon: RefCell<Option<gtk::Image>>,
/// Which preview is presented by the picture.
preview: Cell<MediaPreview>,
}
#[glib::object_subclass]
@ -95,80 +116,84 @@ mod imp {
return;
}
// Reset the preview.
self.preview.take();
self.picture.set_paintable(None::<&gdk::Paintable>);
self.event.replace(event);
self.update();
self.update();
self.obj().notify_event();
}
/// Update this item for the current state.
fn update(&self) {
let Some(media_message) = self
.event
.borrow()
.as_ref()
.and_then(HistoryViewerEvent::visual_media_message)
else {
let Some(event) = self.event.borrow().clone() else {
return;
};
let Some(media_message) = event.visual_media_message() else {
return;
};
let show_overlay = matches!(media_message, VisualMediaMessage::Video(_));
self.show_video_overlay(show_overlay);
let is_video = matches!(media_message, VisualMediaMessage::Video(_));
self.play_icon.set_visible(is_video);
self.obj().set_tooltip_text(Some(&media_message.filename()));
spawn!(
glib::Priority::LOW,
clone!(
#[weak(rename_to = imp)]
self,
async move {
imp.load_thumbnail(media_message).await;
}
)
);
}
if let Some(blurhash) = media_message.blurhash() {
spawn!(
glib::Priority::LOW,
clone!(
#[weak(rename_to = imp)]
self,
async move {
imp.load_placeholder(blurhash).await;
}
)
);
}
/// Set whether to show the video overlay.
fn show_video_overlay(&self, show: bool) {
if show && self.overlay_icon.borrow().is_none() {
let icon = gtk::Image::builder()
.icon_name("media-playback-start-symbolic")
.css_classes(vec!["osd".to_string()])
.halign(gtk::Align::Center)
.valign(gtk::Align::Center)
.accessible_role(gtk::AccessibleRole::Presentation)
.build();
self.overlay.add_overlay(&icon);
self.overlay_icon.replace(Some(icon));
} else if !show {
if let Some(icon) = self.overlay_icon.take() {
self.overlay.remove_overlay(&icon);
}
let Some(room) = event.room() else {
return;
};
let Some(session) = room.session() else {
return;
};
if session.settings().should_room_show_media_previews(&room) {
spawn!(
glib::Priority::LOW,
clone!(
#[weak(rename_to = imp)]
self,
async move {
imp.load_thumbnail(media_message, &session).await;
}
)
);
}
}
/// Load the thumbnail for the given media message.
async fn load_thumbnail(&self, media_message: VisualMediaMessage) {
let Some(session) = self
.event
.borrow()
.as_ref()
.and_then(HistoryViewerEvent::room)
.and_then(|r| r.session())
else {
/// Load the thumbnail for the given Blurhash.
async fn load_placeholder(&self, blurhash: Blurhash) {
let Some(placeholder_texture) = blurhash.into_texture(PREVIEW_DIMENSIONS).await else {
return;
};
// Do not replace the thumbnail by the placeholder, in case the thumbnail is
// loaded before.
if self.preview.get() != MediaPreview::Thumbnail {
self.picture.set_paintable(Some(&placeholder_texture));
self.preview.set(MediaPreview::Placeholder);
}
}
/// Load the thumbnail for the given media message.
async fn load_thumbnail(&self, media_message: VisualMediaMessage, session: &Session) {
let client = session.client();
let scale_factor = u32::try_from(self.obj().scale_factor()).unwrap_or(1);
let size = THUMBNAIL_SIZE * scale_factor;
let dimensions = FrameDimensions {
width: size,
height: size,
};
let dimensions = PREVIEW_DIMENSIONS.scale(scale_factor);
let settings = ThumbnailSettings {
dimensions,
@ -183,6 +208,7 @@ mod imp {
{
self.picture
.set_paintable(Some(&gdk::Paintable::from(image)));
self.preview.set(MediaPreview::Thumbnail);
}
}
@ -204,7 +230,7 @@ mod imp {
}
glib::wrapper! {
/// A row presenting a visual media (image or video) event.
/// An item presenting a visual media (image or video) event.
pub struct VisualMediaItem(ObjectSubclass<imp::VisualMediaItem>)
@extends gtk::Widget, @implements gtk::Accessible;
}

16
src/session/view/content/room_details/history_viewer/visual_media_item.ui

@ -15,6 +15,22 @@
<property name="content-fit">cover</property>
</object>
</child>
<child type="overlay">
<object class="GtkImage" id="play_icon">
<style>
<class name="osd"/>
<class name="circular"/>
</style>
<property name="visible">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="icon-name">media-playback-start-symbolic</property>
<property name="accessible-role">presentation</property>
<layout>
<property name="measure">true</property>
</layout>
</object>
</child>
</object>
</child>
</template>

Loading…
Cancel
Save