add alternative controls
This commit is contained in:
@@ -111,6 +111,7 @@
|
||||
"enable_swiping": true,
|
||||
"enable_profile_description": true,
|
||||
"user_alternative_infobox": false,
|
||||
"user_alternative_steuerung": false,
|
||||
"enable_swf": false,
|
||||
"swf_thumb": "/s/img/swf.png",
|
||||
"enable_item_title": true,
|
||||
|
||||
@@ -1493,6 +1493,7 @@ CREATE TABLE public.user_options (
|
||||
hide_koepfe boolean DEFAULT false NOT NULL,
|
||||
language text,
|
||||
use_alternative_infobox boolean DEFAULT false,
|
||||
use_alternative_steuerung boolean DEFAULT false,
|
||||
receive_system_notifications boolean DEFAULT true,
|
||||
receive_user_notifications boolean DEFAULT true,
|
||||
do_not_disturb boolean DEFAULT false,
|
||||
|
||||
@@ -9023,6 +9023,28 @@ html[theme="f0ck95d"] .badge-dark {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.steuerung.steuerung-icon {
|
||||
font-size: x-large;
|
||||
}
|
||||
|
||||
.steuerung.steuerung-icon a {
|
||||
color: white;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 1.6em;
|
||||
transition: color 0.15s ease, transform 0.15s ease;
|
||||
}
|
||||
|
||||
.steuerung.steuerung-icon a:hover {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.steuerung.steuerung-icon .fa-solid {
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
|
||||
.blahlol {
|
||||
grid-column: 1 / 4;
|
||||
width: 100%;
|
||||
|
||||
@@ -9099,6 +9099,16 @@ if (navigator.vibrate) {
|
||||
}, { passive: true });
|
||||
}
|
||||
|
||||
// ── Steuerung icon style: #scrolltobottom smooth scroll ───────────────────────
|
||||
// The alternative icon nav replaces the Zufall link with a down-chevron that
|
||||
// scrolls the page to the bottom (comments / tag section).
|
||||
document.addEventListener('click', (e) => {
|
||||
const link = e.target.closest('a.steuerung-scroll-down');
|
||||
if (!link) return;
|
||||
e.preventDefault();
|
||||
window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
|
||||
});
|
||||
|
||||
// ── Spoiler Tags Event Delegation ─────────────────────────────────────────────
|
||||
(function() {
|
||||
const isHidden = (el) => el && el.classList && (el.classList.contains('spoiler') || el.classList.contains('blur-text'));
|
||||
|
||||
@@ -670,6 +670,37 @@
|
||||
});
|
||||
}
|
||||
|
||||
// Alternative Steuerung Toggle (icon-only nav style)
|
||||
const alternativeSteuerungToggle = document.getElementById('alternative_steuerung_toggle');
|
||||
if (alternativeSteuerungToggle) {
|
||||
alternativeSteuerungToggle.addEventListener('change', async () => {
|
||||
const use_alternative_steuerung = alternativeSteuerungToggle.checked;
|
||||
try {
|
||||
const res = await fetch('/api/v2/settings/alternative_steuerung', {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'X-CSRF-Token': window.f0ckSession?.csrf_token
|
||||
},
|
||||
body: new URLSearchParams({ use_alternative_steuerung })
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.success) {
|
||||
showStatus('Navigation style updated!', 'success');
|
||||
if (window.f0ckSession) window.f0ckSession.use_alternative_steuerung = use_alternative_steuerung;
|
||||
} else {
|
||||
alert(data.msg || 'Error saving preference');
|
||||
alternativeSteuerungToggle.checked = !use_alternative_steuerung;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
alert('Failed to save navigation style preference');
|
||||
alternativeSteuerungToggle.checked = !use_alternative_steuerung;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Notification Preferences Toggles
|
||||
const setupPreferenceToggle = (id, sessionKey) => {
|
||||
const el = document.getElementById(id);
|
||||
|
||||
@@ -142,6 +142,8 @@
|
||||
"modern_layout_hint": "3-Spalten-Layout",
|
||||
"alternative_infobox": "Alternativer Autor-Infoblock",
|
||||
"alternative_infobox_hint": "Zeigt einen erweiterten Autor-Block mit Avatar und Bio auf Beitragsseiten",
|
||||
"alternative_steuerung": "Icon-Navigationsstil",
|
||||
"alternative_steuerung_hint": "Ersetzt die Text-Navigation (← zurück | Zufall | weiter →) durch kompakte Chevron-Icons",
|
||||
"disable_autoplay": "Automatische Wiedergabe deaktivieren",
|
||||
"disable_autoplay_hint": "Verhindert die automatische Wiedergabe von Videos und Audio",
|
||||
"disable_swiping": "Wischen deaktivieren",
|
||||
|
||||
@@ -142,6 +142,8 @@
|
||||
"modern_layout_hint": "3 Column Layout",
|
||||
"alternative_infobox": "Alternative Author Infobox",
|
||||
"alternative_infobox_hint": "Show a rich author card with avatar and bio on item pages",
|
||||
"alternative_steuerung": "Icon nav style",
|
||||
"alternative_steuerung_hint": "Replace text navigation (← prev | random | next →) with compact chevron icons",
|
||||
"disable_autoplay": "Disable Autoplay",
|
||||
"disable_autoplay_hint": "Prevent videos and audio from playing automatically",
|
||||
"disable_swiping": "Disable Swiping",
|
||||
|
||||
@@ -142,6 +142,8 @@
|
||||
"modern_layout_hint": "Indeling met 3 kolommen",
|
||||
"alternative_infobox": "Alternatief auteur-informatievak",
|
||||
"alternative_infobox_hint": "Toont een uitgebreide auteurkaart met avatar en bio op itempagina's",
|
||||
"alternative_steuerung": "Icoon-navigatiestijl",
|
||||
"alternative_steuerung_hint": "Vervangt tekstnavigatie (← terug | willekeurig | verder →) door compacte chevron-iconen",
|
||||
"disable_autoplay": "Automatisch afspelen uitschakelen",
|
||||
"disable_autoplay_hint": "Voorkomen dat video's en audio automatisch worden afgespeeld",
|
||||
"disable_swiping": "Swipen uitschakelen",
|
||||
|
||||
@@ -142,6 +142,8 @@
|
||||
"modern_layout_hint": "3-Spalten-Layout",
|
||||
"alternative_infobox": "Alternativer Autor-Infoblock",
|
||||
"alternative_infobox_hint": "Zeigt einen erweiterten Autor-Block mit Avatar und Bio auf Beitragsseiten",
|
||||
"alternative_steuerung": "Icon-Navigationsstil",
|
||||
"alternative_steuerung_hint": "Ersetzt die Text-Navigation (← zurück | Zufall | weiter →) durch kompakte Chevron-Icons",
|
||||
"disable_autoplay": "Automatische Wiedergabe deaktivieren",
|
||||
"disable_autoplay_hint": "Vermeiden Sie das automatische Abspielen von Videos und Tondateien",
|
||||
"disable_swiping": "Wischen deaktivieren",
|
||||
|
||||
@@ -645,6 +645,24 @@ export default router => {
|
||||
}
|
||||
});
|
||||
|
||||
// Update alternative steuerung preference (per-user toggle for icon-only nav)
|
||||
group.put(/\/alternative_steuerung/, lib.loggedin, async (req, res) => {
|
||||
const use_alternative_steuerung = req.post.use_alternative_steuerung === true || req.post.use_alternative_steuerung === 'true';
|
||||
try {
|
||||
await db`
|
||||
update user_options
|
||||
set use_alternative_steuerung = ${use_alternative_steuerung}
|
||||
where user_id = ${+req.session.id}
|
||||
`;
|
||||
if (req.session) req.session.use_alternative_steuerung = use_alternative_steuerung;
|
||||
return res.json({ success: true, use_alternative_steuerung }, 200);
|
||||
} catch (e) {
|
||||
console.error('Update alternative_steuerung error:', e);
|
||||
return res.json({ success: false, msg: 'Error updating preference' }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Update per-user language preference
|
||||
group.put(/\/language/, lib.loggedin, async (req, res) => {
|
||||
if (cfg.websrv.allow_language_change === false) {
|
||||
|
||||
@@ -506,7 +506,7 @@ process.on('uncaughtException', err => {
|
||||
|
||||
if (req.cookies.session) {
|
||||
const user = await db`
|
||||
select "user".id, "user".login, "user".user, "user".admin, "user".is_moderator, "user".banned, "user".ban_reason, "user".ban_expires, "user".force_password_change, "user_sessions".id as sess_id, "user_sessions".csrf_token, "user_options".mode, "user_options".theme, "user_options".fullscreen, "user_options".excluded_tags, "user_options".avatar, "user_options".avatar_file, "user_options".show_motd, "user_options".strict_mode, "user_options".show_background, "user_options".use_new_layout, "user_options".username_color, "user_options".font, "user_options".disable_autoplay, "user_options".disable_swiping, "user_options".description, "user_options".display_name, COALESCE("user_options".min_xd_score, 0) as min_xd_score, "user_options".ruffle_volume, "user_options".ruffle_background, "user_options".quote_emojis, "user_options".embed_youtube_in_comments, "user_options".hide_koepfe, "user_options".language, "user_options".use_alternative_infobox, "user_options".receive_system_notifications, "user_options".receive_user_notifications, "user_options".do_not_disturb, "user_options".comment_display_mode, "user_options".force_comment_display_mode
|
||||
select "user".id, "user".login, "user".user, "user".admin, "user".is_moderator, "user".banned, "user".ban_reason, "user".ban_expires, "user".force_password_change, "user_sessions".id as sess_id, "user_sessions".csrf_token, "user_options".mode, "user_options".theme, "user_options".fullscreen, "user_options".excluded_tags, "user_options".avatar, "user_options".avatar_file, "user_options".show_motd, "user_options".strict_mode, "user_options".show_background, "user_options".use_new_layout, "user_options".username_color, "user_options".font, "user_options".disable_autoplay, "user_options".disable_swiping, "user_options".description, "user_options".display_name, COALESCE("user_options".min_xd_score, 0) as min_xd_score, "user_options".ruffle_volume, "user_options".ruffle_background, "user_options".quote_emojis, "user_options".embed_youtube_in_comments, "user_options".hide_koepfe, "user_options".language, "user_options".use_alternative_infobox, "user_options".use_alternative_steuerung, "user_options".receive_system_notifications, "user_options".receive_user_notifications, "user_options".do_not_disturb, "user_options".comment_display_mode, "user_options".force_comment_display_mode
|
||||
from "user_sessions"
|
||||
left join "user" on "user".id = "user_sessions".user_id
|
||||
left join "user_options" on "user_options".user_id = "user_sessions".user_id
|
||||
@@ -633,9 +633,10 @@ process.on('uncaughtException', err => {
|
||||
hide_koepfe: user[0].hide_koepfe ?? false,
|
||||
language: (user[0].language && user[0].language.trim()) ? user[0].language.trim() : null,
|
||||
use_alternative_infobox: user[0].use_alternative_infobox ?? (cfg.websrv.user_alternative_infobox !== false),
|
||||
use_alternative_steuerung: user[0].use_alternative_steuerung ?? (cfg.websrv.user_alternative_steuerung !== false),
|
||||
comment_display_mode: user[0].comment_display_mode ?? (cfg.websrv.default_comment_display_mode || 0),
|
||||
force_comment_display_mode: user[0].force_comment_display_mode ?? 0
|
||||
}, 'user_id', 'mode', 'theme', 'fullscreen', 'excluded_tags', 'font', 'disable_autoplay', 'disable_swiping', 'show_background', 'ruffle_volume', 'ruffle_background', 'quote_emojis', 'embed_youtube_in_comments', 'hide_koepfe', 'language', 'use_alternative_infobox', 'comment_display_mode', 'force_comment_display_mode')
|
||||
}, 'user_id', 'mode', 'theme', 'fullscreen', 'excluded_tags', 'font', 'disable_autoplay', 'disable_swiping', 'show_background', 'ruffle_volume', 'ruffle_background', 'quote_emojis', 'embed_youtube_in_comments', 'hide_koepfe', 'language', 'use_alternative_infobox', 'use_alternative_steuerung', 'comment_display_mode', 'force_comment_display_mode')
|
||||
}
|
||||
on conflict ("user_id") do update set
|
||||
theme = excluded.theme,
|
||||
@@ -652,6 +653,7 @@ process.on('uncaughtException', err => {
|
||||
hide_koepfe = excluded.hide_koepfe,
|
||||
language = excluded.language,
|
||||
use_alternative_infobox = excluded.use_alternative_infobox,
|
||||
use_alternative_steuerung = excluded.use_alternative_steuerung,
|
||||
comment_display_mode = excluded.comment_display_mode,
|
||||
force_comment_display_mode = excluded.force_comment_display_mode,
|
||||
user_id = excluded.user_id
|
||||
@@ -1222,10 +1224,19 @@ process.on('uncaughtException', err => {
|
||||
? data.user_alternative_infobox
|
||||
: (cfg.websrv.user_alternative_infobox !== false)));
|
||||
|
||||
const useAltSteuerung = (req && req.session && typeof req.session.use_alternative_steuerung === 'boolean')
|
||||
? req.session.use_alternative_steuerung
|
||||
: (req && !req.session
|
||||
? false
|
||||
: (data && typeof data.user_alternative_steuerung === 'boolean'
|
||||
? data.user_alternative_steuerung
|
||||
: (cfg.websrv.user_alternative_steuerung !== false)));
|
||||
|
||||
data = Object.assign({}, globals, data || {}, {
|
||||
t: perRequestT,
|
||||
lang: perRequestLang,
|
||||
user_alternative_infobox: useAltInfobox,
|
||||
user_alternative_steuerung: useAltSteuerung,
|
||||
comment_display_mode: (req && req.session && typeof req.session.comment_display_mode === 'number')
|
||||
? req.session.comment_display_mode
|
||||
: (data && typeof data.comment_display_mode === 'number'
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
<div class="kontrollelement">
|
||||
<div class="einheit">
|
||||
@if(typeof pagination !== "undefined")
|
||||
@if(!user_alternative_steuerung)
|
||||
<nav class="steuerung">
|
||||
@if(pagination.next)
|
||||
<a class="nav-prev" href="{{ link.main }}{{ pagination.next }}{{ link.suffix }}">← {{ t('nav.prev') }}</a>
|
||||
@@ -62,6 +63,21 @@
|
||||
<a class="nav-next" href="#" style="visibility: hidden">{{ t('nav.next') }} →</a>
|
||||
@endif
|
||||
</nav>
|
||||
@else
|
||||
<nav class="steuerung steuerung-icon">
|
||||
@if(pagination.next)
|
||||
<a class="nav-prev" href="{{ link.main }}{{ pagination.next }}{{ link.suffix }}"><i class="fa-solid fa-chevron-left"></i></a>
|
||||
@else
|
||||
<a class="nav-prev" href="#" style="visibility: hidden"><i class="fa-solid fa-chevron-left"></i></a>
|
||||
@endif
|
||||
<a class="steuerung-scroll-down" href="#scrolltobottom"><i class="fa-solid fa-chevron-down"></i></a>
|
||||
@if(pagination.prev)
|
||||
<a class="nav-next" href="{{ link.main }}{{ pagination.prev }}{{ link.suffix }}"><i class="fa-solid fa-chevron-right"></i></a>
|
||||
@else
|
||||
<a class="nav-next" href="#" style="visibility: hidden"><i class="fa-solid fa-chevron-right"></i></a>
|
||||
@endif
|
||||
</nav>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -197,6 +197,17 @@
|
||||
<small class="text-muted" style="margin-left: 25px;">{{ t('settings.alternative_infobox_hint') }}</small>
|
||||
</div>
|
||||
@endif
|
||||
@if(!session.use_new_layout)
|
||||
<div class="setting-item" style="margin-top: 15px;">
|
||||
<label for="alternative_steuerung_toggle"
|
||||
style="cursor: pointer; display: flex; align-items: center; gap: 10px;">
|
||||
<input type="checkbox" id="alternative_steuerung_toggle" @if(session.use_alternative_steuerung===true) checked @endif>
|
||||
<span>{{ t('settings.alternative_steuerung') }}</span>
|
||||
</label>
|
||||
<small class="text-muted" style="margin-left: 25px;">{{ t('settings.alternative_steuerung_hint') }}</small>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="setting-item" style="margin-bottom: 15px;">
|
||||
<label for="wheel_nav_toggle" style="cursor: pointer; display: flex; align-items: center; gap: 10px;">
|
||||
<input type="checkbox" id="wheel_nav_toggle">
|
||||
|
||||
Reference in New Issue
Block a user