require item revealing when user wants it

This commit is contained in:
2026-05-23 23:23:31 +02:00
parent a3bec09864
commit 6688db6145
11 changed files with 158 additions and 4 deletions

View File

@@ -14734,3 +14734,85 @@ body.scroller-active #gchat-reopen-bubble {
font-size: 11px !important; font-size: 11px !important;
line-height: 1 !important; line-height: 1 !important;
} }
/* Post detail page media-object blur overlay support (NSFW, NSFL, SFW, Untagged) */
.blur-detail-active.blur-nsfw-active .media-object[data-mode="nsfw"]:not(.revealed),
.blur-detail-active.blur-nsfl-active .media-object[data-mode="nsfl"]:not(.revealed),
.blur-detail-active.blur-sfw-active .media-object[data-mode="sfw"]:not(.revealed),
.blur-detail-active.blur-untagged-active .media-object[data-mode="untagged"]:not(.revealed) {
position: relative !important;
cursor: pointer !important;
}
.blur-detail-active.blur-nsfw-active .media-object[data-mode="nsfw"]:not(.revealed) > *,
.blur-detail-active.blur-nsfl-active .media-object[data-mode="nsfl"]:not(.revealed) > *,
.blur-detail-active.blur-sfw-active .media-object[data-mode="sfw"]:not(.revealed) > *,
.blur-detail-active.blur-untagged-active .media-object[data-mode="untagged"]:not(.revealed) > * {
filter: blur(40px) brightness(0.6) !important;
-webkit-filter: blur(40px) brightness(0.6) !important;
pointer-events: none !important;
user-select: none !important;
transition: filter 0.35s ease, -webkit-filter 0.35s ease !important;
}
.media-object.revealed > * {
filter: none !important;
-webkit-filter: none !important;
pointer-events: auto !important;
}
.blur-detail-active.blur-nsfw-active .media-object[data-mode="nsfw"]:not(.revealed)::after,
.blur-detail-active.blur-nsfl-active .media-object[data-mode="nsfl"]:not(.revealed)::after,
.blur-detail-active.blur-sfw-active .media-object[data-mode="sfw"]:not(.revealed)::after,
.blur-detail-active.blur-untagged-active .media-object[data-mode="untagged"]:not(.revealed)::after {
position: absolute !important;
inset: 0 !important;
display: flex !important;
flex-direction: column !important;
align-items: center !important;
justify-content: center !important;
z-index: 1000 !important;
color: #fff !important;
font-family: inherit !important;
font-weight: 800 !important;
letter-spacing: 2px !important;
text-transform: uppercase !important;
text-align: center !important;
white-space: pre-wrap !important;
cursor: pointer !important;
transition: opacity 0.35s cubic-bezier(0.25, 1, 0.5, 1) !important;
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.9) !important;
pointer-events: none !important;
opacity: 1 !important;
}
.blur-detail-active.blur-nsfw-active .media-object[data-mode="nsfw"]:not(.revealed)::after {
content: "NSFW\A \A Click to reveal" !important;
font-size: 18px !important;
color: #ffffff !important;
background: rgba(10, 10, 12, 0.45) !important;
}
.blur-detail-active.blur-nsfl-active .media-object[data-mode="nsfl"]:not(.revealed)::after {
content: "NSFL\A \A Click to reveal" !important;
font-size: 18px !important;
color: #ff3b30 !important;
background: rgba(10, 10, 12, 0.55) !important;
}
.blur-detail-active.blur-sfw-active .media-object[data-mode="sfw"]:not(.revealed)::after {
content: "SFW\A \A Click to reveal" !important;
font-size: 18px !important;
color: #30d158 !important;
background: rgba(10, 10, 12, 0.4) !important;
}
.blur-detail-active.blur-untagged-active .media-object[data-mode="untagged"]:not(.revealed)::after {
content: "UNTAGGED\A \A Click to reveal" !important;
font-size: 18px !important;
color: #ff9f0a !important;
background: rgba(10, 10, 12, 0.45) !important;
}
/* Hide overlay when revealed */
.media-object.revealed::after {
opacity: 0 !important;
pointer-events: none !important;
}

View File

@@ -44,6 +44,7 @@ window.cancelAnimFrame = (function () {
if (localStorage.getItem('blurNsfl') === 'true') htmlEl.classList.add('blur-nsfl-active'); if (localStorage.getItem('blurNsfl') === 'true') htmlEl.classList.add('blur-nsfl-active');
if (localStorage.getItem('blurSfw') === 'true') htmlEl.classList.add('blur-sfw-active'); if (localStorage.getItem('blurSfw') === 'true') htmlEl.classList.add('blur-sfw-active');
if (localStorage.getItem('blurUntagged') === 'true') htmlEl.classList.add('blur-untagged-active'); if (localStorage.getItem('blurUntagged') === 'true') htmlEl.classList.add('blur-untagged-active');
if (localStorage.getItem('blurDetail') !== 'false') htmlEl.classList.add('blur-detail-active');
window.updateVisitIndicators = () => { window.updateVisitIndicators = () => {
try { try {
@@ -8827,6 +8828,30 @@ if (navigator.vibrate) {
const blurUntagged = localStorage.getItem('blurUntagged') === 'true'; const blurUntagged = localStorage.getItem('blurUntagged') === 'true';
if (!blurNsfw && !blurNsfl && !blurSfw && !blurUntagged) return; if (!blurNsfw && !blurNsfl && !blurSfw && !blurUntagged) return;
// Check if they clicked a blurred media-object container on the detail page
const mediaObj = e.target.closest('.media-object');
if (mediaObj && localStorage.getItem('blurDetail') !== 'false') {
const mode = mediaObj.getAttribute('data-mode');
let shouldBlurThis = false;
if (mode === 'nsfw') shouldBlurThis = blurNsfw;
else if (mode === 'nsfl') shouldBlurThis = blurNsfl;
else if (mode === 'sfw') shouldBlurThis = blurSfw;
else if (mode === 'untagged') shouldBlurThis = blurUntagged;
if (shouldBlurThis && !mediaObj.classList.contains('revealed')) {
e.preventDefault();
e.stopPropagation();
mediaObj.classList.add('revealed');
// Start audio/video playback cleanly on reveal
const videoElem = mediaObj.querySelector('video') || mediaObj.querySelector('audio');
if (videoElem) {
videoElem.play().catch(() => {});
}
return;
}
}
// Check if they clicked the elegant hide-again button in the corner // Check if they clicked the elegant hide-again button in the corner
const hideBtn = e.target.closest('.hide-thumb-btn'); const hideBtn = e.target.closest('.hide-thumb-btn');
if (hideBtn) { if (hideBtn) {

View File

@@ -779,6 +779,20 @@
showStatus(enabled ? 'Untagged blurring enabled!' : 'Untagged blurring disabled!', 'success'); showStatus(enabled ? 'Untagged blurring enabled!' : 'Untagged blurring disabled!', 'success');
}); });
} }
const blurDetailToggle = document.getElementById('blur_detail_toggle');
if (blurDetailToggle) {
blurDetailToggle.checked = localStorage.getItem('blurDetail') !== 'false';
blurDetailToggle.addEventListener('change', () => {
const enabled = blurDetailToggle.checked;
localStorage.setItem('blurDetail', enabled ? 'true' : 'false');
if (enabled) {
document.documentElement.classList.add('blur-detail-active');
} else {
document.documentElement.classList.remove('blur-detail-active');
}
showStatus(enabled ? 'Detail page blurring enabled!' : 'Detail page blurring disabled!', 'success');
});
}
// Background Blur Toggle // Background Blur Toggle
const backgroundToggle = document.getElementById('show_background_toggle'); const backgroundToggle = document.getElementById('show_background_toggle');

View File

@@ -421,8 +421,28 @@ class v0ck {
video.volume = _volume = volumeSlider.value = +(localStorage.getItem('volume') ?? defaultVolume); video.volume = _volume = volumeSlider.value = +(localStorage.getItem('volume') ?? defaultVolume);
handleVolumeButton(video.volume); handleVolumeButton(video.volume);
const mediaObj = player.closest('.media-object');
let isBlurredDetail = false;
if (mediaObj && localStorage.getItem('blurDetail') !== 'false') {
const mode = mediaObj.getAttribute('data-mode');
const blurNsfw = localStorage.getItem('blurNsfw') === 'true';
const blurNsfl = localStorage.getItem('blurNsfl') === 'true';
const blurSfw = localStorage.getItem('blurSfw') === 'true';
const blurUntagged = localStorage.getItem('blurUntagged') === 'true';
let shouldBlurThis = false;
if (mode === 'nsfw') shouldBlurThis = blurNsfw;
else if (mode === 'nsfl') shouldBlurThis = blurNsfl;
else if (mode === 'sfw') shouldBlurThis = blurSfw;
else if (mode === 'untagged') shouldBlurThis = blurUntagged;
if (shouldBlurThis && !mediaObj.classList.contains('revealed')) {
isBlurredDetail = true;
}
}
// Attempt autoplay and show overlay if blocked // Attempt autoplay and show overlay if blocked
const shouldAutoplay = window.f0ckSession?.disable_autoplay !== true; const shouldAutoplay = !isBlurredDetail && window.f0ckSession?.disable_autoplay !== true;
if (shouldAutoplay) { if (shouldAutoplay) {
const playPromise = togglePlay(); const playPromise = togglePlay();
if (playPromise !== undefined) { if (playPromise !== undefined) {

View File

@@ -158,6 +158,8 @@
"blur_sfw_hint": "Zeichnet SFW-Vorschaubilder weich.", "blur_sfw_hint": "Zeichnet SFW-Vorschaubilder weich.",
"blur_untagged": "Unmarkierte weichzeichnen", "blur_untagged": "Unmarkierte weichzeichnen",
"blur_untagged_hint": "Zeichnet unmarkierte Vorschaubilder weich.", "blur_untagged_hint": "Zeichnet unmarkierte Vorschaubilder weich.",
"blur_detail": "Beiträge weichzeichnen",
"blur_detail_hint": "Erfordert das Anklicken zum Anzeigen von weichgezeichneten Medien auf der Beitragsseite.",
"render_emojis": "Emojis in Zitatantworten anzeigen", "render_emojis": "Emojis in Zitatantworten anzeigen",
"render_emojis_hint": ":emoji:-Bilder in >zitierten Zeilen anzeigen", "render_emojis_hint": ":emoji:-Bilder in >zitierten Zeilen anzeigen",
"embed_yt": "YouTube-Videos in Kommentaren einbetten", "embed_yt": "YouTube-Videos in Kommentaren einbetten",

View File

@@ -158,6 +158,8 @@
"blur_sfw_hint": "Blur SFW-rated thumbnails.", "blur_sfw_hint": "Blur SFW-rated thumbnails.",
"blur_untagged": "Blur Untagged", "blur_untagged": "Blur Untagged",
"blur_untagged_hint": "Blur thumbnails with no tags or rating.", "blur_untagged_hint": "Blur thumbnails with no tags or rating.",
"blur_detail": "Click to reveal item on detail page",
"blur_detail_hint": "Require clicking to reveal blurred media on the post detail page.",
"render_emojis": "Render emojis in quote replies", "render_emojis": "Render emojis in quote replies",
"render_emojis_hint": "Show :emoji: images inside >quoted lines", "render_emojis_hint": "Show :emoji: images inside >quoted lines",
"embed_yt": "Embed YouTube links in comments", "embed_yt": "Embed YouTube links in comments",

View File

@@ -158,6 +158,8 @@
"blur_sfw_hint": "Vervaagt SFW-thumbnails.", "blur_sfw_hint": "Vervaagt SFW-thumbnails.",
"blur_untagged": "Ongetagde vervagen", "blur_untagged": "Ongetagde vervagen",
"blur_untagged_hint": "Vervaagt ongetagde thumbnails.", "blur_untagged_hint": "Vervaagt ongetagde thumbnails.",
"blur_detail": "Details weigeren direct te tonen (vervagen)",
"blur_detail_hint": "Vereist klikken om vervaagde media op de detailpagina te onthullen.",
"render_emojis": "Emoji's weergeven in antwoorden", "render_emojis": "Emoji's weergeven in antwoorden",
"render_emojis_hint": "Toon :emoji: afbeeldingen binnen >geciteerde regels", "render_emojis_hint": "Toon :emoji: afbeeldingen binnen >geciteerde regels",
"embed_yt": "YouTube-links insluiten in opmerkingen", "embed_yt": "YouTube-links insluiten in opmerkingen",

View File

@@ -22,7 +22,7 @@
</div> </div>
@endif @endif
</div> </div>
<div class="media-object"> <div class="media-object" data-mode="@if(item.is_nsfl)nsfl@elseif(item.is_nsfw)nsfw@elseif(item.is_sfw)sfw@elseuntagged@endif">
@include(snippets/item-media) @include(snippets/item-media)
</div> </div>
<div class="next-post"> <div class="next-post">

View File

@@ -65,7 +65,7 @@
</div> </div>
@endif @endif
</div> </div>
<div class="media-object"> <div class="media-object" data-mode="@if(item.is_nsfl)nsfl@elseif(item.is_nsfw)nsfw@elseif(item.is_sfw)sfw@elseuntagged@endif">
@include(snippets/item-media) @include(snippets/item-media)
</div> </div>
<div class="next-post"> <div class="next-post">

View File

@@ -319,6 +319,13 @@
</label> </label>
<small class="text-muted" style="margin-left: 25px;">{{ t('settings.blur_untagged_hint') }}</small> <small class="text-muted" style="margin-left: 25px;">{{ t('settings.blur_untagged_hint') }}</small>
</div> </div>
<div class="setting-item" style="margin-top: 15px; border-top: 1px dashed var(--nav-border-color); padding-top: 15px;">
<label for="blur_detail_toggle" style="cursor: pointer; display: flex; align-items: center; gap: 10px;">
<input type="checkbox" id="blur_detail_toggle">
<span>{{ t('settings.blur_detail') || 'Click to reveal item on detail page' }}</span>
</label>
<small class="text-muted" style="margin-left: 25px;">{{ t('settings.blur_detail_hint') || 'Require clicking to reveal blurred media on the post detail page instead of showing it immediately.' }}</small>
</div>
</fieldset> </fieldset>
<fieldset <fieldset

View File

@@ -8,7 +8,7 @@
</div> </div>
@elseif(item.mime.startsWith("audio")) @elseif(item.mime.startsWith("audio"))
<div class="embed-responsive embed-responsive-16by9" style="background: url('@if(item.coverart)//w0bm.com{{ item.coverart }}@else/s/img/200.gif@endif') no-repeat center / contain black;"> <div class="embed-responsive embed-responsive-16by9" style="background: url('@if(item.coverart)//w0bm.com{{ item.coverart }}@else/s/img/200.gif@endif') no-repeat center / contain black;">
<audio id="my-video" class="embed-responsive-item" autoplay loop src="{{ item.dest }}" data-setup="{}" data-size="{{ item.size }}" poster="@if(item.coverart){{ item.coverart }}@else/s/img/200.gif@endif" type="{{ item.mime }}"></audio> <audio id="my-video" class="embed-responsive-item" loop src="{{ item.dest }}" data-setup="{}" data-size="{{ item.size }}" poster="@if(item.coverart){{ item.coverart }}@else/s/img/200.gif@endif" type="{{ item.mime }}"></audio>
<img id="f0ck-audio-cover" src="@if(item.coverart){{ item.coverart }}@else/s/img/200.gif@endif" style="display: none;"> <img id="f0ck-audio-cover" src="@if(item.coverart){{ item.coverart }}@else/s/img/200.gif@endif" style="display: none;">
</div> </div>
@elseif(item.mime.startsWith("image")) @elseif(item.mime.startsWith("image"))