SHA256
Compare commits
1
Commits
| Author | SHA256 | Date | |
|---|---|---|---|
|
|
85d90a7f95 |
+4
-1
@@ -82,7 +82,10 @@ public class Net_ListSubscriptionsFeed_Handler implements JsonMessageHandler {
|
|||||||
|
|
||||||
row.setChannel(channelRef);
|
row.setChannel(channelRef);
|
||||||
row.setMessagesCount(ChannelsReadSupport.countPosts(c, key.ownerBch, key.rootNumber));
|
row.setMessagesCount(ChannelsReadSupport.countPosts(c, key.ownerBch, key.rootNumber));
|
||||||
row.setUnreadCount(ChannelsReadSupport.countUnreadMessages(c, viewerLogin, key.ownerBch, meta.channelName, row.getMessagesCount()));
|
boolean ownChannel = key.ownerLogin != null && key.ownerLogin.equalsIgnoreCase(viewerLogin);
|
||||||
|
row.setUnreadCount(ownChannel
|
||||||
|
? 0
|
||||||
|
: ChannelsReadSupport.countUnreadMessages(c, viewerLogin, key.ownerBch, meta.channelName, row.getMessagesCount()));
|
||||||
|
|
||||||
ChannelsReadSupport.PostBlock lastPost = ChannelsReadSupport.loadLastPost(c, key.ownerBch, key.rootNumber);
|
ChannelsReadSupport.PostBlock lastPost = ChannelsReadSupport.loadLastPost(c, key.ownerBch, key.rootNumber);
|
||||||
if (lastPost != null) {
|
if (lastPost != null) {
|
||||||
|
|||||||
@@ -24,8 +24,9 @@
|
|||||||
|
|
||||||
> На первом этапе мы **не используем курсоры** (`nextCursor`) и загружаем полные списки.
|
> На первом этапе мы **не используем курсоры** (`nextCursor`) и загружаем полные списки.
|
||||||
>
|
>
|
||||||
> `unreadCount` для канала считается по `user_settings`:
|
> `unreadCount` для канала считается по `user_settings`.
|
||||||
> если для пары `ownerBlockchainName/channelName` ещё нет записи, канал считается полностью прочитанным, а UI при первом открытии записывает текущий курсор чтения.
|
> Для собственных каналов владельцу всегда возвращается `unreadCount = 0`, чтобы его собственные публикации не становились «новыми» для него самого.
|
||||||
|
> Если для пары `ownerBlockchainName/channelName` ещё нет записи, канал временно считается полностью прочитанным; UI при загрузке списка каналов создаёт baseline на текущем `messagesCount`. После этого новые публикации увеличивают `unreadCount` до продвижения курсора чтения.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -393,22 +393,11 @@ function createChannelReadTracker({
|
|||||||
}
|
}
|
||||||
window.addEventListener('resize', onResize);
|
window.addEventListener('resize', onResize);
|
||||||
|
|
||||||
const initialSyncRequired = canWrite && unreadCount <= 0 && safeMessagesCount >= 0;
|
if (canWrite) {
|
||||||
if (initialSyncRequired) {
|
|
||||||
void authService.upsertUserSetting({
|
|
||||||
login,
|
|
||||||
settingType: 1,
|
|
||||||
settingKey,
|
|
||||||
timeMs: Date.now(),
|
|
||||||
valueText: '',
|
|
||||||
valueNum: safeMessagesCount,
|
|
||||||
storagePwd,
|
|
||||||
}).catch(() => {});
|
|
||||||
persistedSeenCount = safeMessagesCount;
|
|
||||||
desiredSeenCount = safeMessagesCount;
|
desiredSeenCount = safeMessagesCount;
|
||||||
} else {
|
void flush();
|
||||||
window.setTimeout(() => measure(), 120);
|
|
||||||
}
|
}
|
||||||
|
window.setTimeout(() => measure(), 120);
|
||||||
|
|
||||||
const cleanup = () => {
|
const cleanup = () => {
|
||||||
disposed = true;
|
disposed = true;
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ const CHANNEL_TYPE_STORIES = 0;
|
|||||||
const CHANNEL_TYPE_PERSONAL = 100;
|
const CHANNEL_TYPE_PERSONAL = 100;
|
||||||
const DIARY_CHANNEL_NAME = 'diary';
|
const DIARY_CHANNEL_NAME = 'diary';
|
||||||
const DIARY_DISPLAY_NAME = 'Дневник';
|
const DIARY_DISPLAY_NAME = 'Дневник';
|
||||||
|
const CHANNEL_READ_SETTING_TYPE = 1;
|
||||||
|
|
||||||
const CHANNELS_VIEW_ALL = 'all';
|
const CHANNELS_VIEW_ALL = 'all';
|
||||||
const CHANNELS_VIEW_OWNED = 'owned';
|
const CHANNELS_VIEW_OWNED = 'owned';
|
||||||
@@ -111,6 +112,58 @@ function isVisibleChannelSummary(summary) {
|
|||||||
return !!ownerLogin && !!channelName;
|
return !!ownerLogin && !!channelName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function channelReadSettingKey(summary) {
|
||||||
|
const ownerBch = String(summary?.channel?.ownerBlockchainName || '').trim();
|
||||||
|
const channelName = String(summary?.channel?.channelName || '').trim();
|
||||||
|
if (!ownerBch || !channelName) return '';
|
||||||
|
return `${ownerBch}/${channelName}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ensureChannelReadBaselines(feed) {
|
||||||
|
const login = String(state.session.login || '').trim();
|
||||||
|
const storagePwd = state.session.storagePwdInMemory;
|
||||||
|
if (!login || !storagePwd) return;
|
||||||
|
|
||||||
|
let settingsPayload;
|
||||||
|
try {
|
||||||
|
settingsPayload = await authService.listUserSettings(login);
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const existing = new Set(
|
||||||
|
(Array.isArray(settingsPayload?.settings) ? settingsPayload.settings : [])
|
||||||
|
.filter((item) => Number(item?.setting_type) === CHANNEL_READ_SETTING_TYPE)
|
||||||
|
.map((item) => String(item?.setting_key || '').trim())
|
||||||
|
.filter(Boolean),
|
||||||
|
);
|
||||||
|
|
||||||
|
const summaries = [
|
||||||
|
...(Array.isArray(feed?.followedUsersChannels) ? feed.followedUsersChannels : []),
|
||||||
|
...(Array.isArray(feed?.followedChannels) ? feed.followedChannels : []),
|
||||||
|
].filter(isVisibleChannelSummary);
|
||||||
|
|
||||||
|
for (const summary of summaries) {
|
||||||
|
const settingKey = channelReadSettingKey(summary);
|
||||||
|
if (!settingKey || existing.has(settingKey)) continue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await authService.upsertUserSetting({
|
||||||
|
login,
|
||||||
|
settingType: CHANNEL_READ_SETTING_TYPE,
|
||||||
|
settingKey,
|
||||||
|
timeMs: Date.now(),
|
||||||
|
valueText: '',
|
||||||
|
valueNum: Math.max(0, Number(summary?.messagesCount || 0)),
|
||||||
|
storagePwd,
|
||||||
|
});
|
||||||
|
existing.add(settingKey);
|
||||||
|
} catch {
|
||||||
|
// Не ломаем экран каналов из-за фоновой инициализации read-state.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function avatarLetterFromName(name = '') {
|
function avatarLetterFromName(name = '') {
|
||||||
const first = Array.from(String(name || '').trim())[0] || '#';
|
const first = Array.from(String(name || '').trim())[0] || '#';
|
||||||
return first.toUpperCase();
|
return first.toUpperCase();
|
||||||
@@ -1323,6 +1376,7 @@ async function loadFeedAndRender({ screen, listState, contentEl, navigate }) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const feed = await authService.listSubscriptionsFeed(state.session.login, 200);
|
const feed = await authService.listSubscriptionsFeed(state.session.login, 200);
|
||||||
|
void ensureChannelReadBaselines(feed);
|
||||||
let diaryPayload = null;
|
let diaryPayload = null;
|
||||||
try {
|
try {
|
||||||
diaryPayload = await authService.getPersonalDiary(state.session.login, 200, 'asc');
|
diaryPayload = await authService.getPersonalDiary(state.session.login, 200, 'asc');
|
||||||
|
|||||||
@@ -3030,6 +3030,15 @@ export class AuthService {
|
|||||||
return response.payload || {};
|
return response.payload || {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async listUserSettings(login) {
|
||||||
|
const cleanLogin = String(login || '').trim();
|
||||||
|
if (!cleanLogin) throw new Error('Не передан login');
|
||||||
|
|
||||||
|
const response = await this.ws.request('ListUserSettings', { login: cleanLogin });
|
||||||
|
if (response.status !== 200) throw opError('ListUserSettings', response);
|
||||||
|
return response.payload || {};
|
||||||
|
}
|
||||||
|
|
||||||
async upsertUserSetting({
|
async upsertUserSetting({
|
||||||
login,
|
login,
|
||||||
settingType,
|
settingType,
|
||||||
|
|||||||
Reference in New Issue
Block a user