From 745a0e39d7241075bae373e55937dc447bee7fffb75fae74f88589ed67de0ed0 Mon Sep 17 00:00:00 2001 From: AidarKC Date: Fri, 21 Aug 2026 13:45:49 +0400 Subject: [PATCH] Fix unread channel counts and badges --- .../JSON/handlers/channels/ChannelsReadSupport.java | 2 +- docs/API/06_Channels_Read_API.md | 3 +++ docs/API/13_User_Settings_API.md | 3 ++- shine-UI/js/pages/channel-view.js | 9 +++++++-- shine-UI/js/pages/channels-list.js | 8 +++++--- 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/channels/ChannelsReadSupport.java b/SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/channels/ChannelsReadSupport.java index 01d5e866..f0e5109f 100644 --- a/SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/channels/ChannelsReadSupport.java +++ b/SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/channels/ChannelsReadSupport.java @@ -161,7 +161,7 @@ final class ChannelsReadSupport { AND setting_key = ? LIMIT 1 """; - long lastSeen = messagesCount; + long lastSeen = 0; try (PreparedStatement ps = c.prepareStatement(sql)) { ps.setString(1, viewerLogin); ps.setInt(2, 1); diff --git a/docs/API/06_Channels_Read_API.md b/docs/API/06_Channels_Read_API.md index b2dc3d93..7996b5a1 100644 --- a/docs/API/06_Channels_Read_API.md +++ b/docs/API/06_Channels_Read_API.md @@ -23,6 +23,9 @@ 7. `GetGroupDialog` — отдает сообщения конкретного группового чата типа `200`. > На первом этапе мы **не используем курсоры** (`nextCursor`) и загружаем полные списки. +> +> `unreadCount` для канала считается по `user_settings`: +> если для пары `ownerBlockchainName/channelName` ещё нет записи, канал считается полностью непросмотренным. --- diff --git a/docs/API/13_User_Settings_API.md b/docs/API/13_User_Settings_API.md index dc51a1f9..97e22578 100644 --- a/docs/API/13_User_Settings_API.md +++ b/docs/API/13_User_Settings_API.md @@ -13,7 +13,8 @@ - `value_num = number of messages already seen in channel`; - `value_text = ''`. -Если настройки нет, канал считается просмотренным до конца, то есть unread = `0`. +Если настройки нет, канал считается полностью непросмотренным, то есть unread = `messagesCount`. +После первого открытия канала UI отправляет актуальный курсор и тем самым переводит unread к `0`. ## 2. Структура записи diff --git a/shine-UI/js/pages/channel-view.js b/shine-UI/js/pages/channel-view.js index b30b07dc..da9f549e 100644 --- a/shine-UI/js/pages/channel-view.js +++ b/shine-UI/js/pages/channel-view.js @@ -237,6 +237,7 @@ function buildThreadRoute(messageRef, selector) { function buildChannelSettingsKey(ownerBlockchainName, channelName) { const ownerBch = String(ownerBlockchainName || '').trim(); const name = String(channelName || '').trim(); + if (!ownerBch || !name) return ''; return `${ownerBch}/${name}`; } @@ -1370,7 +1371,7 @@ async function loadFromApi(route, channelId) { throw new Error('Канал не найден.'); } unreadCount = Number(channel?.unreadCount || 0); - messagesCount = Number(channel?.messagesCount || mergedMessages.length || 0); + messagesCount = Number(channel?.messagesCount || 0); selector = { ownerBlockchainName: String(channel.channel.ownerBlockchainName), channelRootBlockNumber: Number(channel.channel.channelRoot.blockNumber), @@ -1447,6 +1448,7 @@ async function loadFromApi(route, channelId) { return { channel: { name: payload.channel?.channelName || 'неизвестный канал', + ownerBlockchainName: String(payload.channel?.ownerBlockchainName || '').trim(), displayTitle: String(payload.channel?.displayName || payload.channel?.channelName || 'неизвестный канал').trim(), displayName: `${ownerLogin || 'неизвестно'}/${payload.channel?.channelName || 'неизвестный канал'}`, description: String(payload.channel?.channelDescription || '').trim(), @@ -2318,7 +2320,10 @@ export function render({ navigate, route, chrome }) { const apiData = await loadFromApi(route, channelId); activeSelector = apiData?.selector || null; const lastSeenCount = Number(apiData?.messagesCount || (Array.isArray(apiData?.posts) ? apiData.posts.length : 0) || 0); - const settingKey = buildChannelSettingsKey(apiData?.channel?.ownerBlockchainName, apiData?.channel?.name); + const settingKey = buildChannelSettingsKey( + apiData?.channel?.ownerBlockchainName || apiData?.selector?.ownerBlockchainName, + apiData?.channel?.name || apiData?.channel?.channelName, + ); if (settingKey && state.session.login && state.session.storagePwdInMemory) { void authService.upsertUserSetting({ login: state.session.login, diff --git a/shine-UI/js/pages/channels-list.js b/shine-UI/js/pages/channels-list.js index 10013350..77acd5dc 100644 --- a/shine-UI/js/pages/channels-list.js +++ b/shine-UI/js/pages/channels-list.js @@ -1230,8 +1230,6 @@ function renderListContent({ screen, container, listState, navigate, refreshFeed const count = document.createElement('span'); count.className = 'unread channel-row-count'; const unreadCount = Number(channel.unreadCount || 0); - count.textContent = unreadCount > 0 ? String(unreadCount) : ''; - count.classList.toggle('is-empty', unreadCount <= 0); if (!isGuest) { const menuButton = document.createElement('button'); @@ -1261,7 +1259,11 @@ function renderListContent({ screen, container, listState, navigate, refreshFeed }); controls.append(menuButton); } - controls.append(time, count); + controls.append(time); + if (unreadCount > 0) { + count.textContent = unreadCount > 99 ? '99+' : String(unreadCount); + controls.append(count); + } row.append(avatar, main, controls); row.addEventListener('click', () => {