This commit is contained in:
2026-08-19 21:51:41 +02:00
parent 63ea313742
commit 0b2ece9447
9 changed files with 153 additions and 14 deletions

View File

@@ -1043,7 +1043,7 @@ html[theme='95'] .comment,
html[theme='95'] .login-form, html[theme='95'] .login-form,
html[theme='95'] .user-infobox-block { html[theme='95'] .user-infobox-block {
border: 2px inset #dfdfdf !important; border: 2px inset #dfdfdf !important;
background: #c0c0c0 !important; background-color: #c0c0c0 !important;
color: #000 !important; color: #000 !important;
padding: 10px !important; padding: 10px !important;
border-radius: 0 !important; border-radius: 0 !important;
@@ -3100,16 +3100,38 @@ body.layout-legacy .scroll-to-bottom svg {
display: flex; display: flex;
gap: 15px; gap: 15px;
padding: 10px; padding: 10px;
background: var(--comment-bg); margin: 5px;
margin-left: 5px;
background-color: var(--comment-bg);
/* Very light seethrough */ /* Very light seethrough */
border: 1px solid var(--nav-border-color); border: 1px solid var(--nav-border-color);
border-radius: 0; border-radius: 0;
/* No rounded corners */ /* No rounded corners */
position: relative; position: relative;
isolation: isolate;
padding-bottom: 25px; padding-bottom: 25px;
/* Room for absolute permalink */ /* Room for absolute permalink */
} }
.comment > * {
position: relative;
z-index: 1;
}
.comment::before {
content: "";
position: absolute;
inset: 0;
background-image: var(--author-banner, none);
background-position: var(--author-banner-position, center top);
background-size: var(--author-banner-size, cover);
background-repeat: var(--author-banner-repeat, repeat);
opacity: var(--author-banner-opacity, 0.25);
z-index: 0;
border-radius: inherit;
pointer-events: none;
}
.comment.deleted { .comment.deleted {
opacity: 0.5; opacity: 0.5;
} }
@@ -9655,7 +9677,7 @@ body.layout-legacy .comment-content img.emoji {
.comment-highlighted { .comment-highlighted {
box-shadow: inset 3px 0px 0px var(--accent) !important; box-shadow: inset 3px 0px 0px var(--accent) !important;
background: var(--bg) !important; background-color: var(--bg) !important;
} }
.anchor-target { .anchor-target {

View File

@@ -468,9 +468,15 @@ class CommentSystem {
// 2. Check for duplicates (if we just posted it ourselves via optimistic insert). // 2. Check for duplicates (if we just posted it ourselves via optimistic insert).
// Even on early return, ensure the button is present if body was truncated. // Even on early return, ensure the button is present if body was truncated.
if (document.getElementById('c' + data.id)) { if (document.getElementById('c' + data.id)) {
const el = document.getElementById('c' + data.id);
if (el && data.banner_file && data.banner_file !== 'null') {
el.style.setProperty('--author-banner', `url('/a/${data.banner_file}')`);
el.style.setProperty('--author-banner-position', data.banner_position === 'center' ? 'center top' : (data.banner_position || 'center top'));
el.style.setProperty('--author-banner-size', data.banner_size || 'cover');
el.style.setProperty('--author-banner-repeat', data.banner_repeat || 'repeat');
}
if (data.body && data.body.endsWith('\u2026')) { if (data.body && data.body.endsWith('\u2026')) {
console.log('[handleLiveComment] duplicate+truncated, ensuring button for', data.id); console.log('[handleLiveComment] duplicate+truncated, ensuring button for', data.id);
const el = document.getElementById('c' + data.id);
const contentEl = el?.querySelector('.comment-content'); const contentEl = el?.querySelector('.comment-content');
if (contentEl && !contentEl.querySelector('.load-full-comment-btn')) { if (contentEl && !contentEl.querySelector('.load-full-comment-btn')) {
const btnLabel = (window.f0ckI18n?.sidebar_show_full_comment) || 'show full comment'; const btnLabel = (window.f0ckI18n?.sidebar_show_full_comment) || 'show full comment';
@@ -507,6 +513,10 @@ class CommentSystem {
avatar: data.avatar, avatar: data.avatar,
avatar_file: data.avatar_file, avatar_file: data.avatar_file,
username_color: data.username_color, username_color: data.username_color,
banner_file: data.banner_file || null,
banner_position: data.banner_position || null,
banner_size: data.banner_size || null,
banner_repeat: data.banner_repeat || null,
video_time: data.video_time ?? null, video_time: data.video_time ?? null,
is_new: true, is_new: true,
is_deleted: false, is_deleted: false,
@@ -593,7 +603,20 @@ class CommentSystem {
// Update in-memory data so future reconciles use the full content // Update in-memory data so future reconciles use the full content
if (this.lastData) { if (this.lastData) {
const cached = this.lastData.find(c => String(c.id) === String(commentId)); const cached = this.lastData.find(c => String(c.id) === String(commentId));
if (cached) cached.content = fullContent; if (cached) {
cached.content = fullContent;
if (json.comment.banner_file) cached.banner_file = json.comment.banner_file;
if (json.comment.banner_position) cached.banner_position = json.comment.banner_position;
if (json.comment.banner_size) cached.banner_size = json.comment.banner_size;
if (json.comment.banner_repeat) cached.banner_repeat = json.comment.banner_repeat;
}
}
if (json.comment && json.comment.banner_file) {
el.style.setProperty('--author-banner', `url('/a/${json.comment.banner_file}')`);
el.style.setProperty('--author-banner-position', json.comment.banner_position === 'center' ? 'center top' : (json.comment.banner_position || 'center top'));
el.style.setProperty('--author-banner-size', json.comment.banner_size || 'cover');
el.style.setProperty('--author-banner-repeat', json.comment.banner_repeat || 'repeat');
} }
contentEl.dataset.raw = fullContent; contentEl.dataset.raw = fullContent;
@@ -1502,6 +1525,13 @@ class CommentSystem {
el.classList.toggle('pinned', !!incoming.is_pinned); el.classList.toggle('pinned', !!incoming.is_pinned);
const pinIcon = el.querySelector('.pin-icon'); const pinIcon = el.querySelector('.pin-icon');
if (pinIcon) pinIcon.style.display = incoming.is_pinned ? 'block' : 'none'; if (pinIcon) pinIcon.style.display = incoming.is_pinned ? 'block' : 'none';
if (incoming && incoming.banner_file && incoming.banner_file !== 'null') {
el.style.setProperty('--author-banner', `url('/a/${incoming.banner_file}')`);
el.style.setProperty('--author-banner-position', incoming.banner_position === 'center' ? 'center top' : (incoming.banner_position || 'center top'));
el.style.setProperty('--author-banner-size', incoming.banner_size || 'cover');
el.style.setProperty('--author-banner-repeat', incoming.banner_repeat || 'repeat');
}
} }
}); });
@@ -2170,7 +2200,11 @@ class CommentSystem {
} }
} }
return `<div class="${commentClass} ${isDeleted ? 'deleted' : ''} ${isPinned ? 'pinned' : ''}" id="c${comment.id}"><div class="comment-avatar">${comment.username ? `<a href="/user/${comment.username}">` : ''}<img src="${comment.avatar_file ? `/a/${comment.avatar_file}` : (comment.avatar ? `/t/${comment.avatar}.webp` : '/a/default.png')}">${comment.username ? `</a>` : ''}</div><div class="comment-body"><div class="comment-header"><div class="comment-header-left">${pinnedBadge}${comment.username ? `<a href="/user/${comment.username}" class="comment-author" tooltip="ID: ${comment.user_id}" ${comment.username_color ? `style="color: ${comment.username_color}"` : ''}>${this.escapeHtml(comment.display_name || comment.username)}</a>` : '<span class="comment-author">System</span>'}${contextMarker}${backlinkHtml}</div><a href="#c${comment.id}" class="comment-time timeago" tooltip="${fullDate}" data-iso="${isoDate}" data-id="${comment.id}" data-username="${comment.username}" data-display="${this.escapeHtml(comment.display_name || '')}">${timeAgo}</a></div><div class="comment-content" data-raw="${this.escapeHtml(comment.content)}">${content}</div>${this.renderCommentAttachments(comment.files, comment.content)}${this.renderCommentPoll(comment.poll, comment.id, comment.username)}<div class="comment-footer"><div class="comment-footer-right"><div class="comment-actions">${!isDeleted && currentUserId ? `<button class="reply-btn" data-id="${comment.id}" data-username="${comment.username}" data-display="${this.escapeHtml(comment.display_name || '')}" title="Reply"><i class="fa-solid fa-reply"></i></button><button class="quote-btn" data-id="${comment.id}" data-username="${comment.username}" data-display="${this.escapeHtml(comment.display_name || '')}" title="Quote with Text"><i class="fa-solid fa-quote-left"></i></button><button class="report-comment-btn" data-id="${comment.id}" title="Report Comment" style="background:none;border:none;color:inherit;cursor:pointer;opacity:0.75;padding:0;"><i class="fa-solid fa-triangle-exclamation"></i></button>` : ''}${adminButtons}${userDeleteButton}</div></div></div></div><a href="#c${comment.id}" class="comment-permalink" title="Permalink" data-id="${comment.id}" data-username="${comment.username}" data-display="${this.escapeHtml(comment.display_name || '')}">#${comment.id}</a></div>${repliesHtml}`; const bannerStyle = (comment.banner_file && comment.banner_file !== 'null')
? `style="--author-banner: url('/a/${comment.banner_file}'); --author-banner-position: ${comment.banner_position === 'center' ? 'center top' : (comment.banner_position || 'center top')}; --author-banner-size: ${comment.banner_size || 'cover'}; --author-banner-repeat: ${comment.banner_repeat || 'repeat'};"`
: '';
return `<div class="${commentClass} ${isDeleted ? 'deleted' : ''} ${isPinned ? 'pinned' : ''}" id="c${comment.id}" ${bannerStyle}><div class="comment-avatar">${comment.username ? `<a href="/user/${comment.username}">` : ''}<img src="${comment.avatar_file ? `/a/${comment.avatar_file}` : (comment.avatar ? `/t/${comment.avatar}.webp` : '/a/default.png')}">${comment.username ? `</a>` : ''}</div><div class="comment-body"><div class="comment-header"><div class="comment-header-left">${pinnedBadge}${comment.username ? `<a href="/user/${comment.username}" class="comment-author" tooltip="ID: ${comment.user_id}" ${comment.username_color ? `style="color: ${comment.username_color}"` : ''}>${this.escapeHtml(comment.display_name || comment.username)}</a>` : '<span class="comment-author">System</span>'}${contextMarker}${backlinkHtml}</div><a href="#c${comment.id}" class="comment-time timeago" tooltip="${fullDate}" data-iso="${isoDate}" data-id="${comment.id}" data-username="${comment.username}" data-display="${this.escapeHtml(comment.display_name || '')}">${timeAgo}</a></div><div class="comment-content" data-raw="${this.escapeHtml(comment.content)}">${content}</div>${this.renderCommentAttachments(comment.files, comment.content)}${this.renderCommentPoll(comment.poll, comment.id, comment.username)}<div class="comment-footer"><div class="comment-footer-right"><div class="comment-actions">${!isDeleted && currentUserId ? `<button class="reply-btn" data-id="${comment.id}" data-username="${comment.username}" data-display="${this.escapeHtml(comment.display_name || '')}" title="Reply"><i class="fa-solid fa-reply"></i></button><button class="quote-btn" data-id="${comment.id}" data-username="${comment.username}" data-display="${this.escapeHtml(comment.display_name || '')}" title="Quote with Text"><i class="fa-solid fa-quote-left"></i></button><button class="report-comment-btn" data-id="${comment.id}" title="Report Comment" style="background:none;border:none;color:inherit;cursor:pointer;opacity:0.75;padding:0;"><i class="fa-solid fa-triangle-exclamation"></i></button>` : ''}${adminButtons}${userDeleteButton}</div></div></div></div><a href="#c${comment.id}" class="comment-permalink" title="Permalink" data-id="${comment.id}" data-username="${comment.username}" data-display="${this.escapeHtml(comment.display_name || '')}">#${comment.id}</a></div>${repliesHtml}`;
} }
timeAgo(date) { timeAgo(date) {
@@ -3494,6 +3528,50 @@ class CommentSystem {
} }
} }
let resolvedBannerFile = (json.comment?.banner_file && json.comment.banner_file !== 'null') ? json.comment.banner_file : ((session.banner_file && session.banner_file !== 'null') ? session.banner_file : null);
let resolvedBannerPosition = (json.comment?.banner_position && json.comment.banner_position !== 'null') ? json.comment.banner_position : ((session.banner_position && session.banner_position !== 'null') ? session.banner_position : null);
let resolvedBannerSize = (json.comment?.banner_size && json.comment.banner_size !== 'null') ? json.comment.banner_size : ((session.banner_size && session.banner_size !== 'null') ? session.banner_size : null);
let resolvedBannerRepeat = (json.comment?.banner_repeat && json.comment.banner_repeat !== 'null') ? json.comment.banner_repeat : ((session.banner_repeat && session.banner_repeat !== 'null') ? session.banner_repeat : null);
if (!resolvedBannerFile && this.lastData && currentUsername) {
const existingByMe = this.lastData.find(c =>
c.username && c.username.toLowerCase() === currentUsername.toLowerCase() && c.banner_file && c.banner_file !== 'null'
);
if (existingByMe) {
resolvedBannerFile = existingByMe.banner_file || null;
resolvedBannerPosition = existingByMe.banner_position || null;
resolvedBannerSize = existingByMe.banner_size || null;
resolvedBannerRepeat = existingByMe.banner_repeat || null;
}
}
if (!resolvedBannerFile && currentUsername) {
const existingCommentEl = document.querySelector(`.comment-author[href="/user/${currentUsername}"], .user-infobox-block[style*="--author-banner"]`);
if (existingCommentEl) {
const commentDiv = existingCommentEl.closest('.comment') || existingCommentEl.closest('.user-infobox-block');
if (commentDiv) {
const style = commentDiv.getAttribute('style') || '';
const bannerMatch = style.match(/--author-banner:\s*url\(['"]?\/a\/([^'"]+)['"]?\)/);
if (bannerMatch) {
resolvedBannerFile = bannerMatch[1];
const posMatch = style.match(/--author-banner-position:\s*([^;]+)/);
if (posMatch) resolvedBannerPosition = posMatch[1].trim();
const sizeMatch = style.match(/--author-banner-size:\s*([^;]+)/);
if (sizeMatch) resolvedBannerSize = sizeMatch[1].trim();
const repeatMatch = style.match(/--author-banner-repeat:\s*([^;]+)/);
if (repeatMatch) resolvedBannerRepeat = repeatMatch[1].trim();
}
}
}
}
if (resolvedBannerFile && window.f0ckSession) {
window.f0ckSession.banner_file = resolvedBannerFile;
if (resolvedBannerPosition) window.f0ckSession.banner_position = resolvedBannerPosition;
if (resolvedBannerSize) window.f0ckSession.banner_size = resolvedBannerSize;
if (resolvedBannerRepeat) window.f0ckSession.banner_repeat = resolvedBannerRepeat;
}
const newComment = { const newComment = {
id: json.comment.id, id: json.comment.id,
item_id: this.itemId, item_id: this.itemId,
@@ -3507,6 +3585,10 @@ class CommentSystem {
avatar: resolvedAvatar, avatar: resolvedAvatar,
avatar_file: resolvedAvatarFile, avatar_file: resolvedAvatarFile,
username_color: session.username_color || null, username_color: session.username_color || null,
banner_file: resolvedBannerFile,
banner_position: resolvedBannerPosition,
banner_size: resolvedBannerSize,
banner_repeat: resolvedBannerRepeat,
is_deleted: false, is_deleted: false,
is_pinned: false, is_pinned: false,
video_time: json.comment.video_time ?? null, video_time: json.comment.video_time ?? null,
@@ -3533,7 +3615,7 @@ class CommentSystem {
if (!this.lastData) this.lastData = []; if (!this.lastData) this.lastData = [];
const existingInLastData = this.lastData.find(c => c.id === newComment.id); const existingInLastData = this.lastData.find(c => c.id === newComment.id);
if (existingInLastData) { if (existingInLastData) {
existingInLastData.files = files; Object.assign(existingInLastData, newComment);
} else { } else {
if (this.sort === 'new') this.lastData.unshift(newComment); if (this.sort === 'new') this.lastData.unshift(newComment);
else this.lastData.push(newComment); else this.lastData.push(newComment);

View File

@@ -7436,6 +7436,9 @@ class NotificationSystem {
} else if (data.type === 'comments') { } else if (data.type === 'comments') {
window.f0ckDebug(`[SSE] Comment update received:`, data.data); window.f0ckDebug(`[SSE] Comment update received:`, data.data);
if (data.data.type === 'comment') { if (data.data.type === 'comment') {
if (window.commentSystem && typeof window.commentSystem.handleLiveComment === 'function') {
window.commentSystem.handleLiveComment(data.data);
}
// New comment posted — update xD badge from server-authoritative score // New comment posted — update xD badge from server-authoritative score
if (typeof data.data.xd_score === 'number') { if (typeof data.data.xd_score === 'number') {
updateXdBadgeFromScore(data.data.item_id, data.data.xd_score); updateXdBadgeFromScore(data.data.item_id, data.data.xd_score);

View File

@@ -415,7 +415,11 @@ if (!window.UserCommentSystem) {
const fullDate = new Date(c.created_at).toISOString(); const fullDate = new Date(c.created_at).toISOString();
const content = this.renderCommentContent(c.content, itemKey); const content = this.renderCommentContent(c.content, itemKey);
return `<div class="comment" id="c${c.id}"><div class="comment-avatar"><a href="/${itemKey}"><img src="/t/${c.item_id}.webp" alt=""></a></div><div class="comment-body"><div class="comment-header"><div class="comment-header-left"><span class="comment-author" tooltip="ID: ${c.user_id}" ${this.userColor ? `style="color: ${this.userColor}"` : ''}>${this.username}</span></div><span class="comment-time timeago" title="${fullDate}">${timeAgo}</span></div><div class="comment-content" data-raw="${this.escapeHtml(c.content)}">${content}</div>${this.renderCommentAttachments(c.files, c.content)}${this.renderCommentPoll(c.poll, c.id)}<div class="comment-footer"><div class="comment-footer-right"><div class="comment-actions">${window.f0ckSession && window.f0ckSession.logged_in ? `<button class="report-comment-btn" data-id="${c.id}" title="Report Comment" style="background:none;border:none;color:inherit;cursor:pointer;opacity:0.75;padding:0;"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 512 512" fill="currentColor"><path d="M506.3 417l-213.3-364c-16.3-28-57.5-28-73.8 0l-213.2 364C-10.6 445.1 9.7 480 42.7 480h426.6C502.5 480 522.6 445.1 506.3 417zM256 384c-14.1 0-25.6-11.5-25.6-25.6 0-14.1 11.5-25.6 25.6-25.6 14.1 0 25.6 11.5 25.6 25.6C281.6 372.5 270.1 384 256 384zM281.6 264.4c0 14.1-11.5 25.6-25.6 25.6-14.1 0-25.6-11.5-25.6-25.6v-96c0-14.1 11.5-25.6 25.6-25.6 14.1 0 25.6 11.5 25.6 25.6V264.4z"/></svg></button>` : ''}</div></div></div></div><a href="/${itemKey}#c${c.id}" class="comment-permalink" title="Permalink">#${c.id}</a></div>`; const bannerStyle = (c.banner_file && c.banner_file !== 'null')
? `style="--author-banner: url('/a/${c.banner_file}'); --author-banner-position: ${c.banner_position === 'center' ? 'center top' : (c.banner_position || 'center top')}; --author-banner-size: ${c.banner_size || 'cover'}; --author-banner-repeat: ${c.banner_repeat || 'repeat'};"`
: '';
return `<div class="comment" id="c${c.id}" ${bannerStyle}><div class="comment-avatar"><a href="/${itemKey}"><img src="/t/${c.item_id}.webp" alt=""></a></div><div class="comment-body"><div class="comment-header"><div class="comment-header-left"><span class="comment-author" tooltip="ID: ${c.user_id}" ${this.userColor ? `style="color: ${this.userColor}"` : ''}>${this.username}</span></div><span class="comment-time timeago" title="${fullDate}">${timeAgo}</span></div><div class="comment-content" data-raw="${this.escapeHtml(c.content)}">${content}</div>${this.renderCommentAttachments(c.files, c.content)}${this.renderCommentPoll(c.poll, c.id)}<div class="comment-footer"><div class="comment-footer-right"><div class="comment-actions">${window.f0ckSession && window.f0ckSession.logged_in ? `<button class="report-comment-btn" data-id="${c.id}" title="Report Comment" style="background:none;border:none;color:inherit;cursor:pointer;opacity:0.75;padding:0;"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 512 512" fill="currentColor"><path d="M506.3 417l-213.3-364c-16.3-28-57.5-28-73.8 0l-213.2 364C-10.6 445.1 9.7 480 42.7 480h426.6C502.5 480 522.6 445.1 506.3 417zM256 384c-14.1 0-25.6-11.5-25.6-25.6 0-14.1 11.5-25.6 25.6-25.6 14.1 0 25.6 11.5 25.6 25.6C281.6 372.5 270.1 384 256 384zM281.6 264.4c0 14.1-11.5 25.6-25.6 25.6-14.1 0-25.6-11.5-25.6-25.6v-96c0-14.1 11.5-25.6 25.6-25.6 14.1 0 25.6 11.5 25.6 25.6V264.4z"/></svg></button>` : ''}</div></div></div></div><a href="/${itemKey}#c${c.id}" class="comment-permalink" title="Permalink">#${c.id}</a></div>`;
} }
startLiveTimestamps() { startLiveTimestamps() {

View File

@@ -190,15 +190,20 @@ export const handleBannerUpload = async (req, res) => {
update user_options update user_options
set banner_file = ${finalFilename}, set banner_file = ${finalFilename},
banner_position = ${parts.banner_position || 'center'}, banner_position = ${parts.banner_position || 'center'},
banner_size = ${parts.banner_size || 'cover'} banner_size = ${parts.banner_size || 'cover'},
banner_repeat = ${parts.banner_repeat || 'repeat'}
where user_id = ${+req.session.id} where user_id = ${+req.session.id}
`; `;
if (global._invalidateSessionCache && req.cookies && req.cookies.session) {
global._invalidateSessionCache(lib.sha256(req.cookies.session));
}
const payloadStr = JSON.stringify({ const payloadStr = JSON.stringify({
user_id: +req.session.id, user_id: +req.session.id,
user: req.session.user, user: req.session.user,
banner_file: finalFilename, banner_file: finalFilename,
banner_position: parts.banner_position || 'center', banner_position: parts.banner_position || 'center',
banner_size: parts.banner_size || 'cover' banner_size: parts.banner_size || 'cover',
banner_repeat: parts.banner_repeat || 'repeat'
}); });
await db`select pg_notify('profile_update', ${payloadStr})`; await db`select pg_notify('profile_update', ${payloadStr})`;
} catch (dbErr) { } catch (dbErr) {
@@ -273,6 +278,9 @@ export const handleBannerDelete = async (req, res) => {
set banner_file = null set banner_file = null
where user_id = ${+req.session.id} where user_id = ${+req.session.id}
`; `;
if (global._invalidateSessionCache && req.cookies && req.cookies.session) {
global._invalidateSessionCache(lib.sha256(req.cookies.session));
}
const payloadStr = JSON.stringify({ const payloadStr = JSON.stringify({
user_id: +req.session.id, user_id: +req.session.id,
user: req.session.user, user: req.session.user,

View File

@@ -1313,7 +1313,7 @@ export default {
c.id, c.parent_id, c.content, c.created_at, c.vote_score, c.is_deleted, c.id, c.parent_id, c.content, c.created_at, c.vote_score, c.is_deleted,
COALESCE(c.is_pinned, false) as is_pinned, COALESCE(c.is_pinned, false) as is_pinned,
c.video_time, c.video_time,
u.user as username, u.id as user_id, uo.avatar, uo.avatar_file, uo.username_color, uo.display_name, u.user as username, u.id as user_id, uo.avatar, uo.avatar_file, uo.username_color, uo.display_name, uo.banner_file, uo.banner_position, uo.banner_size, uo.banner_repeat,
(SELECT count(*) FROM comments r WHERE r.parent_id = c.id) as reply_count (SELECT count(*) FROM comments r WHERE r.parent_id = c.id) as reply_count
FROM comments c FROM comments c
JOIN "user" u ON c.user_id = u.id JOIN "user" u ON c.user_id = u.id
@@ -1432,7 +1432,7 @@ export default {
c.id, c.parent_id, c.item_id, c.content, c.created_at, c.vote_score, c.is_deleted, c.id, c.parent_id, c.item_id, c.content, c.created_at, c.vote_score, c.is_deleted,
COALESCE(c.is_pinned, false) as is_pinned, COALESCE(c.is_pinned, false) as is_pinned,
c.video_time, c.video_time,
u.user as username, u.id as user_id, uo.avatar, uo.avatar_file, uo.username_color, uo.display_name u.user as username, u.id as user_id, uo.avatar, uo.avatar_file, uo.username_color, uo.display_name, uo.banner_file, uo.banner_position, uo.banner_size, uo.banner_repeat
FROM comments c FROM comments c
JOIN "user" u ON c.user_id = u.id JOIN "user" u ON c.user_id = u.id
LEFT JOIN user_options uo ON uo.user_id = u.id LEFT JOIN user_options uo ON uo.user_id = u.id

View File

@@ -593,6 +593,9 @@ export default (router, tpl) => {
// Large comments would silently drop the notification. The client fetches // Large comments would silently drop the notification. The client fetches
// the full content via _silentSync; the NOTIFY only needs to trigger the update. // the full content via _silentSync; the NOTIFY only needs to trigger the update.
const notifyBody = content.length > 500 ? content.substring(0, 500) + '…' : content; const notifyBody = content.length > 500 ? content.substring(0, 500) + '…' : content;
const uo = await db`SELECT banner_file, banner_position, banner_size, banner_repeat FROM user_options WHERE user_id = ${req.session.id}`;
const bannerOpt = uo[0] || {};
const livePayload = { const livePayload = {
type: 'comment', type: 'comment',
id: commentId, id: commentId,
@@ -604,6 +607,10 @@ export default (router, tpl) => {
user_id: req.session.id, user_id: req.session.id,
avatar: req.session.avatar, avatar: req.session.avatar,
avatar_file: req.session.avatar_file, avatar_file: req.session.avatar_file,
banner_file: bannerOpt.banner_file || req.session.banner_file || null,
banner_position: bannerOpt.banner_position || req.session.banner_position || null,
banner_size: bannerOpt.banner_size || req.session.banner_size || null,
banner_repeat: bannerOpt.banner_repeat || req.session.banner_repeat || null,
created_at: new Date().toISOString(), created_at: new Date().toISOString(),
username_color: req.session.username_color, username_color: req.session.username_color,
display_name: req.session.display_name || null, display_name: req.session.display_name || null,
@@ -631,6 +638,10 @@ export default (router, tpl) => {
item_rating_label: ratingLabel, item_rating_label: ratingLabel,
avatar: req.session.avatar, avatar: req.session.avatar,
avatar_file: req.session.avatar_file, avatar_file: req.session.avatar_file,
banner_file: bannerOpt.banner_file || req.session.banner_file || null,
banner_position: bannerOpt.banner_position || req.session.banner_position || null,
banner_size: bannerOpt.banner_size || req.session.banner_size || null,
banner_repeat: bannerOpt.banner_repeat || req.session.banner_repeat || null,
username: req.session.user, username: req.session.user,
username_color: req.session.username_color, username_color: req.session.username_color,
display_name: req.session.display_name || null, display_name: req.session.display_name || null,
@@ -654,7 +665,11 @@ export default (router, tpl) => {
comment: { comment: {
...newComment[0], ...newComment[0],
content, content,
files: activityFiles files: activityFiles,
banner_file: bannerOpt.banner_file || req.session.banner_file || null,
banner_position: bannerOpt.banner_position || req.session.banner_position || null,
banner_size: bannerOpt.banner_size || req.session.banner_size || null,
banner_repeat: bannerOpt.banner_repeat || req.session.banner_repeat || null
}, },
xd_score: xdRow?.xd_score ?? null, xd_score: xdRow?.xd_score ?? null,
is_new_subscription is_new_subscription

View File

@@ -501,6 +501,7 @@ process.on('uncaughtException', err => {
await runMigration(db`ALTER TABLE user_options ADD COLUMN IF NOT EXISTS banner_file character varying(255) DEFAULT NULL`); await runMigration(db`ALTER TABLE user_options ADD COLUMN IF NOT EXISTS banner_file character varying(255) DEFAULT NULL`);
await runMigration(db`ALTER TABLE user_options ADD COLUMN IF NOT EXISTS banner_position character varying(50) DEFAULT 'center'`); await runMigration(db`ALTER TABLE user_options ADD COLUMN IF NOT EXISTS banner_position character varying(50) DEFAULT 'center'`);
await runMigration(db`ALTER TABLE user_options ADD COLUMN IF NOT EXISTS banner_size character varying(50) DEFAULT 'cover'`); await runMigration(db`ALTER TABLE user_options ADD COLUMN IF NOT EXISTS banner_size character varying(50) DEFAULT 'cover'`);
await runMigration(db`ALTER TABLE user_options ADD COLUMN IF NOT EXISTS banner_repeat character varying(50) DEFAULT 'repeat'`);
await runMigration(db`ALTER TABLE items ADD COLUMN IF NOT EXISTS expires_at bigint DEFAULT NULL`); await runMigration(db`ALTER TABLE items ADD COLUMN IF NOT EXISTS expires_at bigint DEFAULT NULL`);
await runMigration(db`ALTER TABLE items ADD COLUMN IF NOT EXISTS width integer DEFAULT NULL`); await runMigration(db`ALTER TABLE items ADD COLUMN IF NOT EXISTS width integer DEFAULT NULL`);
await runMigration(db`ALTER TABLE items ADD COLUMN IF NOT EXISTS height integer DEFAULT NULL`); await runMigration(db`ALTER TABLE items ADD COLUMN IF NOT EXISTS height integer DEFAULT NULL`);
@@ -747,7 +748,7 @@ process.on('uncaughtException', err => {
user = [_cachedRow]; user = [_cachedRow];
} else { } else {
user = await db` 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".banner_file, "user_options".banner_position, "user_options".banner_size, "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".favorites_private, "user_options".hide_fav_badge, "user_options".default_upload_visibility, "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 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".banner_file, "user_options".banner_position, "user_options".banner_size, "user_options".banner_repeat, "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".favorites_private, "user_options".hide_fav_badge, "user_options".default_upload_visibility, "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

View File

@@ -391,6 +391,10 @@
embed_youtube_in_comments: @if(session && session.embed_youtube_in_comments !== false) true @else false @endif, embed_youtube_in_comments: @if(session && session.embed_youtube_in_comments !== false) true @else false @endif,
avatar: @if(session && session.avatar) {{ session.avatar }} @else null @endif, avatar: @if(session && session.avatar) {{ session.avatar }} @else null @endif,
avatar_file: @if(session && session.avatar_file) "{{ session.avatar_file }}" @else null @endif, avatar_file: @if(session && session.avatar_file) "{{ session.avatar_file }}" @else null @endif,
banner_file: @if(session && session.banner_file) "{{ session.banner_file }}" @else null @endif,
banner_position: @if(session && session.banner_position) "{{ session.banner_position }}" @else null @endif,
banner_size: @if(session && session.banner_size) "{{ session.banner_size }}" @else null @endif,
banner_repeat: @if(session && session.banner_repeat) "{{ session.banner_repeat }}" @else null @endif,
receive_system_notifications: @if(session)@if(session.receive_system_notifications !== false) true @else false @endif@else true @endif, receive_system_notifications: @if(session)@if(session.receive_system_notifications !== false) true @else false @endif@else true @endif,
receive_user_notifications: @if(session)@if(session.receive_user_notifications !== false) true @else false @endif@else true @endif, receive_user_notifications: @if(session)@if(session.receive_user_notifications !== false) true @else false @endif@else true @endif,
do_not_disturb: @if(session && session.do_not_disturb) true @else false @endif, do_not_disturb: @if(session && session.do_not_disturb) true @else false @endif,