Slightly improve the media experience (#452)

* Slightly improve the media experience

- Use a grid to display the list of media
- Add icons for non-image media preview
- Paginate the gallery
- Add links to the gallery in the editor and in the profile settings to make it more discoverable when you need it

Fixes #432

* Allow video and audio tags in SafeString

Otherwise we can't display their preview, nor show them in articles

Also show controls by default for these two elements

* Show fallback images for audio and unknown files, to make them more visible

* Add a new constructor to SafeString when the input is trusted and doesn't need to be escaped.

And use it to generate media previews.

* Make it possible to insert video/audio in articles
This commit is contained in:
Baptiste Gelez
2019-03-06 14:11:36 +01:00
committed by GitHub
parent a5e0486da0
commit eff2698664
27 changed files with 677 additions and 58 deletions
+37 -25
View File
@@ -45,6 +45,17 @@ pub enum MediaCategory {
Unknown,
}
impl MediaCategory {
pub fn to_string(&self) -> &str {
match *self {
MediaCategory::Image => "image",
MediaCategory::Audio => "audio",
MediaCategory::Video => "video",
MediaCategory::Unknown => "unknown",
}
}
}
impl Media {
insert!(medias, NewMedia);
get!(medias);
@@ -56,6 +67,23 @@ impl Media {
.map_err(Error::from)
}
pub fn page_for_user(conn: &Connection, user: &User, (min, max): (i32, i32)) -> Result<Vec<Media>> {
medias::table
.filter(medias::owner_id.eq(user.id))
.offset(min as i64)
.limit((max - min) as i64)
.load::<Media>(conn)
.map_err(Error::from)
}
pub fn count_for_user(conn: &Connection, user: &User) -> Result<i64> {
medias::table
.filter(medias::owner_id.eq(user.id))
.count()
.get_result(conn)
.map_err(Error::from)
}
pub fn category(&self) -> MediaCategory {
match &*self
.file_path
@@ -71,41 +99,25 @@ impl Media {
}
}
pub fn preview_html(&self, conn: &Connection) -> Result<SafeString> {
let url = self.url(conn)?;
Ok(match self.category() {
MediaCategory::Image => SafeString::new(&format!(
r#"<img src="{}" alt="{}" title="{}" class=\"preview\">"#,
url, escape(&self.alt_text), escape(&self.alt_text)
)),
MediaCategory::Audio => SafeString::new(&format!(
r#"<audio src="{}" title="{}" class="preview"></audio>"#,
url, escape(&self.alt_text)
)),
MediaCategory::Video => SafeString::new(&format!(
r#"<video src="{}" title="{}" class="preview"></video>"#,
url, escape(&self.alt_text)
)),
MediaCategory::Unknown => SafeString::new(""),
})
}
pub fn html(&self, conn: &Connection) -> Result<SafeString> {
let url = self.url(conn)?;
Ok(match self.category() {
MediaCategory::Image => SafeString::new(&format!(
MediaCategory::Image => SafeString::trusted(&format!(
r#"<img src="{}" alt="{}" title="{}">"#,
url, escape(&self.alt_text), escape(&self.alt_text)
)),
MediaCategory::Audio => SafeString::new(&format!(
r#"<audio src="{}" title="{}"></audio>"#,
MediaCategory::Audio => SafeString::trusted(&format!(
r#"<div class="media-preview audio"></div><audio src="{}" title="{}" controls></audio>"#,
url, escape(&self.alt_text)
)),
MediaCategory::Video => SafeString::new(&format!(
r#"<video src="{}" title="{}"></video>"#,
MediaCategory::Video => SafeString::trusted(&format!(
r#"<video src="{}" title="{}" controls></video>"#,
url, escape(&self.alt_text)
)),
MediaCategory::Unknown => SafeString::new(""),
MediaCategory::Unknown => SafeString::trusted(&format!(
r#"<a href="{}" class="media-preview unknown"></a>"#,
url,
)),
})
}
+22 -2
View File
@@ -19,12 +19,20 @@ lazy_static! {
static ref CLEAN: Builder<'static> = {
let mut b = Builder::new();
b.add_generic_attributes(iter::once("id"))
.add_tags(iter::once("iframe"))
.add_tags(&[ "iframe", "video", "audio" ])
.id_prefix(Some("postcontent-"))
.url_relative(UrlRelative::Custom(Box::new(url_add_prefix)))
.add_tag_attributes(
"iframe",
["width", "height", "src", "frameborder"].iter().map(|&v| v),
[ "width", "height", "src", "frameborder" ].iter().map(|&v| v),
)
.add_tag_attributes(
"video",
[ "src", "title", "controls" ].iter(),
)
.add_tag_attributes(
"audio",
[ "src", "title", "controls" ].iter(),
);
b
};
@@ -53,6 +61,18 @@ impl SafeString {
value: CLEAN.clean(&value).to_string(),
}
}
/// Creates a new `SafeString`, but without escaping the given value.
///
/// Only use when you are sure you can trust the input (when the HTML
/// is entirely generated by Plume, not depending on user-inputed data).
/// Prefer `SafeString::new` as much as possible.
pub fn trusted(value: impl AsRef<str>) -> Self {
SafeString {
value: value.as_ref().to_string()
}
}
pub fn set(&mut self, value: &str) {
self.value = CLEAN.clean(value).to_string();
}