9 Commits

Author SHA1 Message Date
6856f4109e g 2026-07-18 12:31:05 +02:00
4fa0ed7e58 f0ckv2 specifics... 2026-07-18 12:27:52 +02:00
2a5f8288d2 f0ckv2 specifics 2026-07-18 12:22:51 +02:00
a8af6427eb Merge pull request 'master' (#18) from master into f0ckv2
Reviewed-on: #18
2026-07-17 23:34:12 +00:00
b26b7518cb Merge pull request 'pipi kaka' (#17) from master into f0ckv2
Reviewed-on: #17
2026-07-17 22:33:32 +00:00
ce03f9d17f Merge pull request 'master' (#16) from master into f0ckv2
Reviewed-on: #16
2026-07-17 22:23:15 +00:00
87905dd547 Merge pull request 'master' (#15) from master into f0ckv2
Reviewed-on: #15
2026-07-17 21:53:32 +00:00
18a871976c ffff 2026-07-17 22:59:53 +02:00
d09cb44fa7 f0ckv2 things 2026-07-17 22:06:56 +02:00
9 changed files with 48 additions and 12 deletions

View File

@@ -7178,7 +7178,7 @@ span#favs {
grid-column: 1/4; grid-column: 1/4;
padding: 5px; padding: 5px;
background: var(--badge-bg); background: var(--badge-bg);
border-top: 1px solid var(--nav-border-color); border-bottom: 1px solid var(--nav-border-color);
} }
span#favs[hidden] { span#favs[hidden] {
@@ -8351,7 +8351,7 @@ input#s_avatar {
.nav-user-menu a { .nav-user-menu a {
display: block; display: block;
padding: 10px 15px; padding: 10px 10px;
color: var(--white); color: var(--white);
text-decoration: none; text-decoration: none;
transition: background 0.2s; transition: background 0.2s;
@@ -8722,6 +8722,8 @@ video.autoplay-gif {
text-align: center; text-align: center;
background: var(--bg); background: var(--bg);
border-bottom: 1px solid var(--nav-link-hover-bg); border-bottom: 1px solid var(--nav-link-hover-bg);
border-left: 1px solid var(--nav-border-color);
border-right: 1px solid var(--nav-border-color);
} }
/* Markdown Styles */ /* Markdown Styles */
@@ -9678,7 +9680,7 @@ html[theme="f0ck95d"] .badge-dark {
} }
.steuerung { .steuerung {
font-size: large; font-size: x-large;
font-family: monospace; font-family: monospace;
} }
@@ -14036,7 +14038,7 @@ textarea#profile_description {
align-items: flex-start; align-items: flex-start;
width: 100%; width: 100%;
max-width: 100%; max-width: 100%;
min-height: 120px; min-height: 90px;
box-sizing: border-box; box-sizing: border-box;
margin-bottom: 10px; margin-bottom: 10px;
} }

View File

@@ -120,6 +120,8 @@ class CommentSystem {
if (!document.body.classList.contains('layout-legacy') && !document.body.classList.contains('legacy-view')) return; if (!document.body.classList.contains('layout-legacy') && !document.body.classList.contains('legacy-view')) return;
const updateBtn = () => { const updateBtn = () => {
if (!this.initialLoadDone) return;
const btn = document.querySelector('.scroll-to-bottom'); const btn = document.querySelector('.scroll-to-bottom');
if (!btn) return; if (!btn) return;
@@ -128,7 +130,9 @@ class CommentSystem {
const rect = container.getBoundingClientRect(); const rect = container.getBoundingClientRect();
// Detect if we are at the bottom of the comments section // Detect if we are at the bottom of the comments section
const isAtBottom = rect.bottom < window.innerHeight + 100; // We also require window.scrollY > 50 to prevent flickering to 'scroll to top'
// when the page first loads and images haven't fully pushed the container down yet.
const isAtBottom = (rect.bottom < window.innerHeight + 100) && (window.scrollY > 50);
if (isAtBottom) { if (isAtBottom) {
btn.classList.add('is-at-bottom'); btn.classList.add('is-at-bottom');
@@ -673,6 +677,7 @@ class CommentSystem {
this.render(comments, this.user, initialIsSubscribed); this.render(comments, this.user, initialIsSubscribed);
this.initialLoadDone = true; this.initialLoadDone = true;
this.restoreState(state); this.restoreState(state);
if (this.scrollHandler) this.scrollHandler();
this._loadDanmaku(comments); this._loadDanmaku(comments);
if (scrollToId) { if (scrollToId) {
@@ -743,6 +748,7 @@ class CommentSystem {
this.reconcile(data.comments, data.user_id, data.is_subscribed); this.reconcile(data.comments, data.user_id, data.is_subscribed);
this.initialLoadDone = true; this.initialLoadDone = true;
this.restoreState(state); this.restoreState(state);
if (this.scrollHandler) this.scrollHandler();
if (scrollToId) { if (scrollToId) {
this.preservingScroll = false; this.preservingScroll = false;
@@ -772,6 +778,7 @@ class CommentSystem {
this.render(data.comments, data.user_id, data.is_subscribed); this.render(data.comments, data.user_id, data.is_subscribed);
this.initialLoadDone = true; this.initialLoadDone = true;
this.restoreState(state); this.restoreState(state);
if (this.scrollHandler) this.scrollHandler();
this._loadDanmaku(data.comments); this._loadDanmaku(data.comments);
if (scrollToId) { if (scrollToId) {

View File

@@ -252,6 +252,7 @@ export default {
${hallFilter} ${hallFilter}
${userHallFilter} ${userHallFilter}
${!session && getGlobalfilter() ? db`and not exists (select 1 from tags_assign where item_id = items.id and (${db.unsafe(getGlobalfilter())}))` : db``} ${!session && getGlobalfilter() ? db`and not exists (select 1 from tags_assign where item_id = items.id and (${db.unsafe(getGlobalfilter())}))` : db``}
${!session && cfg.websrv.public_untagged === false ? db`and exists (select 1 from tags_assign where item_id = items.id)` : db``}
${excludedTags.length > 0 ? db`and not exists (select 1 from tags_assign where item_id = items.id and tag_id = any(${excludedTags}::int[]))` : db``} ${excludedTags.length > 0 ? db`and not exists (select 1 from tags_assign where item_id = items.id and tag_id = any(${excludedTags}::int[]))` : db``}
${newerThan ? db`and items.id > ${newerThan}` : db``} ${newerThan ? db`and items.id > ${newerThan}` : db``}
${xdFilter} ${xdFilter}
@@ -294,6 +295,7 @@ export default {
${hallFilter} ${hallFilter}
${userHallFilter} ${userHallFilter}
${!session && getGlobalfilter() ? db`and not exists (select 1 from tags_assign where item_id = items.id and (${db.unsafe(getGlobalfilter())}))` : db``} ${!session && getGlobalfilter() ? db`and not exists (select 1 from tags_assign where item_id = items.id and (${db.unsafe(getGlobalfilter())}))` : db``}
${!session && cfg.websrv.public_untagged === false ? db`and exists (select 1 from tags_assign where item_id = items.id)` : db``}
${excludedTags.length > 0 ? db`and not exists (select 1 from tags_assign where item_id = items.id and tag_id = any(${excludedTags}::int[]))` : db``} ${excludedTags.length > 0 ? db`and not exists (select 1 from tags_assign where item_id = items.id and tag_id = any(${excludedTags}::int[]))` : db``}
${newerThan ? db`and items.id > ${newerThan}` : db``} ${newerThan ? db`and items.id > ${newerThan}` : db``}
${xdFilter} ${xdFilter}
@@ -516,6 +518,7 @@ export default {
${!fav && user ? db`and items.username ilike ${user}` : db``} ${!fav && user ? db`and items.username ilike ${user}` : db``}
${mimeSQL} ${mimeSQL}
${!session && getGlobalfilter() ? db`and not exists (select 1 from tags_assign where item_id = items.id and (${db.unsafe(getGlobalfilter())}))` : db``} ${!session && getGlobalfilter() ? db`and not exists (select 1 from tags_assign where item_id = items.id and (${db.unsafe(getGlobalfilter())}))` : db``}
${!session && cfg.websrv.public_untagged === false ? db`and exists (select 1 from tags_assign where item_id = items.id)` : db``}
${excludedTags.length > 0 ? db`and not exists (select 1 from tags_assign where item_id = items.id and tag_id = any(${excludedTags}::int[]))` : db``} ${excludedTags.length > 0 ? db`and not exists (select 1 from tags_assign where item_id = items.id and tag_id = any(${excludedTags}::int[]))` : db``}
`; `;
}; };
@@ -552,6 +555,7 @@ export default {
items.id = ${itemid} and items.id = ${itemid} and
items.active = true items.active = true
${!session && getGlobalfilter() ? db`and not exists (select 1 from tags_assign where item_id = items.id and (${db.unsafe(getGlobalfilter())}))` : db``} ${!session && getGlobalfilter() ? db`and not exists (select 1 from tags_assign where item_id = items.id and (${db.unsafe(getGlobalfilter())}))` : db``}
${!session && cfg.websrv.public_untagged === false ? db`and exists (select 1 from tags_assign where item_id = items.id)` : db``}
limit 1 limit 1
`; `;
@@ -569,7 +573,7 @@ export default {
if (!actitem) { if (!actitem) {
// Item not found or filtered out - check if it exists but was filtered (for OG meta tags) // Item not found or filtered out - check if it exists but was filtered (for OG meta tags)
if (!session && getGlobalfilter()) { if (!session && (getGlobalfilter() || cfg.websrv.public_untagged === false)) {
const unfilteredItem = await db` const unfilteredItem = await db`
select id from items where id = ${itemid} and active = true limit 1 select id from items where id = ${itemid} and active = true limit 1
`; `;
@@ -925,6 +929,7 @@ export default {
AND items.title IS NOT NULL AND items.title IS NOT NULL
${mimeSQL} ${mimeSQL}
${!session && getGlobalfilter() ? db`and not exists (select 1 from tags_assign where item_id = items.id and (${db.unsafe(getGlobalfilter())}))` : db``} ${!session && getGlobalfilter() ? db`and not exists (select 1 from tags_assign where item_id = items.id and (${db.unsafe(getGlobalfilter())}))` : db``}
${!session && cfg.websrv.public_untagged === false ? db`and exists (select 1 from tags_assign where item_id = items.id)` : db``}
${excludedTags.length > 0 ? db`and not exists (select 1 from tags_assign where item_id = items.id and tag_id = any(${excludedTags}::int[]))` : db``} ${excludedTags.length > 0 ? db`and not exists (select 1 from tags_assign where item_id = items.id and tag_id = any(${excludedTags}::int[]))` : db``}
ORDER BY random() ORDER BY random()
LIMIT 1 LIMIT 1
@@ -945,6 +950,7 @@ export default {
and items.active = true and items.active = true
${mimeSQL} ${mimeSQL}
${!session && getGlobalfilter() ? db`and not exists (select 1 from tags_assign where item_id = items.id and (${db.unsafe(getGlobalfilter())}))` : db``} ${!session && getGlobalfilter() ? db`and not exists (select 1 from tags_assign where item_id = items.id and (${db.unsafe(getGlobalfilter())}))` : db``}
${!session && cfg.websrv.public_untagged === false ? db`and exists (select 1 from tags_assign where item_id = items.id)` : db``}
group by items.id group by items.id
order by random() order by random()
limit 1 limit 1
@@ -987,6 +993,7 @@ export default {
${hall ? db`and items.id in (select item_id from halls_assign ha join halls h on h.id = ha.hall_id where h.slug = ${hall})` : db``} ${hall ? db`and items.id in (select item_id from halls_assign ha join halls h on h.id = ha.hall_id where h.slug = ${hall})` : db``}
${mimeSQL} ${mimeSQL}
${!session && getGlobalfilter() ? db`and not exists (select 1 from tags_assign where item_id = items.id and (${db.unsafe(getGlobalfilter())}))` : db``} ${!session && getGlobalfilter() ? db`and not exists (select 1 from tags_assign where item_id = items.id and (${db.unsafe(getGlobalfilter())}))` : db``}
${!session && cfg.websrv.public_untagged === false ? db`and exists (select 1 from tags_assign where item_id = items.id)` : db``}
${excludedTags.length > 0 ? db`and not exists (select 1 from tags_assign where item_id = items.id and tag_id = any(${excludedTags}::int[]))` : db``} ${excludedTags.length > 0 ? db`and not exists (select 1 from tags_assign where item_id = items.id and tag_id = any(${excludedTags}::int[]))` : db``}
group by items.id, tags.tag group by items.id, tags.tag
order by random() order by random()
@@ -1006,6 +1013,7 @@ export default {
and items.active = true and items.active = true
${mimeSQL} ${mimeSQL}
${!session && getGlobalfilter() ? db`and not exists (select 1 from tags_assign where item_id = items.id and (${db.unsafe(getGlobalfilter())}))` : db``} ${!session && getGlobalfilter() ? db`and not exists (select 1 from tags_assign where item_id = items.id and (${db.unsafe(getGlobalfilter())}))` : db``}
${!session && cfg.websrv.public_untagged === false ? db`and exists (select 1 from tags_assign where item_id = items.id)` : db``}
${excludedTags.length > 0 ? db`and not exists (select 1 from tags_assign where item_id = items.id and tag_id = any(${excludedTags}::int[]))` : db``} ${excludedTags.length > 0 ? db`and not exists (select 1 from tags_assign where item_id = items.id and tag_id = any(${excludedTags}::int[]))` : db``}
order by random() order by random()
limit 1 limit 1
@@ -1021,6 +1029,8 @@ export default {
and uha.hall_id = ${userHallId} and uha.hall_id = ${userHallId}
and items.active = true and items.active = true
${mimeSQL} ${mimeSQL}
${!session && getGlobalfilter() ? db`and not exists (select 1 from tags_assign where item_id = items.id and (${db.unsafe(getGlobalfilter())}))` : db``}
${!session && cfg.websrv.public_untagged === false ? db`and exists (select 1 from tags_assign where item_id = items.id)` : db``}
${excludedTags.length > 0 ? db`and not exists (select 1 from tags_assign where item_id = items.id and tag_id = any(${excludedTags}::int[]))` : db``} ${excludedTags.length > 0 ? db`and not exists (select 1 from tags_assign where item_id = items.id and tag_id = any(${excludedTags}::int[]))` : db``}
order by random() order by random()
limit 1 limit 1
@@ -1048,6 +1058,7 @@ export default {
WHERE items.active = true WHERE items.active = true
${mimeSQL} ${mimeSQL}
${checkFilter ? db`AND filter_ta.tag_id IS NULL` : db``} ${checkFilter ? db`AND filter_ta.tag_id IS NULL` : db``}
${!session && cfg.websrv.public_untagged === false ? db`AND EXISTS (SELECT 1 FROM tags_assign WHERE item_id = items.id)` : db``}
${excludedTags.length > 0 ? db`AND NOT EXISTS (SELECT 1 FROM tags_assign WHERE item_id = items.id AND tag_id = ANY(${excludedTags}::int[]))` : db``} ${excludedTags.length > 0 ? db`AND NOT EXISTS (SELECT 1 FROM tags_assign WHERE item_id = items.id AND tag_id = ANY(${excludedTags}::int[]))` : db``}
${!useTagIdOpt ? db`AND ${db.unsafe(globalModeQuery)}` : db``} ${!useTagIdOpt ? db`AND ${db.unsafe(globalModeQuery)}` : db``}
ORDER BY random() ORDER BY random()

View File

@@ -972,6 +972,15 @@ export default (router, tpl) => {
// Recent Activity Page // Recent Activity Page
router.get(/\/activity\/?/, async (req, res) => { router.get(/\/activity\/?/, async (req, res) => {
if (!req.session && cfg.main.hide_comments_from_public) {
if (req.url.qs?.json === 'true' || req.headers['x-requested-with'] === 'XMLHttpRequest') {
return res.reply({
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ success: true, comments: [], page: 1, hasMore: false, require_login: true })
});
}
return res.reply({ code: 403, body: "Forbidden" });
}
try { try {
const page = +(req.url.qs?.page || 1); const page = +(req.url.qs?.page || 1);
const limit = Math.min(+(req.url.qs?.limit || 50), 50); const limit = Math.min(+(req.url.qs?.limit || 50), 50);

View File

@@ -142,8 +142,9 @@ db.listen('activity', async (payload) => {
data.username = 'System'; data.username = 'System';
} }
// Broadcast to ALL connected clients // Broadcast to ALL connected clients (except guests if comments are hidden)
for (const client of clients) { for (const client of clients) {
if (!client.userId && cfg.main.hide_comments_from_public) continue;
client.send({ type: 'activity', data }); client.send({ type: 'activity', data });
} }
} catch (e) { } catch (e) {

View File

@@ -1276,6 +1276,8 @@ process.on('uncaughtException', err => {
enable_danmaku: cfg.websrv.enable_danmaku !== false, enable_danmaku: cfg.websrv.enable_danmaku !== false,
enable_item_title: cfg.websrv.enable_item_title !== false, enable_item_title: cfg.websrv.enable_item_title !== false,
enable_global_chat: !!cfg.websrv.enable_global_chat, enable_global_chat: !!cfg.websrv.enable_global_chat,
enable_sidebar: cfg.websrv.enable_sidebar !== false,
sidebar_closed_default: cfg.websrv.sidebar_closed_default === true,
embed_youtube_in_comments: cfg.websrv.embed_youtube_in_comments !== false, embed_youtube_in_comments: cfg.websrv.embed_youtube_in_comments !== false,
get koepfe_json() { get koepfe_json() {
try { try {
@@ -1357,11 +1359,11 @@ process.on('uncaughtException', err => {
globals.lang = perRequestLang; globals.lang = perRequestLang;
// Resolve per-request infobox preference // Resolve per-request infobox preference
// Guests always get false — the alternative infobox is a logged-in user preference only // Guests check the new config flag, whereas users use their session pref or default to the user flag
const useAltInfobox = (req && req.session && typeof req.session.use_alternative_infobox === 'boolean') const useAltInfobox = (req && req.session && typeof req.session.use_alternative_infobox === 'boolean')
? req.session.use_alternative_infobox ? req.session.use_alternative_infobox
: (req && !req.session : (req && !req.session
? false ? (cfg.websrv.guest_alternative_infobox === true)
: (data && typeof data.user_alternative_infobox === 'boolean' : (data && typeof data.user_alternative_infobox === 'boolean'
? data.user_alternative_infobox ? data.user_alternative_infobox
: (cfg.websrv.user_alternative_infobox !== false))); : (cfg.websrv.user_alternative_infobox !== false)));

View File

@@ -210,6 +210,7 @@
</div> </div>
</div> </div>
@if(session || !hide_comments_from_public)
<div id="comments-container" <div id="comments-container"
data-item-id="{{ item.id }}" data-item-id="{{ item.id }}"
@if(session) data-user="{{ session.user }}" @endif @if(session) data-user="{{ session.user }}" @endif
@@ -221,6 +222,7 @@
</div> </div>
@endif @endif
</div> </div>
@endif
<button class="mobile-scroll-to-top" title="Back to top" aria-label="Scroll to top"><i class="fa-solid fa-chevron-up"></i></button> <button class="mobile-scroll-to-top" title="Back to top" aria-label="Scroll to top"><i class="fa-solid fa-chevron-up"></i></button>
<script id="initial-subscription" type="application/json">{{ isSubscribed }}</script> <script id="initial-subscription" type="application/json">{{ isSubscribed }}</script>

View File

@@ -102,7 +102,7 @@
@if(session) @if(session)
@include(snippets/metadata-modal) @include(snippets/metadata-modal)
@endif @endif
@if(!private_society || session) @if(!private_society && enable_sidebar || session && enable_sidebar)
<div class="global-sidebar-right"> <div class="global-sidebar-right">
<div class="sidebar-activity"> <div class="sidebar-activity">
<div id="sidebar-activity-container" class="sidebar-comments-list"> <div id="sidebar-activity-container" class="sidebar-comments-list">
@@ -591,7 +591,9 @@
}; };
</script> </script>
<script src="/s/js/f0ckm.js?v={{ ts }}"></script> <script src="/s/js/f0ckm.js?v={{ ts }}"></script>
@if(enable_sidebar)
<script src="/s/js/sidebar-activity.js?v={{ ts }}"></script> <script src="/s/js/sidebar-activity.js?v={{ ts }}"></script>
@endif
<script src="/s/js/flash_yank.js?v={{ ts }}"></script> <script src="/s/js/flash_yank.js?v={{ ts }}"></script>
@if(show_koepfe && !(session && session.hide_koepfe)) @if(show_koepfe && !(session && session.hide_koepfe))
<script>window.f0ckKoepfe = {{ koepfe_json }};</script> <script>window.f0ckKoepfe = {{ koepfe_json }};</script>

View File

@@ -77,8 +77,8 @@
@endif @endif
</head> </head>
<body class="@if(session)@if(session.use_new_layout)layout-modern@else layout-legacy @endif@else @if(default_layout === 'legacy')layout-legacy@else layout-modern@endif @endif @if(private_society && !session)private-gate-active@endif"> <body class="@if(session)@if(session.use_new_layout)layout-modern@else layout-legacy @endif@else @if(default_layout === 'legacy')layout-legacy@else layout-modern@endif @endif @if(private_society && !session)private-gate-active@endif @if(!enable_sidebar)sidebar-right-hidden@endif">
<script>if(localStorage.getItem('sidebarRightHidden')==='true')document.body.classList.add('sidebar-right-hidden');if(localStorage.getItem('comments_hidden')==='true')document.body.classList.add('sidebar-left-hidden');(function(){var d=window.devicePixelRatio||1;console.log('[DPR] devicePixelRatio='+d);var t='dpr-1x';if(d>=1.55)t='dpr-high';else if(d>=1.39)t='dpr-150';else if(d>=1.26)t='dpr-130';else if(d>=1.20)t='dpr-120';else if(d>=1.05)t='dpr-110';document.documentElement.setAttribute('data-dpr',t);})();</script> <script>if(localStorage.getItem('sidebarRightHidden')==='true' || (localStorage.getItem('sidebarRightHidden')===null && {{ sidebar_closed_default ? 'true' : 'false' }}))document.body.classList.add('sidebar-right-hidden');if(localStorage.getItem('comments_hidden')==='true')document.body.classList.add('sidebar-left-hidden');(function(){var d=window.devicePixelRatio||1;console.log('[DPR] devicePixelRatio='+d);var t='dpr-1x';if(d>=1.55)t='dpr-high';else if(d>=1.39)t='dpr-150';else if(d>=1.26)t='dpr-130';else if(d>=1.20)t='dpr-120';else if(d>=1.05)t='dpr-110';document.documentElement.setAttribute('data-dpr',t);})();</script>
@if(!private_society || session) @if(!private_society || session)
<canvas class="hidden-xs" id="bg"></canvas> <canvas class="hidden-xs" id="bg"></canvas>
@endif @endif