live update banner udpates

This commit is contained in:
2026-07-16 21:37:28 +02:00
parent e416575419
commit 813a0a066e
6 changed files with 61 additions and 5 deletions

View File

@@ -7306,8 +7306,11 @@ class NotificationSystem {
}
}
} else if (data.type === 'profile_update') {
const { display_name, user } = data.data;
this.applyDisplayNameUpdate(display_name, user);
const { display_name, user, banner_file, banner_position, banner_size } = data.data;
if (display_name !== undefined) this.applyDisplayNameUpdate(display_name, user);
if (banner_file !== undefined || banner_position !== undefined || banner_size !== undefined) {
this.applyBannerUpdate(banner_file, banner_position, banner_size, user);
}
// Sync preferences to global session object for client-side gating (like DND)
if (window.f0ckSession && data.data.user_id === window.f0ckSession.id) {
for (const key in data.data) {
@@ -7366,6 +7369,34 @@ class NotificationSystem {
}
};
// Helper: apply banner update to relevant DOM nodes
this.applyBannerUpdate = (banner_file, banner_position, banner_size, user) => {
const userName = user || window.f0ckSession?.user;
if (!userName) return;
const setBannerStyle = (el) => {
if (banner_file) {
el.style.setProperty('--author-banner', `url('/a/${banner_file}?t=${Date.now()}')`);
el.style.setProperty('--author-banner-position', banner_position || 'center');
el.style.setProperty('--author-banner-size', banner_size || 'cover');
} else {
el.style.removeProperty('--author-banner');
el.style.removeProperty('--author-banner-position');
el.style.removeProperty('--author-banner-size');
}
};
document.querySelectorAll(`.profile_head[data-banner-user="${userName}"], .user-infobox-block[data-banner-user="${userName}"]`).forEach(el => {
setBannerStyle(el);
});
// Special case for settings page preview block
if (window.f0ckSession && userName === window.f0ckSession.user) {
const previewBlock = document.getElementById('live-profile-preview-box');
if (previewBlock) setBannerStyle(previewBlock);
}
};
// Poll fresh display_name from DB — used on tab focus to catch missed SSE events
this.syncDisplayName = async () => {
try {

View File

@@ -193,6 +193,14 @@ export const handleBannerUpload = async (req, res) => {
banner_size = ${parts.banner_size || 'cover'}
where user_id = ${+req.session.id}
`;
const payloadStr = JSON.stringify({
user_id: +req.session.id,
user: req.session.user,
banner_file: finalFilename,
banner_position: parts.banner_position || 'center',
banner_size: parts.banner_size || 'cover'
});
await db`select pg_notify('profile_update', ${payloadStr})`;
} catch (dbErr) {
console.error('[BANNER HANDLER] DB update failed:', dbErr.message);
await fs.unlink(finalPath).catch(() => {});
@@ -265,6 +273,12 @@ export const handleBannerDelete = async (req, res) => {
set banner_file = null
where user_id = ${+req.session.id}
`;
const payloadStr = JSON.stringify({
user_id: +req.session.id,
user: req.session.user,
banner_file: null
});
await db`select pg_notify('profile_update', ${payloadStr})`;
console.log('[BANNER HANDLER] Delete complete');
return sendJson(res, { success: true, msg: 'Custom banner removed' }, 200);

View File

@@ -94,6 +94,17 @@ db.listen('profile_update', (payload) => {
if (data.do_not_disturb !== undefined) client.do_not_disturb = data.do_not_disturb;
client.send({ type: 'profile_update', data });
} else {
// For other users, only send public profile fields (so their DOM can live-update)
const publicData = {
user_id: data.user_id,
user: data.user,
display_name: data.display_name,
banner_file: data.banner_file,
banner_position: data.banner_position,
banner_size: data.banner_size
};
client.send({ type: 'profile_update', data: publicData });
}
}
} catch (e) {

View File

@@ -1,4 +1,4 @@
<div class="profile_head" style="@if(user.banner_file && user_banner_enabled) --author-banner: url('/a/{{ user.banner_file }}'); --author-banner-position: {{ user.banner_position || 'center' }}; --author-banner-size: {{ user.banner_size || 'cover' }}; @endif">
<div class="profile_head" data-banner-user="{{ user.user || '' }}" style="@if(user.banner_file && user_banner_enabled) --author-banner: url('/a/{{ user.banner_file }}'); --author-banner-position: {{ user.banner_position || 'center' }}; --author-banner-size: {{ user.banner_size || 'cover' }}; @endif">
@if(user.avatar_file)
<div class="profile_head_avatar">
<img src="/a/{{ user.avatar_file }}" style="display: grid;width: 55px" />

View File

@@ -84,7 +84,7 @@
<div class="blahlol">
@if(user_alternative_infobox)
<div class="user-infobox-block" style="--author-accent: @if(item.author_color){{ item.author_color }}@else var(--accent) @endif; --author-border: @if(item.author_color){{ item.author_color }}@else var(--accent) @endif;@if(item.author_banner_file && user_banner_enabled) --author-banner: url('/a/{{ item.author_banner_file }}'); --author-banner-position: {{ item.author_banner_position || 'center' }}; --author-banner-size: {{ item.author_banner_size || 'cover' }};@endif">
<div class="user-infobox-block" data-banner-user="{{ item.username || '' }}" style="--author-accent: @if(item.author_color){{ item.author_color }}@else var(--accent) @endif; --author-border: @if(item.author_color){{ item.author_color }}@else var(--accent) @endif;@if(item.author_banner_file && user_banner_enabled) --author-banner: url('/a/{{ item.author_banner_file }}'); --author-banner-position: {{ item.author_banner_position || 'center' }}; --author-banner-size: {{ item.author_banner_size || 'cover' }};@endif">
<div class="user-infobox-avatar">
<a href="/user/{{ (item.username || '').toLowerCase() }}">

View File

@@ -1,4 +1,4 @@
<div class="profile_head" style="@if(user.banner_file && user_banner_enabled) --author-banner: url('/a/{{ user.banner_file }}'); --author-banner-position: {{ user.banner_position || 'center' }}; --author-banner-size: {{ user.banner_size || 'cover' }}; @endif">
<div class="profile_head" data-banner-user="{{ user.user || '' }}" style="@if(user.banner_file && user_banner_enabled) --author-banner: url('/a/{{ user.banner_file }}'); --author-banner-position: {{ user.banner_position || 'center' }}; --author-banner-size: {{ user.banner_size || 'cover' }}; @endif">
@if(user.is_ghost)
<div class="profile_head_avatar">
<div class="avatar-placeholder" style="background: #444; color: #888; width: 55px; height: 55px; display: flex; align-items: center; justify-content: center; font-size: 24px; font-weight: bold; border-radius: 4px;">?</div>