Release v2.2.0
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
// Announcement data access helpers.
|
||||
|
||||
const { fetchPagedRows } = require('./utils');
|
||||
const {
|
||||
ANNOUNCEMENT_ICON_KEYS,
|
||||
DEFAULT_ANNOUNCEMENT_ICON,
|
||||
normalizeAnnouncementIcon: normalizeAnnouncementIconFromConfig
|
||||
} = require('./announcement-icons');
|
||||
|
||||
const ANNOUNCEMENT_TYPES = ['lower-third', 'fullscreen', 'top-banner'];
|
||||
const ANNOUNCEMENT_COLORS = ['primary', 'secondary', 'success', 'info', 'warning', 'danger', 'dark', 'light'];
|
||||
|
||||
function normalizeAnnouncementType(value) {
|
||||
const normalized = String(value || '').trim().toLowerCase();
|
||||
if (normalized === 'center-card') {
|
||||
return 'fullscreen';
|
||||
}
|
||||
return ANNOUNCEMENT_TYPES.includes(normalized) ? normalized : 'lower-third';
|
||||
}
|
||||
|
||||
function normalizeAnnouncementColor(value) {
|
||||
const normalized = String(value || '').trim().toLowerCase();
|
||||
return ANNOUNCEMENT_COLORS.includes(normalized) ? normalized : 'primary';
|
||||
}
|
||||
|
||||
function normalizeAnnouncementMessage(value) {
|
||||
return String(value || '').replace(/\r\n/g, '\n').trim();
|
||||
}
|
||||
|
||||
function normalizeAnnouncementShortLabel(value) {
|
||||
return String(value || '').replace(/\r\n/g, ' ').trim();
|
||||
}
|
||||
|
||||
function normalizeAnnouncementDurationSeconds(value) {
|
||||
const seconds = Math.max(0, Math.round(Number(value) || 0));
|
||||
return seconds > 0 ? seconds : null;
|
||||
}
|
||||
|
||||
function normalizeAnnouncementScreenIds(value) {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(function (screenId) {
|
||||
return Number(screenId) || 0;
|
||||
}).filter(function (screenId) {
|
||||
return screenId > 0;
|
||||
});
|
||||
}
|
||||
|
||||
return String(value || '')
|
||||
.split(',')
|
||||
.map(function (screenId) {
|
||||
return Number(screenId) || 0;
|
||||
})
|
||||
.filter(function (screenId) {
|
||||
return screenId > 0;
|
||||
});
|
||||
}
|
||||
|
||||
function buildAnnouncementPayload(input) {
|
||||
const message = normalizeAnnouncementMessage(input && input.message);
|
||||
const shortLabel = normalizeAnnouncementShortLabel(input && input.short_label);
|
||||
const announcementType = normalizeAnnouncementType(input && input.announcement_type);
|
||||
const colorKey = normalizeAnnouncementColor(input && input.color_key);
|
||||
const iconKey = normalizeAnnouncementIconFromConfig(input && input.icon_key);
|
||||
const durationSeconds = normalizeAnnouncementDurationSeconds(input && input.duration_seconds);
|
||||
|
||||
return {
|
||||
message,
|
||||
shortLabel,
|
||||
announcementType,
|
||||
colorKey,
|
||||
iconKey,
|
||||
durationSeconds,
|
||||
expiresAt: durationSeconds ? new Date(Date.now() + (durationSeconds * 1000)) : null
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeAnnouncementRow(row) {
|
||||
if (!row) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
id: Number(row.id) || null,
|
||||
message: normalizeAnnouncementMessage(row.message),
|
||||
short_label: normalizeAnnouncementShortLabel(row.short_label),
|
||||
announcement_type: normalizeAnnouncementType(row.announcement_type),
|
||||
color_key: normalizeAnnouncementColor(row.color_key),
|
||||
icon_key: normalizeAnnouncementIconFromConfig(row.icon_key),
|
||||
duration_seconds: row.duration_seconds === null || row.duration_seconds === undefined
|
||||
? null
|
||||
: Math.max(0, Math.round(Number(row.duration_seconds) || 0)),
|
||||
expires_at: row.expires_at || null,
|
||||
screen_target_count: row.screen_target_count === null || row.screen_target_count === undefined
|
||||
? null
|
||||
: Math.max(0, Math.round(Number(row.screen_target_count) || 0)),
|
||||
total_screen_count: row.total_screen_count === null || row.total_screen_count === undefined
|
||||
? null
|
||||
: Math.max(0, Math.round(Number(row.total_screen_count) || 0)),
|
||||
screen_targets_label: String(row.screen_targets_label || '').trim(),
|
||||
screen_ids: normalizeAnnouncementScreenIds(row.screen_ids),
|
||||
created_at: row.created_at || null,
|
||||
modified_at: row.modified_at || null,
|
||||
created_by: row.created_by || null,
|
||||
modified_by: row.modified_by || null
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchAnnouncementById(pool, id) {
|
||||
const [rows] = await pool.query(`
|
||||
SELECT a.id, a.message, a.short_label, a.announcement_type, a.color_key, a.icon_key, a.duration_seconds, a.expires_at, a.created_at, a.modified_at, a.created_by, a.modified_by,
|
||||
GROUP_CONCAT(DISTINCT aas.screen_id ORDER BY aas.screen_id SEPARATOR ',') AS screen_ids
|
||||
FROM d_announcements a
|
||||
LEFT JOIN d_announcement_screens aas ON aas.announcement_id = a.id
|
||||
WHERE a.id = ?
|
||||
GROUP BY a.id
|
||||
`, [id]);
|
||||
return normalizeAnnouncementRow(rows[0] || null);
|
||||
}
|
||||
|
||||
async function fetchAnnouncementsPage(pool, page, pageSize, searchTerm, sortKey, sortDirection) {
|
||||
const paged = await fetchPagedRows(pool, {
|
||||
selectSql: `SELECT a.id, a.message, a.short_label, a.announcement_type, a.color_key, a.icon_key, a.duration_seconds, a.expires_at, a.created_at, a.modified_at, a.created_by, a.modified_by,
|
||||
COUNT(DISTINCT aas.screen_id) AS screen_target_count,
|
||||
(SELECT COUNT(*) FROM d_screens) AS total_screen_count,
|
||||
COALESCE(
|
||||
GROUP_CONCAT(DISTINCT COALESCE(NULLIF(TRIM(s.name), ''), s.slug) ORDER BY COALESCE(NULLIF(TRIM(s.name), ''), s.slug) SEPARATOR ', '),
|
||||
''
|
||||
) AS screen_targets_label
|
||||
FROM d_announcements a
|
||||
LEFT JOIN d_announcement_screens aas ON aas.announcement_id = a.id
|
||||
LEFT JOIN d_screens s ON s.id = aas.screen_id
|
||||
GROUP BY a.id`,
|
||||
countSql: 'SELECT COUNT(*) AS count FROM d_announcements',
|
||||
searchColumns: ['message', 'short_label', 'announcement_type', 'color_key'],
|
||||
searchTerm: searchTerm,
|
||||
sortColumns: {
|
||||
description: 'short_label',
|
||||
message: 'message',
|
||||
type: 'announcement_type',
|
||||
color: 'color_key',
|
||||
status: 'expires_at',
|
||||
expires: 'expires_at',
|
||||
created: 'created_at',
|
||||
modified: 'modified_at'
|
||||
},
|
||||
sortKey: sortKey,
|
||||
sortDirection: sortDirection,
|
||||
page: page,
|
||||
pageSize: pageSize
|
||||
});
|
||||
|
||||
return Object.assign({ announcements: (paged.rows || []).map(normalizeAnnouncementRow) }, paged);
|
||||
}
|
||||
|
||||
async function fetchActiveAnnouncement(pool, screenSlug) {
|
||||
const [rows] = await pool.query(`
|
||||
SELECT a.id, a.message, a.short_label, a.announcement_type, a.color_key, a.icon_key, a.duration_seconds, a.expires_at, a.created_at, a.modified_at, a.created_by, a.modified_by,
|
||||
GROUP_CONCAT(DISTINCT aas.screen_id ORDER BY aas.screen_id SEPARATOR ',') AS screen_ids
|
||||
FROM d_announcements a
|
||||
LEFT JOIN d_announcement_screens aas ON aas.announcement_id = a.id
|
||||
WHERE (a.expires_at IS NULL OR a.expires_at > CURRENT_TIMESTAMP)
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM d_announcement_screens restriction
|
||||
JOIN d_screens screen ON screen.id = restriction.screen_id
|
||||
WHERE restriction.announcement_id = a.id
|
||||
AND screen.slug = ?
|
||||
)
|
||||
GROUP BY a.id
|
||||
ORDER BY a.modified_at DESC, a.created_at DESC, a.id DESC
|
||||
LIMIT 1
|
||||
`, [screenSlug]);
|
||||
return normalizeAnnouncementRow(rows[0] || null);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
ANNOUNCEMENT_TYPES,
|
||||
ANNOUNCEMENT_COLORS,
|
||||
ANNOUNCEMENT_ICONS: ANNOUNCEMENT_ICON_KEYS,
|
||||
DEFAULT_ANNOUNCEMENT_ICON,
|
||||
normalizeAnnouncementType,
|
||||
normalizeAnnouncementColor,
|
||||
normalizeAnnouncementIcon: normalizeAnnouncementIconFromConfig,
|
||||
normalizeAnnouncementShortLabel,
|
||||
buildAnnouncementPayload,
|
||||
fetchAnnouncementById,
|
||||
fetchAnnouncementsPage,
|
||||
fetchActiveAnnouncement
|
||||
};
|
||||
Reference in New Issue
Block a user