QoL fixes
This commit is contained in:
@@ -283,6 +283,8 @@ window.cancelAnimFrame = (function () {
|
|||||||
baseStyle += 'background:rgba(200,30,30,0.95);';
|
baseStyle += 'background:rgba(200,30,30,0.95);';
|
||||||
} else if (type === 'success') {
|
} else if (type === 'success') {
|
||||||
baseStyle += 'background:rgba(30,130,60,0.95);';
|
baseStyle += 'background:rgba(30,130,60,0.95);';
|
||||||
|
} else if (type === 'warning') {
|
||||||
|
baseStyle += 'background:rgba(220,180,0,0.95);color:#000;';
|
||||||
} else {
|
} else {
|
||||||
baseStyle += 'background:rgba(30,30,30,0.95);';
|
baseStyle += 'background:rgba(30,30,30,0.95);';
|
||||||
}
|
}
|
||||||
@@ -5985,7 +5987,7 @@ if (sbtForm) {
|
|||||||
class NotificationSystem {
|
class NotificationSystem {
|
||||||
// Notification type categorization
|
// Notification type categorization
|
||||||
static USER_TYPES = ['comment_reply', 'subscription', 'mention', 'upload_comment'];
|
static USER_TYPES = ['comment_reply', 'subscription', 'mention', 'upload_comment'];
|
||||||
static SYSTEM_TYPES = ['approve', 'deny', 'item_deleted', 'upload_success', 'upload_error', 'admin_pending', 'report'];
|
static SYSTEM_TYPES = ['approve', 'deny', 'item_deleted', 'upload_success', 'upload_error', 'admin_pending', 'report', 'warning'];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.bell = document.getElementById('nav-notif-btn');
|
this.bell = document.getElementById('nav-notif-btn');
|
||||||
@@ -6185,7 +6187,7 @@ class NotificationSystem {
|
|||||||
|
|
||||||
// System notifications (deletion, deny, reports) require explicit acknowledgment —
|
// System notifications (deletion, deny, reports) require explicit acknowledgment —
|
||||||
// never auto-mark them as read just because the user is viewing that item.
|
// never auto-mark them as read just because the user is viewing that item.
|
||||||
const isSystemNotif = ['item_deleted', 'deny', 'admin_pending', 'report'].includes(notifType);
|
const isSystemNotif = ['item_deleted', 'deny', 'admin_pending', 'report', 'warning'].includes(notifType);
|
||||||
|
|
||||||
// If the user is currently viewing this item, mark comment-type notifications as read immediately
|
// If the user is currently viewing this item, mark comment-type notifications as read immediately
|
||||||
// (they are live on the thread, so no need to show a badge/highlight)
|
// (they are live on the thread, so no need to show a badge/highlight)
|
||||||
@@ -6950,6 +6952,11 @@ class NotificationSystem {
|
|||||||
link = '/mod/reports';
|
link = '/mod/reports';
|
||||||
user = (window.f0ckI18n && window.f0ckI18n.notif_moderation) || 'Moderator';
|
user = (window.f0ckI18n && window.f0ckI18n.notif_moderation) || 'Moderator';
|
||||||
msg = (window.f0ckI18n && window.f0ckI18n.notif_new_report) || 'A new user report has been submitted';
|
msg = (window.f0ckI18n && window.f0ckI18n.notif_new_report) || 'A new user report has been submitted';
|
||||||
|
} else if (n.type === 'warning') {
|
||||||
|
link = `/notifications?tab=system#notif-${n.id}`;
|
||||||
|
user = (window.f0ckI18n && window.f0ckI18n.notif_system) || 'System';
|
||||||
|
msg = (window.f0ckI18n && window.f0ckI18n.account_warning && window.f0ckI18n.account_warning.title) || 'Account Warning';
|
||||||
|
if (n.reason) msg += `<br><div class="notif-reason" style="font-size: 0.85em; color: #ffb8b8; margin-top: 3px;">${n.reason}</div>`;
|
||||||
} else {
|
} else {
|
||||||
// Comment notification
|
// Comment notification
|
||||||
link = `/${n.item_id}#c${n.comment_id || n.reference_id}`;
|
link = `/${n.item_id}#c${n.comment_id || n.reference_id}`;
|
||||||
@@ -6961,7 +6968,18 @@ class NotificationSystem {
|
|||||||
|
|
||||||
// For admin_pending the thumbnail lives in /mod/pending/t/ until approved
|
// For admin_pending the thumbnail lives in /mod/pending/t/ until approved
|
||||||
let thumbSrc, thumbOnerror;
|
let thumbSrc, thumbOnerror;
|
||||||
if (n.type === 'admin_pending') {
|
if (n.type === 'warning') {
|
||||||
|
return `
|
||||||
|
<div class="notif-item ${n.is_read ? '' : 'unread'} notif-with-thumb" data-id="${n.id}">
|
||||||
|
<div class="notif-thumb" style="display:flex;align-items:center;justify-content:center;background:var(--bg-lighter);color:var(--danger);font-size:1.5em;"><i class="fa-solid fa-triangle-exclamation"></i></div>
|
||||||
|
<div class="notif-content">
|
||||||
|
<div class="notif-user"><strong ${n.username_color ? `style="color: ${n.username_color}"` : ''}>${user}</strong></div>
|
||||||
|
<div class="notif-msg">${msg}</div>
|
||||||
|
<div class="notif-time">${new Date(n.created_at).toLocaleString()}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
} else if (n.type === 'admin_pending') {
|
||||||
thumbSrc = `/mod/pending/t/${n.item_id}.webp`;
|
thumbSrc = `/mod/pending/t/${n.item_id}.webp`;
|
||||||
thumbOnerror = `this.onerror=null;this.src='/t/${n.item_id}.webp';this.onerror=function(){this.style.display='none';}`;
|
thumbOnerror = `this.onerror=null;this.src='/t/${n.item_id}.webp';this.onerror=function(){this.style.display='none';}`;
|
||||||
} else {
|
} else {
|
||||||
@@ -7080,6 +7098,22 @@ class NotificationSystem {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (n.type === 'warning') {
|
||||||
|
const link = `/notifications?tab=system#notif-${n.id}`;
|
||||||
|
return `
|
||||||
|
<a href="${link}" class="notif-item ${n.is_read ? '' : 'unread'} notif-with-thumb" data-id="${n.id}" data-notif-nav="true">
|
||||||
|
<div class="notif-thumb" style="display:flex;align-items:center;justify-content:center;background:var(--bg-lighter);color:var(--danger);font-size:1.5em;"><i class="fa-solid fa-triangle-exclamation"></i></div>
|
||||||
|
<div class="notif-content">
|
||||||
|
<div>
|
||||||
|
<strong>${(window.f0ckI18n && window.f0ckI18n.account_warning && window.f0ckI18n.account_warning.title) || 'Account Warning'}</strong>
|
||||||
|
<div style="font-size: 0.85em; color: #ffb8b8; margin-top: 3px;">${n.reason || 'No reason provided'}</div>
|
||||||
|
</div>
|
||||||
|
<small class="notif-time">${new Date(n.created_at).toLocaleString()}</small>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
let typeText = 'Start';
|
let typeText = 'Start';
|
||||||
if (n.type === 'comment_reply') typeText = (window.f0ckI18n && window.f0ckI18n.notif_replied) || 'replied to you';
|
if (n.type === 'comment_reply') typeText = (window.f0ckI18n && window.f0ckI18n.notif_replied) || 'replied to you';
|
||||||
else if (n.type === 'subscription') typeText = (window.f0ckI18n && window.f0ckI18n.notif_subscribed) || 'commented in a thread you follow';
|
else if (n.type === 'subscription') typeText = (window.f0ckI18n && window.f0ckI18n.notif_subscribed) || 'commented in a thread you follow';
|
||||||
|
|||||||
@@ -543,6 +543,14 @@
|
|||||||
|
|
||||||
const result = await res.json();
|
const result = await res.json();
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
|
if (result.manual_approval) {
|
||||||
|
window.flashMessage(window.f0ckI18n?.upload_pending_approval_patient || 'Upload awaits approval', 3000, 'warning');
|
||||||
|
if (window.loadPageAjax) {
|
||||||
|
window.loadPageAjax('/');
|
||||||
|
} else {
|
||||||
|
window.location.href = '/';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
const dest = result.redirect || '/meme';
|
const dest = result.redirect || '/meme';
|
||||||
if (window.loadItemAjax) {
|
if (window.loadItemAjax) {
|
||||||
window.loadItemAjax(dest);
|
window.loadItemAjax(dest);
|
||||||
@@ -552,6 +560,7 @@
|
|||||||
window.location.href = dest;
|
window.location.href = dest;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
window.flashMessage('Error: ' + result.msg, 3000, 'error');
|
window.flashMessage('Error: ' + result.msg, 3000, 'error');
|
||||||
uploadBtn.disabled = false;
|
uploadBtn.disabled = false;
|
||||||
|
|||||||
@@ -3496,7 +3496,7 @@
|
|||||||
|
|
||||||
// Tab type arrays
|
// Tab type arrays
|
||||||
const SCROLLER_USER_TYPES = ['comment_reply', 'subscription', 'mention', 'upload_comment'];
|
const SCROLLER_USER_TYPES = ['comment_reply', 'subscription', 'mention', 'upload_comment'];
|
||||||
const SCROLLER_SYSTEM_TYPES = ['approve', 'deny', 'item_deleted', 'upload_success', 'upload_error', 'admin_pending', 'report'];
|
const SCROLLER_SYSTEM_TYPES = ['approve', 'deny', 'item_deleted', 'upload_success', 'upload_error', 'admin_pending', 'report', 'warning'];
|
||||||
let sActiveTab = 'user';
|
let sActiveTab = 'user';
|
||||||
let sCachedNotifs = [];
|
let sCachedNotifs = [];
|
||||||
|
|
||||||
@@ -3600,6 +3600,9 @@
|
|||||||
} else if (n.type === 'report') {
|
} else if (n.type === 'report') {
|
||||||
link = '/mod/reports'; user = i18n.notif_moderation || 'Moderator';
|
link = '/mod/reports'; user = i18n.notif_moderation || 'Moderator';
|
||||||
msg = i18n.notif_new_report || 'New user report';
|
msg = i18n.notif_new_report || 'New user report';
|
||||||
|
} else if (n.type === 'warning') {
|
||||||
|
link = `/notifications?tab=system#notif-${n.id}`; user = i18n.notif_system || 'System';
|
||||||
|
msg = (i18n.account_warning && i18n.account_warning.title) || 'Account Warning';
|
||||||
} else {
|
} else {
|
||||||
link = `/${n.item_id}#c${n.reference_id}`;
|
link = `/${n.item_id}#c${n.reference_id}`;
|
||||||
if (n.type === 'comment_reply') msg = i18n.notif_replied || 'replied to you';
|
if (n.type === 'comment_reply') msg = i18n.notif_replied || 'replied to you';
|
||||||
@@ -3607,8 +3610,13 @@
|
|||||||
else if (n.type === 'mention') msg = i18n.notif_mentioned || 'highlighted you';
|
else if (n.type === 'mention') msg = i18n.notif_mentioned || 'highlighted you';
|
||||||
else msg = i18n.notif_commented || 'commented';
|
else msg = i18n.notif_commented || 'commented';
|
||||||
}
|
}
|
||||||
|
let thumb;
|
||||||
|
if (n.type === 'warning') {
|
||||||
|
thumb = `<div class="notif-thumb" style="display:flex;align-items:center;justify-content:center;background:var(--bg-lighter);color:var(--danger);font-size:1.5em;"><i class="fa-solid fa-triangle-exclamation"></i></div>`;
|
||||||
|
} else {
|
||||||
const thumbSrc = n.type === 'admin_pending' ? `/mod/pending/t/${n.item_id}.webp` : `/t/${n.item_id}.webp`;
|
const thumbSrc = n.type === 'admin_pending' ? `/mod/pending/t/${n.item_id}.webp` : `/t/${n.item_id}.webp`;
|
||||||
const thumb = n.item_id ? `<div class="notif-thumb"><img src="${thumbSrc}" alt="" onerror="this.style.display='none'"></div>` : '';
|
thumb = n.item_id ? `<div class="notif-thumb"><img src="${thumbSrc}" alt="" onerror="this.style.display='none'"></div>` : '';
|
||||||
|
}
|
||||||
return `<a href="${link}" target="_blank" class="notif-item ${n.is_read ? '' : 'unread'} notif-with-thumb" data-id="${n.id}">
|
return `<a href="${link}" target="_blank" class="notif-item ${n.is_read ? '' : 'unread'} notif-with-thumb" data-id="${n.id}">
|
||||||
${thumb}
|
${thumb}
|
||||||
<div class="notif-content">
|
<div class="notif-content">
|
||||||
|
|||||||
@@ -1971,18 +1971,18 @@ window.initUploadForm = (selector) => {
|
|||||||
form._f0ckUploader.reset();
|
form._f0ckUploader.reset();
|
||||||
|
|
||||||
if (isShitpost) {
|
if (isShitpost) {
|
||||||
// Flash message removed as requested
|
if (lastData?.manual_approval && typeof window.flashMessage === 'function') {
|
||||||
if (lastData?.manual_approval && typeof window.showFlash === 'function') {
|
window.flashMessage(window.f0ckI18n?.upload_pending_approval_patient || 'Upload awaits approval', 3000, 'warning');
|
||||||
window.showFlash('Upload awaits approval, please be patient', 'info');
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!dragModal && statusDiv) {
|
if (lastData?.manual_approval) {
|
||||||
|
if (typeof window.flashMessage === 'function') {
|
||||||
|
window.flashMessage(window.f0ckI18n?.upload_pending_approval_patient || 'Upload awaits approval', 3000, 'warning');
|
||||||
|
}
|
||||||
|
} else if (!dragModal && statusDiv) {
|
||||||
statusDiv.innerHTML = '✓ ' + (lastData?.msg || 'Upload successful');
|
statusDiv.innerHTML = '✓ ' + (lastData?.msg || 'Upload successful');
|
||||||
statusDiv.className = 'upload-status success';
|
statusDiv.className = 'upload-status success';
|
||||||
}
|
}
|
||||||
if (lastData?.manual_approval && typeof window.showFlash === 'function') {
|
|
||||||
window.showFlash('Upload awaits approval, please be patient', 'info');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -2127,17 +2127,18 @@ window.initUploadForm = (selector) => {
|
|||||||
if (dragModal) dragModal.classList.remove('show');
|
if (dragModal) dragModal.classList.remove('show');
|
||||||
form._f0ckUploader.reset();
|
form._f0ckUploader.reset();
|
||||||
if (isShitpost) {
|
if (isShitpost) {
|
||||||
// Flash message removed as requested
|
if (lastData?.manual_approval && typeof window.flashMessage === 'function') {
|
||||||
if (lastData?.manual_approval && typeof window.showFlash === 'function') {
|
window.flashMessage(window.f0ckI18n?.upload_pending_approval_patient || 'Upload awaits approval', 3000, 'warning');
|
||||||
window.showFlash('Upload awaits approval, please be patient', 'info');
|
}
|
||||||
|
} else {
|
||||||
|
if (lastData?.manual_approval) {
|
||||||
|
if (typeof window.flashMessage === 'function') {
|
||||||
|
window.flashMessage(window.f0ckI18n?.upload_pending_approval_patient || 'Upload awaits approval', 3000, 'warning');
|
||||||
}
|
}
|
||||||
} else if (!dragModal && statusDiv) {
|
} else if (!dragModal && statusDiv) {
|
||||||
statusDiv.innerHTML = '✓ ' + (lastData?.msg || 'Upload successful');
|
statusDiv.innerHTML = '✓ ' + (lastData?.msg || 'Upload successful');
|
||||||
statusDiv.className = 'upload-status success';
|
statusDiv.className = 'upload-status success';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isShitpost && lastData?.manual_approval && typeof window.showFlash === 'function') {
|
|
||||||
window.showFlash('Upload awaits approval, please be patient', 'info');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|||||||
@@ -57,7 +57,8 @@ db.listen('notifications', (payload) => {
|
|||||||
if (client.do_not_disturb === true) continue;
|
if (client.do_not_disturb === true) continue;
|
||||||
|
|
||||||
if (SYSTEM_TYPES.includes(data.type) && client.receive_system_notifications === false) continue;
|
if (SYSTEM_TYPES.includes(data.type) && client.receive_system_notifications === false) continue;
|
||||||
if (USER_TYPES.includes(data.type) && client.receive_user_notifications === false) continue;
|
// warnings bypass user settings
|
||||||
|
if (data.type !== 'warning' && USER_TYPES.includes(data.type) && client.receive_user_notifications === false) continue;
|
||||||
client.send({ type: 'notify', data });
|
client.send({ type: 'notify', data });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -343,7 +344,7 @@ db.listen('global_chat_topic', (payload) => {
|
|||||||
export default (router, tpl) => {
|
export default (router, tpl) => {
|
||||||
|
|
||||||
const USER_TYPES = ['comment_reply', 'subscription', 'mention', 'upload_comment'];
|
const USER_TYPES = ['comment_reply', 'subscription', 'mention', 'upload_comment'];
|
||||||
const SYSTEM_TYPES = ['approve', 'deny', 'item_deleted', 'upload_success', 'upload_error', 'admin_pending', 'report'];
|
const SYSTEM_TYPES = ['approve', 'deny', 'item_deleted', 'upload_success', 'upload_error', 'admin_pending', 'report', 'warning'];
|
||||||
|
|
||||||
async function getNotificationHistory(userId, page = 1, limit = 50, tab = null) {
|
async function getNotificationHistory(userId, page = 1, limit = 50, tab = null) {
|
||||||
const offset = (page - 1) * limit;
|
const offset = (page - 1) * limit;
|
||||||
@@ -363,7 +364,7 @@ export default (router, tpl) => {
|
|||||||
LEFT JOIN items i ON n.item_id = i.id
|
LEFT JOIN items i ON n.item_id = i.id
|
||||||
WHERE n.user_id = ${userId}
|
WHERE n.user_id = ${userId}
|
||||||
AND n.type = ANY(${typeFilter})
|
AND n.type = ANY(${typeFilter})
|
||||||
AND (n.type IN ('admin_pending', 'deny', 'item_deleted', 'report') OR i.id IS NULL OR (i.active = true AND i.is_deleted = false))
|
AND (n.type IN ('admin_pending', 'deny', 'item_deleted', 'report', 'warning') OR i.id IS NULL OR (i.active = true AND i.is_deleted = false))
|
||||||
ORDER BY n.created_at DESC
|
ORDER BY n.created_at DESC
|
||||||
LIMIT ${limit + 1}
|
LIMIT ${limit + 1}
|
||||||
OFFSET ${offset}
|
OFFSET ${offset}
|
||||||
@@ -381,7 +382,7 @@ export default (router, tpl) => {
|
|||||||
LEFT JOIN user_options uo ON u.id = uo.user_id
|
LEFT JOIN user_options uo ON u.id = uo.user_id
|
||||||
LEFT JOIN items i ON n.item_id = i.id
|
LEFT JOIN items i ON n.item_id = i.id
|
||||||
WHERE n.user_id = ${userId}
|
WHERE n.user_id = ${userId}
|
||||||
AND (n.type IN ('admin_pending', 'deny', 'item_deleted', 'report') OR i.id IS NULL OR (i.active = true AND i.is_deleted = false))
|
AND (n.type IN ('admin_pending', 'deny', 'item_deleted', 'report', 'warning') OR i.id IS NULL OR (i.active = true AND i.is_deleted = false))
|
||||||
ORDER BY n.created_at DESC
|
ORDER BY n.created_at DESC
|
||||||
LIMIT ${limit + 1}
|
LIMIT ${limit + 1}
|
||||||
OFFSET ${offset}
|
OFFSET ${offset}
|
||||||
@@ -426,7 +427,7 @@ export default (router, tpl) => {
|
|||||||
LEFT JOIN user_options uo ON u.id = uo.user_id
|
LEFT JOIN user_options uo ON u.id = uo.user_id
|
||||||
LEFT JOIN items i ON n.item_id = i.id
|
LEFT JOIN items i ON n.item_id = i.id
|
||||||
WHERE n.user_id = ${req.session.id} AND n.is_read = false
|
WHERE n.user_id = ${req.session.id} AND n.is_read = false
|
||||||
AND (n.type IN ('admin_pending', 'deny', 'item_deleted', 'report', 'approve')
|
AND (n.type IN ('admin_pending', 'deny', 'item_deleted', 'report', 'approve', 'warning')
|
||||||
OR (
|
OR (
|
||||||
${req.session.do_not_disturb !== true} AND (
|
${req.session.do_not_disturb !== true} AND (
|
||||||
(n.type IN ('upload_success', 'upload_error') AND ${req.session.receive_system_notifications !== false})
|
(n.type IN ('upload_success', 'upload_error') AND ${req.session.receive_system_notifications !== false})
|
||||||
@@ -434,7 +435,7 @@ export default (router, tpl) => {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
AND (n.item_id IS NULL OR (i.active = true AND i.is_deleted = false) OR n.type IN ('admin_pending', 'deny', 'item_deleted', 'report'))
|
AND (n.item_id IS NULL OR (i.active = true AND i.is_deleted = false) OR n.type IN ('admin_pending', 'deny', 'item_deleted', 'report', 'warning'))
|
||||||
ORDER BY n.created_at DESC
|
ORDER BY n.created_at DESC
|
||||||
LIMIT 1000
|
LIMIT 1000
|
||||||
`;
|
`;
|
||||||
@@ -496,7 +497,7 @@ export default (router, tpl) => {
|
|||||||
router.post(/\/api\/notifications\/item\/(?<itemId>\d+)\/read/, async (req, res) => {
|
router.post(/\/api\/notifications\/item\/(?<itemId>\d+)\/read/, async (req, res) => {
|
||||||
if (!req.session) return res.reply({ code: 401, body: JSON.stringify({ success: false }) });
|
if (!req.session) return res.reply({ code: 401, body: JSON.stringify({ success: false }) });
|
||||||
const itemId = req.params.itemId;
|
const itemId = req.params.itemId;
|
||||||
const SYSTEM_TYPES = ['item_deleted', 'deny', 'admin_pending', 'report'];
|
const SYSTEM_TYPES = ['item_deleted', 'deny', 'admin_pending', 'report', 'warning'];
|
||||||
console.log(`[NotificationRoute] Marking comment notifications for item ${itemId} as read for user ${req.session.id}`);
|
console.log(`[NotificationRoute] Marking comment notifications for item ${itemId} as read for user ${req.session.id}`);
|
||||||
try {
|
try {
|
||||||
await db`
|
await db`
|
||||||
@@ -646,6 +647,7 @@ export default (router, tpl) => {
|
|||||||
next: data.hasMore ? 2 : null
|
next: data.hasMore ? 2 : null
|
||||||
};
|
};
|
||||||
data.link = { main: '/notifications', path: '/' };
|
data.link = { main: '/notifications', path: '/' };
|
||||||
|
data.activeTab = tab;
|
||||||
data.domain = cfg.main.url.domain; // For header
|
data.domain = cfg.main.url.domain; // For header
|
||||||
return res.html(tpl.render('notifications', data, req));
|
return res.html(tpl.render('notifications', data, req));
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -21,6 +21,11 @@ export default (router, tpl) => {
|
|||||||
|
|
||||||
// Broadcast to SSE clients instantly
|
// Broadcast to SSE clients instantly
|
||||||
if (result.length > 0) {
|
if (result.length > 0) {
|
||||||
|
await db`
|
||||||
|
INSERT INTO notifications (user_id, type, reference_id, data, is_read)
|
||||||
|
VALUES (${+user_id}, 'warning', 0, ${JSON.stringify({ reason: reason.trim(), warning_id: result[0].id })}, false)
|
||||||
|
`;
|
||||||
|
|
||||||
await db`SELECT pg_notify('warnings', ${JSON.stringify({
|
await db`SELECT pg_notify('warnings', ${JSON.stringify({
|
||||||
user_id: +user_id,
|
user_id: +user_id,
|
||||||
warning_id: result[0].id,
|
warning_id: result[0].id,
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
<button id="mark-all-read-page" class="btn-small">{{ t('notifications.mark_all_read') }}</button>
|
<button id="mark-all-read-page" class="btn-small">{{ t('notifications.mark_all_read') }}</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="notif-page-tabs">
|
<div class="notif-page-tabs">
|
||||||
<button class="notif-page-tab active" data-tab="user">{{ t('nav.notif_tab_user') }}</button>
|
<button class="notif-page-tab @if(activeTab === 'user') active @endif" data-tab="user">{{ t('nav.notif_tab_user') }}</button>
|
||||||
<button class="notif-page-tab" data-tab="system">{{ t('nav.notif_tab_system') }}</button>
|
<button class="notif-page-tab @if(activeTab === 'system') active @endif" data-tab="system">{{ t('nav.notif_tab_system') }}</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="notifications-container" class="posts notifications-list-full" data-page="{{ pagination.page }}" data-tab="user">
|
<div id="notifications-container" class="posts notifications-list-full" data-page="{{ pagination.page }}" data-tab="{{ activeTab }}">
|
||||||
@include(snippets/notifications-list)
|
@include(snippets/notifications-list)
|
||||||
</div>
|
</div>
|
||||||
@if(pagination.next)
|
@if(pagination.next)
|
||||||
|
|||||||
@@ -103,6 +103,18 @@
|
|||||||
<div class="notif-time">{{ new Date(n.created_at).toLocaleString() }}</div>
|
<div class="notif-time">{{ new Date(n.created_at).toLocaleString() }}</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
@elseif(n.type === 'warning')
|
||||||
|
<div class="notif-item {{ n.is_read ? '' : 'unread' }} notif-with-thumb" data-id="{{ n.id }}">
|
||||||
|
<div class="notif-thumb" style="display:flex;align-items:center;justify-content:center;background:var(--bg-lighter);color:var(--danger);font-size:1.5em;"><i class="fa-solid fa-triangle-exclamation"></i></div>
|
||||||
|
<div class="notif-content">
|
||||||
|
<div class="notif-user"><strong>{{ t('notifications.system') }}</strong></div>
|
||||||
|
<div class="notif-msg">
|
||||||
|
<strong>Account Warning</strong>
|
||||||
|
<div style="font-size: 0.85em; color: #ffb8b8; margin-top: 4px;">Reason: {{ n.reason }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="notif-time">{{ new Date(n.created_at).toLocaleString() }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@else
|
@else
|
||||||
<a href="/{{ n.item_id }}#c{{ n.comment_id || n.reference_id }}" class="notif-item {{ n.is_read ? '' : 'unread' }} notif-with-thumb" data-id="{{ n.id }}">
|
<a href="/{{ n.item_id }}#c{{ n.comment_id || n.reference_id }}" class="notif-item {{ n.is_read ? '' : 'unread' }} notif-with-thumb" data-id="{{ n.id }}">
|
||||||
@if(n.item_id)
|
@if(n.item_id)
|
||||||
|
|||||||
Reference in New Issue
Block a user