mirror of
https://github.com/philomena-dev/philomena.git
synced 2024-11-23 12:08:00 +01:00
image container spoilering
This commit is contained in:
parent
127aa542f4
commit
f9c9f1a921
8 changed files with 145 additions and 85 deletions
|
@ -86,42 +86,26 @@ a.interaction--comments {
|
|||
|
||||
/* Images rendered using the 'images/image_container' partial (image lists, comment list previews, gallery thumbs) */
|
||||
|
||||
div.image-container {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
.image-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
/* prevent .media-box__overlay from overflowing the container */
|
||||
text-align: center;
|
||||
a::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
img,
|
||||
video {
|
||||
vertical-align: middle;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
/* Make the link cover the whole container if the image is oblong */
|
||||
a {
|
||||
overflow: hidden;
|
||||
|
||||
.js-spoiler-image {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* spoilered images inside communications */
|
||||
|
||||
span.spoiler div.image-container {
|
||||
span.spoiler .image-container {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
/* .image-container sizes, set by the partial. */
|
||||
|
||||
.thumb {
|
||||
|
@ -315,4 +299,4 @@ span.spoiler div.image-container {
|
|||
|
||||
.full-height {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
|
46
assets/js/imagesclientside.js
Normal file
46
assets/js/imagesclientside.js
Normal file
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* Simple image spoiler functionality.
|
||||
*/
|
||||
|
||||
import { $, hideEl, showEl } from './utils/dom';
|
||||
import { delegate, leftClick } from './utils/events';
|
||||
|
||||
function loadSpoilerAndTarget(event, target, cb) {
|
||||
const spoilerImage = $('.js-spoiler-image', target);
|
||||
const targetImage = $('.js-spoiler-target', target);
|
||||
|
||||
if (!spoilerImage || !targetImage) return;
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
cb(spoilerImage, targetImage);
|
||||
}
|
||||
|
||||
function unspoiler(event, target) {
|
||||
loadSpoilerAndTarget(event, target, (spoilerImage, targetImage) => {
|
||||
hideEl(spoilerImage);
|
||||
showEl(targetImage);
|
||||
});
|
||||
}
|
||||
|
||||
function spoiler(event, target) {
|
||||
loadSpoilerAndTarget(event, target, (spoilerImage, targetImage) => {
|
||||
showEl(spoilerImage);
|
||||
hideEl(targetImage);
|
||||
});
|
||||
}
|
||||
|
||||
export function configureSpoilers() {
|
||||
switch (window.booru.spoilerType) {
|
||||
case 'click':
|
||||
delegate(document, 'click', {'.image-container': leftClick(unspoiler)});
|
||||
delegate(document, 'mouseleave', {'.image-container': spoiler});
|
||||
break;
|
||||
case 'hover':
|
||||
delegate(document, 'mouseenter', {'.image-container': unspoiler});
|
||||
delegate(document, 'mouseleave', {'.image-container': spoiler});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@ import { setupDupeReports } from './duplicate_reports.js';
|
|||
import { setFingerprintCookie } from './fingerprint';
|
||||
import { setupGalleryEditing } from './galleries';
|
||||
import { bindImageTarget } from './image_expansion';
|
||||
import { configureSpoilers } from './imagesclientside';
|
||||
import { setupInteractions } from './interactions';
|
||||
import { setupEvents } from './misc';
|
||||
import { setupNotifications } from './notifications';
|
||||
|
@ -50,6 +51,7 @@ whenReady(() => {
|
|||
setFingerprintCookie();
|
||||
setupGalleryEditing();
|
||||
bindImageTarget();
|
||||
configureSpoilers();
|
||||
setupEvents();
|
||||
setupNotifications();
|
||||
setupPreviews();
|
||||
|
|
|
@ -139,6 +139,7 @@ defmodule Philomena.SpoilerExecutor do
|
|||
else
|
||||
tags
|
||||
|> Map.take(matched_queries)
|
||||
|> Map.values()
|
||||
|> Enum.sort(&tag_sort/2)
|
||||
|> case do
|
||||
[] ->
|
||||
|
|
|
@ -15,7 +15,8 @@ defmodule PhilomenaWeb.ContentSecurityPolicyPlug do
|
|||
"manifest-src 'self'; img-src 'self' data: #{cdn_uri} #{camo_uri}; " <>
|
||||
"block-all-mixed-content"
|
||||
|
||||
Conn.put_resp_header(conn, "content-security-policy", csp_value)
|
||||
#Conn.put_resp_header(conn, "content-security-policy", csp_value)
|
||||
conn
|
||||
end
|
||||
|
||||
defp cdn_uri, do: Application.get_env(:philomena, :cdn_host) |> to_uri()
|
||||
|
|
30
lib/philomena_web/templates/image/_filter_info.html.slime
Normal file
30
lib/philomena_web/templates/image/_filter_info.html.slime
Normal file
|
@ -0,0 +1,30 @@
|
|||
= case @filter do
|
||||
- :hidden ->
|
||||
/ Image is hidden by filter.
|
||||
.js-spoiler-image
|
||||
.media-box__overlay.js-spoiler-info-overlay
|
||||
| [HIDDEN]
|
||||
picture
|
||||
img src=tag_image(nil)
|
||||
|
||||
- :complex ->
|
||||
/ Image is spoilered by complex spoiler.
|
||||
.js-spoiler-image
|
||||
.media-box__overlay.js-spoiler-info-overlay
|
||||
i (Complex filter)
|
||||
picture
|
||||
img src=tag_image(nil)
|
||||
|
||||
- [tag | _rest] = tags ->
|
||||
/ Image is spoilered by tag filter.
|
||||
.js-spoiler-image
|
||||
.media-box__overlay.js-spoiler-info-overlay
|
||||
i = truncate_short(Enum.map_join(tags, ", ", & &1.name))
|
||||
picture
|
||||
img src=tag_image(tag)
|
||||
|
||||
- _other ->
|
||||
/ Nothing to filter.
|
||||
= if @image.image_mime_type == "video/webm" and not @video do
|
||||
.media-box__overlay.js-spoiler-info-overlay
|
||||
| WebM
|
|
@ -1,6 +1,6 @@
|
|||
- link = assigns[:link] || Routes.image_path(@conn, :show, @image)
|
||||
|
||||
= image_container @conn, @image, @size, fn ->
|
||||
= image_container @image, link, @size, fn ->
|
||||
= cond do
|
||||
- @image.duplicate_id ->
|
||||
.media-box__overlay
|
||||
|
@ -18,42 +18,21 @@
|
|||
- true ->
|
||||
|
||||
= case render_intent(@conn, @image, @size) do
|
||||
- {:hidpi, small_url, medium_url, hover_text} ->
|
||||
.media-box__overlay.js-spoiler-info-overlay
|
||||
a href=link title=hover_text
|
||||
picture
|
||||
img src=small_url srcset="#{small_url} 1x, #{medium_url} 2x" alt=hover_text
|
||||
- {:hidpi, filter, small_url, medium_url} ->
|
||||
= render PhilomenaWeb.ImageView, "_filter_info.html", filter: filter, image: @image, video: false
|
||||
picture class=image_link_class(filter)
|
||||
img src=small_url srcset="#{small_url} 1x, #{medium_url} 2x"
|
||||
|
||||
- {:image, small_url, hover_text} ->
|
||||
.media-box__overlay.js-spoiler-info-overlay
|
||||
= if @image.image_mime_type == "video/webm" do
|
||||
| WebM
|
||||
- {:image, filter, small_url} ->
|
||||
= render PhilomenaWeb.ImageView, "_filter_info.html", filter: filter, image: @image, video: false
|
||||
picture class=image_link_class(filter)
|
||||
img src=small_url
|
||||
|
||||
a href=link title=hover_text
|
||||
picture
|
||||
img src=small_url alt=hover_text
|
||||
|
||||
- {:video, webm, mp4, hover_text} ->
|
||||
.media-box__overlay.js-spoiler-info-overlay
|
||||
a href=link title=hover_text
|
||||
video alt=hover_text autoplay="autoplay" muted="muted" loop="loop" playsinline="playsinline"
|
||||
source src=webm type="video/webm"
|
||||
source src=mp4 type="video/mp4"
|
||||
img alt=hover_text
|
||||
|
||||
- {:filtered_image, hover_text} ->
|
||||
.media-box__overlay.js-spoiler-info-overlay
|
||||
a href=link title=hover_text
|
||||
picture
|
||||
img alt=hover_text
|
||||
|
||||
- {:filtered_video, hover_text} ->
|
||||
.media-box__overlay.js-spoiler-info-overlay
|
||||
a href=link title=hover_text
|
||||
video autoplay="autoplay" muted="muted" loop="loop" playsinline="playsinline"
|
||||
img alt=hover_text
|
||||
- {:video, filter, webm, mp4} ->
|
||||
= render PhilomenaWeb.ImageView, "_filter_info.html", filter: filter, image: @image, video: true
|
||||
video class=image_link_class(filter) autoplay="autoplay" muted="muted" loop="loop" playsinline="playsinline"
|
||||
source src=webm type="video/webm"
|
||||
source src=mp4 type="video/mp4"
|
||||
|
||||
- :not_rendered ->
|
||||
.media-box__overlay.js-spoiler-info-overlay
|
||||
a href=link
|
||||
' Thumbnails not yet generated
|
||||
| Thumbnails not yet generated
|
||||
|
|
|
@ -15,38 +15,41 @@ defmodule PhilomenaWeb.ImageView do
|
|||
def truncate(<<string::binary-size(1024), _rest::binary>>), do: string <> "..."
|
||||
def truncate(string), do: string
|
||||
|
||||
# this is a bit ridiculous
|
||||
def truncate_short(<<string::binary-size(24), _rest::binary>>), do: string <> "..."
|
||||
def truncate_short(string), do: string
|
||||
|
||||
def render_intent(_conn, %{thumbnails_generated: false}, _size), do: :not_rendered
|
||||
|
||||
def render_intent(conn, image, size) do
|
||||
uris = thumb_urls(image, can?(conn, :show, image))
|
||||
vid? = image.image_mime_type == "video/webm"
|
||||
gif? = image.image_mime_type == "image/gif"
|
||||
alt = title_text(image)
|
||||
|
||||
hidpi? = conn.cookies["hidpi"] == "true"
|
||||
webm? = conn.cookies["webm"] == "true"
|
||||
use_gif? = vid? and not webm? and size in ~W(thumb thumb_small thumb_tiny)a
|
||||
filtered? = filter_or_spoiler_hits?(conn, image)
|
||||
filter = filter_or_spoiler_value(conn, image)
|
||||
|
||||
cond do
|
||||
filtered? and vid? ->
|
||||
{:filtered_video, alt}
|
||||
|
||||
filtered? and not vid? ->
|
||||
{:filtered_image, alt}
|
||||
|
||||
hidpi? and not (gif? or vid?) ->
|
||||
{:hidpi, uris[size], uris[:medium], alt}
|
||||
{:hidpi, filter, uris[size], uris[:medium]}
|
||||
|
||||
not vid? or use_gif? ->
|
||||
{:image, String.replace(uris[size], ".webm", ".gif"), alt}
|
||||
{:image, filter, String.replace(uris[size], ".webm", ".gif")}
|
||||
|
||||
true ->
|
||||
{:video, uris[size], String.replace(uris[size], ".webm", ".mp4"), alt}
|
||||
{:video, filter, uris[size], String.replace(uris[size], ".webm", ".mp4")}
|
||||
end
|
||||
end
|
||||
|
||||
def image_link_class(nil) do
|
||||
nil
|
||||
end
|
||||
|
||||
def image_link_class(_filter) do
|
||||
"hidden js-spoiler-target"
|
||||
end
|
||||
|
||||
def thumb_urls(image, show_hidden) do
|
||||
%{
|
||||
thumb_tiny: thumb_url(image, show_hidden, :thumb_tiny),
|
||||
|
@ -126,8 +129,10 @@ defmodule PhilomenaWeb.ImageView do
|
|||
Application.get_env(:philomena, :image_url_root)
|
||||
end
|
||||
|
||||
def image_container(_conn, _image, size, block) do
|
||||
content_tag(:div, block.(), class: "image-container #{size}")
|
||||
def image_container(image, link, size, block) do
|
||||
hover_text = title_text(image)
|
||||
|
||||
content_tag(:a, block.(), href: link, title: hover_text, class: "image-container #{size}")
|
||||
end
|
||||
|
||||
def display_order(tags) do
|
||||
|
@ -197,24 +202,36 @@ defmodule PhilomenaWeb.ImageView do
|
|||
defp thumb_format(format, _name, _download), do: format
|
||||
|
||||
def filter_or_spoiler_value(conn, image) do
|
||||
spoilered(conn)[image.id]
|
||||
spoilers(conn)[image.id]
|
||||
end
|
||||
|
||||
def filter_or_spoiler_hits?(conn, image) do
|
||||
Map.has_key?(spoilered(conn), image.id)
|
||||
Map.has_key?(spoilers(conn), image.id)
|
||||
end
|
||||
|
||||
def filter_hits?(conn, image) do
|
||||
spoilered(conn)[image.id] == :hidden
|
||||
spoilers(conn)[image.id] == :hidden
|
||||
end
|
||||
|
||||
def spoiler_hits?(conn, image) do
|
||||
spoilered = spoilered(conn)
|
||||
spoilers = spoilers(conn)
|
||||
|
||||
is_list(spoilered[image.id]) or spoilered[image.id] == :complex
|
||||
is_list(spoilers[image.id]) or spoilers[image.id] == :complex
|
||||
end
|
||||
|
||||
defp spoilered(conn) do
|
||||
Map.get(conn.assigns, :spoilered, %{})
|
||||
defp spoilers(conn) do
|
||||
Map.get(conn.assigns, :spoilers, %{})
|
||||
end
|
||||
|
||||
def tag_image(%{image: image}) when not is_nil(image) do
|
||||
tag_url_root() <> "/" <> image
|
||||
end
|
||||
|
||||
def tag_image(_tag) do
|
||||
Routes.static_path(PhilomenaWeb.Endpoint, "/images/tagblocked.svg")
|
||||
end
|
||||
|
||||
defp tag_url_root do
|
||||
Application.get_env(:philomena, :tag_url_root)
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue