add alternative controls
This commit is contained in:
@@ -111,6 +111,7 @@
|
|||||||
"enable_swiping": true,
|
"enable_swiping": true,
|
||||||
"enable_profile_description": true,
|
"enable_profile_description": true,
|
||||||
"user_alternative_infobox": false,
|
"user_alternative_infobox": false,
|
||||||
|
"user_alternative_steuerung": false,
|
||||||
"enable_swf": false,
|
"enable_swf": false,
|
||||||
"swf_thumb": "/s/img/swf.png",
|
"swf_thumb": "/s/img/swf.png",
|
||||||
"enable_item_title": true,
|
"enable_item_title": true,
|
||||||
|
|||||||
@@ -1493,6 +1493,7 @@ CREATE TABLE public.user_options (
|
|||||||
hide_koepfe boolean DEFAULT false NOT NULL,
|
hide_koepfe boolean DEFAULT false NOT NULL,
|
||||||
language text,
|
language text,
|
||||||
use_alternative_infobox boolean DEFAULT false,
|
use_alternative_infobox boolean DEFAULT false,
|
||||||
|
use_alternative_steuerung boolean DEFAULT false,
|
||||||
receive_system_notifications boolean DEFAULT true,
|
receive_system_notifications boolean DEFAULT true,
|
||||||
receive_user_notifications boolean DEFAULT true,
|
receive_user_notifications boolean DEFAULT true,
|
||||||
do_not_disturb boolean DEFAULT false,
|
do_not_disturb boolean DEFAULT false,
|
||||||
|
|||||||
@@ -9023,6 +9023,28 @@ html[theme="f0ck95d"] .badge-dark {
|
|||||||
color: white;
|
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 {
|
.blahlol {
|
||||||
grid-column: 1 / 4;
|
grid-column: 1 / 4;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -9099,6 +9099,16 @@ if (navigator.vibrate) {
|
|||||||
}, { passive: true });
|
}, { 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 ─────────────────────────────────────────────
|
// ── Spoiler Tags Event Delegation ─────────────────────────────────────────────
|
||||||
(function() {
|
(function() {
|
||||||
const isHidden = (el) => el && el.classList && (el.classList.contains('spoiler') || el.classList.contains('blur-text'));
|
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
|
// Notification Preferences Toggles
|
||||||
const setupPreferenceToggle = (id, sessionKey) => {
|
const setupPreferenceToggle = (id, sessionKey) => {
|
||||||
const el = document.getElementById(id);
|
const el = document.getElementById(id);
|
||||||
|
|||||||
@@ -142,6 +142,8 @@
|
|||||||
"modern_layout_hint": "3-Spalten-Layout",
|
"modern_layout_hint": "3-Spalten-Layout",
|
||||||
"alternative_infobox": "Alternativer Autor-Infoblock",
|
"alternative_infobox": "Alternativer Autor-Infoblock",
|
||||||
"alternative_infobox_hint": "Zeigt einen erweiterten Autor-Block mit Avatar und Bio auf Beitragsseiten",
|
"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": "Automatische Wiedergabe deaktivieren",
|
||||||
"disable_autoplay_hint": "Verhindert die automatische Wiedergabe von Videos und Audio",
|
"disable_autoplay_hint": "Verhindert die automatische Wiedergabe von Videos und Audio",
|
||||||
"disable_swiping": "Wischen deaktivieren",
|
"disable_swiping": "Wischen deaktivieren",
|
||||||
|
|||||||
@@ -142,6 +142,8 @@
|
|||||||
"modern_layout_hint": "3 Column Layout",
|
"modern_layout_hint": "3 Column Layout",
|
||||||
"alternative_infobox": "Alternative Author Infobox",
|
"alternative_infobox": "Alternative Author Infobox",
|
||||||
"alternative_infobox_hint": "Show a rich author card with avatar and bio on item pages",
|
"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": "Disable Autoplay",
|
||||||
"disable_autoplay_hint": "Prevent videos and audio from playing automatically",
|
"disable_autoplay_hint": "Prevent videos and audio from playing automatically",
|
||||||
"disable_swiping": "Disable Swiping",
|
"disable_swiping": "Disable Swiping",
|
||||||
|
|||||||
@@ -142,6 +142,8 @@
|
|||||||
"modern_layout_hint": "Indeling met 3 kolommen",
|
"modern_layout_hint": "Indeling met 3 kolommen",
|
||||||
"alternative_infobox": "Alternatief auteur-informatievak",
|
"alternative_infobox": "Alternatief auteur-informatievak",
|
||||||
"alternative_infobox_hint": "Toont een uitgebreide auteurkaart met avatar en bio op itempagina's",
|
"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": "Automatisch afspelen uitschakelen",
|
||||||
"disable_autoplay_hint": "Voorkomen dat video's en audio automatisch worden afgespeeld",
|
"disable_autoplay_hint": "Voorkomen dat video's en audio automatisch worden afgespeeld",
|
||||||
"disable_swiping": "Swipen uitschakelen",
|
"disable_swiping": "Swipen uitschakelen",
|
||||||
|
|||||||
@@ -142,6 +142,8 @@
|
|||||||
"modern_layout_hint": "3-Spalten-Layout",
|
"modern_layout_hint": "3-Spalten-Layout",
|
||||||
"alternative_infobox": "Alternativer Autor-Infoblock",
|
"alternative_infobox": "Alternativer Autor-Infoblock",
|
||||||
"alternative_infobox_hint": "Zeigt einen erweiterten Autor-Block mit Avatar und Bio auf Beitragsseiten",
|
"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": "Automatische Wiedergabe deaktivieren",
|
||||||
"disable_autoplay_hint": "Vermeiden Sie das automatische Abspielen von Videos und Tondateien",
|
"disable_autoplay_hint": "Vermeiden Sie das automatische Abspielen von Videos und Tondateien",
|
||||||
"disable_swiping": "Wischen deaktivieren",
|
"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
|
// Update per-user language preference
|
||||||
group.put(/\/language/, lib.loggedin, async (req, res) => {
|
group.put(/\/language/, lib.loggedin, async (req, res) => {
|
||||||
if (cfg.websrv.allow_language_change === false) {
|
if (cfg.websrv.allow_language_change === false) {
|
||||||
|
|||||||
@@ -506,7 +506,7 @@ process.on('uncaughtException', err => {
|
|||||||
|
|
||||||
if (req.cookies.session) {
|
if (req.cookies.session) {
|
||||||
const user = await db`
|
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"
|
from "user_sessions"
|
||||||
left join "user" on "user".id = "user_sessions".user_id
|
left join "user" on "user".id = "user_sessions".user_id
|
||||||
left join "user_options" on "user_options".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,
|
hide_koepfe: user[0].hide_koepfe ?? false,
|
||||||
language: (user[0].language && user[0].language.trim()) ? user[0].language.trim() : null,
|
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_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),
|
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
|
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
|
on conflict ("user_id") do update set
|
||||||
theme = excluded.theme,
|
theme = excluded.theme,
|
||||||
@@ -652,6 +653,7 @@ process.on('uncaughtException', err => {
|
|||||||
hide_koepfe = excluded.hide_koepfe,
|
hide_koepfe = excluded.hide_koepfe,
|
||||||
language = excluded.language,
|
language = excluded.language,
|
||||||
use_alternative_infobox = excluded.use_alternative_infobox,
|
use_alternative_infobox = excluded.use_alternative_infobox,
|
||||||
|
use_alternative_steuerung = excluded.use_alternative_steuerung,
|
||||||
comment_display_mode = excluded.comment_display_mode,
|
comment_display_mode = excluded.comment_display_mode,
|
||||||
force_comment_display_mode = excluded.force_comment_display_mode,
|
force_comment_display_mode = excluded.force_comment_display_mode,
|
||||||
user_id = excluded.user_id
|
user_id = excluded.user_id
|
||||||
@@ -1222,10 +1224,19 @@ process.on('uncaughtException', err => {
|
|||||||
? data.user_alternative_infobox
|
? data.user_alternative_infobox
|
||||||
: (cfg.websrv.user_alternative_infobox !== false)));
|
: (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 || {}, {
|
data = Object.assign({}, globals, data || {}, {
|
||||||
t: perRequestT,
|
t: perRequestT,
|
||||||
lang: perRequestLang,
|
lang: perRequestLang,
|
||||||
user_alternative_infobox: useAltInfobox,
|
user_alternative_infobox: useAltInfobox,
|
||||||
|
user_alternative_steuerung: useAltSteuerung,
|
||||||
comment_display_mode: (req && req.session && typeof req.session.comment_display_mode === 'number')
|
comment_display_mode: (req && req.session && typeof req.session.comment_display_mode === 'number')
|
||||||
? req.session.comment_display_mode
|
? req.session.comment_display_mode
|
||||||
: (data && typeof data.comment_display_mode === 'number'
|
: (data && typeof data.comment_display_mode === 'number'
|
||||||
|
|||||||
@@ -45,6 +45,7 @@
|
|||||||
<div class="kontrollelement">
|
<div class="kontrollelement">
|
||||||
<div class="einheit">
|
<div class="einheit">
|
||||||
@if(typeof pagination !== "undefined")
|
@if(typeof pagination !== "undefined")
|
||||||
|
@if(!user_alternative_steuerung)
|
||||||
<nav class="steuerung">
|
<nav class="steuerung">
|
||||||
@if(pagination.next)
|
@if(pagination.next)
|
||||||
<a class="nav-prev" href="{{ link.main }}{{ pagination.next }}{{ link.suffix }}">← {{ t('nav.prev') }}</a>
|
<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>
|
<a class="nav-next" href="#" style="visibility: hidden">{{ t('nav.next') }} →</a>
|
||||||
@endif
|
@endif
|
||||||
</nav>
|
</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
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -197,6 +197,17 @@
|
|||||||
<small class="text-muted" style="margin-left: 25px;">{{ t('settings.alternative_infobox_hint') }}</small>
|
<small class="text-muted" style="margin-left: 25px;">{{ t('settings.alternative_infobox_hint') }}</small>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@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;">
|
<div class="setting-item" style="margin-bottom: 15px;">
|
||||||
<label for="wheel_nav_toggle" style="cursor: pointer; display: flex; align-items: center; gap: 10px;">
|
<label for="wheel_nav_toggle" style="cursor: pointer; display: flex; align-items: center; gap: 10px;">
|
||||||
<input type="checkbox" id="wheel_nav_toggle">
|
<input type="checkbox" id="wheel_nav_toggle">
|
||||||
|
|||||||
Reference in New Issue
Block a user