diff --git a/api-chat.js b/api-chat.js
index 6d50a6b..f823b0e 100644
--- a/api-chat.js
+++ b/api-chat.js
@@ -460,7 +460,44 @@ module.exports = function(app, db, upload) {
});
});
});
+// GET /api/chat/unread-summary – сводка непрочитанных сообщений по задачам
+router.get('/api/chat/unread-summary', requireAuth, (req, res) => {
+ const userId = req.session.user.id;
+ const query = `
+ SELECT
+ m.task_id,
+ t.title,
+ COUNT(*) as unread_count
+ FROM task_chat_messages m
+ JOIN tasks t ON m.task_id = t.id
+ LEFT JOIN task_chat_reads r ON m.id = r.message_id AND r.user_id = ?
+ WHERE m.user_id != ?
+ AND m.is_deleted = 0
+ AND r.id IS NULL
+ GROUP BY m.task_id, t.title
+ ORDER BY MAX(m.created_at) DESC
+ LIMIT 50
+ `;
+
+ db.all(query, [userId, userId], (err, rows) => {
+ if (err) {
+ console.error('❌ Ошибка получения сводки непрочитанных:', err);
+ return res.status(500).json({ error: 'Ошибка получения сводки' });
+ }
+
+ const totalUnread = rows.reduce((sum, row) => sum + row.unread_count, 0);
+
+ res.json({
+ tasks: rows.map(r => ({
+ taskId: r.task_id,
+ title: r.title,
+ unreadCount: r.unread_count
+ })),
+ totalUnread
+ });
+ });
+});
// Вспомогательная функция для уведомлений участников
function notifyTaskParticipants(taskId, senderId, message) {
db.all(`
diff --git a/public/auth.js b/public/auth.js
index 80601c5..ccd8e2e 100644
--- a/public/auth.js
+++ b/public/auth.js
@@ -105,6 +105,7 @@ function reloadAllScripts() {
'profile.js',
'time-selector.js',
'openTaskChat.js',
+ 'openTaskChat2.js',
'tasks_files.js',
'navbar.js',
'chat-ui.js',
diff --git a/public/openTaskChat2.js b/public/openTaskChat2.js
new file mode 100644
index 0000000..b5cc212
--- /dev/null
+++ b/public/openTaskChat2.js
@@ -0,0 +1,130 @@
+// Показывает модальное окно со списком задач
+function showUnreadNotification(tasks) {
+ const modalHtml = `
+
+
+
+
+
В следующих задачах есть непрочитанные сообщения:
+
+ ${tasks.map(task => `
+ -
+ ${escapeHtml(task.title)} (${task.unreadCount} ${pluralize(task.unreadCount, ['новое сообщение', 'новых сообщения', 'новых сообщений'])})
+
+
+ `).join('')}
+
+
+
+
+
+ `;
+
+ let modal = document.getElementById('unread-notification-modal');
+ if (modal) modal.remove();
+
+ const container = document.createElement('div');
+ container.innerHTML = modalHtml;
+ document.body.appendChild(container);
+
+ modal = document.getElementById('unread-notification-modal');
+ modal.style.display = 'block';
+
+ // Добавляем стили (если ещё не добавлены)
+ if (!document.getElementById('unread-notification-styles')) {
+ const style = document.createElement('style');
+ style.id = 'unread-notification-styles';
+ style.textContent = `
+ #unread-notification-modal {
+ display: none;
+ position: fixed;
+ z-index: 1001;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(0,0,0,0.5);
+ animation: fadeIn 0.3s;
+ }
+ #unread-notification-modal .modal-content {
+ animation: slideIn 0.3s ease-out;
+ background-color: #fefefe;
+ margin: 5% auto;
+ border-radius: 10px;
+ box-shadow: 0 4px 20px rgba(0,0,0,0.2);
+ overflow: hidden;
+ }
+ @keyframes fadeIn {
+ from { opacity: 0; }
+ to { opacity: 1; }
+ }
+ @keyframes slideIn {
+ from { transform: translateY(-50px); opacity: 0; }
+ to { transform: translateY(0); opacity: 1; }
+ }
+ `;
+ document.head.appendChild(style);
+ }
+}
+
+function closeUnreadNotification() {
+ const modal = document.getElementById('unread-notification-modal');
+ if (modal) {
+ modal.style.display = 'none';
+ setTimeout(() => modal.remove(), 300);
+ }
+}
+
+// Вспомогательная функция для склонения
+function pluralize(count, words) {
+ const cases = [2, 0, 1, 1, 1, 2];
+ return words[(count % 100 > 4 && count % 100 < 20) ? 2 : cases[Math.min(count % 10, 5)]];
+}
+
+// Экранирование HTML в заголовке задачи (защита от XSS)
+function escapeHtml(unsafe) {
+ return unsafe
+ .replace(/&/g, "&")
+ .replace(//g, ">")
+ .replace(/"/g, """)
+ .replace(/'/g, "'");
+}
+
+// Открывает чат задачи и помечает все сообщения как прочитанные
+function openTaskAndMarkRead(taskId) {
+ closeUnreadNotification();
+ // Опционально: сразу отмечаем все сообщения прочитанными
+ fetch(`/api/chat/tasks/${taskId}/mark-read`, { method: 'POST' })
+ .catch(err => console.warn('Не удалось отметить сообщения как прочитанные', err))
+ .finally(() => openTaskChat(taskId));
+}
+
+// Проверка наличия непрочитанных сообщений
+function checkUnreadMessages() {
+ // Не беспокоим пользователя, если страница не активна
+ if (document.hidden) return;
+
+ fetch('/api/chat/unread-summary')
+ .then(response => {
+ if (response.status === 401) return null; // пользователь не авторизован
+ return response.json();
+ })
+ .then(data => {
+ if (data && data.totalUnread > 0) {
+ showUnreadNotification(data.tasks);
+ }
+ })
+ .catch(err => console.error('Ошибка проверки новых сообщений:', err));
+}
+
+// Запуск периодической проверки (раз в 5 минут)
+setInterval(checkUnreadMessages, 5 * 60 * 1000);
+
+// Проверка при загрузке страницы
+document.addEventListener('DOMContentLoaded', checkUnreadMessages);
\ No newline at end of file