From 9f77c54955d8c387dc482e2f0c2d146c9272265aad37b92e6fd1cf0837be62e0 Mon Sep 17 00:00:00 2001 From: AidarKC Date: Wed, 9 Sep 2026 15:17:31 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20=D0=BA=D0=B0=D0=BD=D0=B0=D0=BB=D1=8B=20=D0=B8=20=D0=BF?= =?UTF-8?q?=D0=B5=D1=80=D0=B5=D0=BD=D0=B5=D1=81=D1=82=D0=B8=20=D1=87=D0=B5?= =?UTF-8?q?=D1=80=D0=BD=D0=BE=D0=B2=D0=B8=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- VERSION.properties | 2 +- ...Черновик_CHANNEL_MEMBERSHIP_для_каналов.md | 0 .../Черновик_TEXT_CHANNEL_META_для_каналов.md | 0 ...олнений_каналов_и_типов_сообщений_SHiNE.md | 0 ...формата_вложений_в_сообщениях_блокчейна.md | 0 docs/закомментировано/Личный_дневник.md | 68 ++++++++ shine-UI/js/pages/channel-view.js | 165 +++++++++--------- shine-UI/js/pages/channels-list.js | 18 +- 8 files changed, 168 insertions(+), 85 deletions(-) rename Черновик_CHANNEL_MEMBERSHIP_для_каналов.md => docs/Черновики/Черновик_CHANNEL_MEMBERSHIP_для_каналов.md (100%) rename Черновик_TEXT_CHANNEL_META_для_каналов.md => docs/Черновики/Черновик_TEXT_CHANNEL_META_для_каналов.md (100%) rename Черновик_дополнений_каналов_и_типов_сообщений_SHiNE.md => docs/Черновики/Черновик_дополнений_каналов_и_типов_сообщений_SHiNE.md (100%) rename Черновик_формата_вложений_в_сообщениях_блокчейна.md => docs/Черновики/Черновик_формата_вложений_в_сообщениях_блокчейна.md (100%) create mode 100644 docs/закомментировано/Личный_дневник.md diff --git a/VERSION.properties b/VERSION.properties index a9b1d142..306b82aa 100644 --- a/VERSION.properties +++ b/VERSION.properties @@ -1,2 +1,2 @@ -client.version=1.12.3 +client.version=1.12.4 server.version=1.10.1 diff --git a/Черновик_CHANNEL_MEMBERSHIP_для_каналов.md b/docs/Черновики/Черновик_CHANNEL_MEMBERSHIP_для_каналов.md similarity index 100% rename from Черновик_CHANNEL_MEMBERSHIP_для_каналов.md rename to docs/Черновики/Черновик_CHANNEL_MEMBERSHIP_для_каналов.md diff --git a/Черновик_TEXT_CHANNEL_META_для_каналов.md b/docs/Черновики/Черновик_TEXT_CHANNEL_META_для_каналов.md similarity index 100% rename from Черновик_TEXT_CHANNEL_META_для_каналов.md rename to docs/Черновики/Черновик_TEXT_CHANNEL_META_для_каналов.md diff --git a/Черновик_дополнений_каналов_и_типов_сообщений_SHiNE.md b/docs/Черновики/Черновик_дополнений_каналов_и_типов_сообщений_SHiNE.md similarity index 100% rename from Черновик_дополнений_каналов_и_типов_сообщений_SHiNE.md rename to docs/Черновики/Черновик_дополнений_каналов_и_типов_сообщений_SHiNE.md diff --git a/Черновик_формата_вложений_в_сообщениях_блокчейна.md b/docs/Черновики/Черновик_формата_вложений_в_сообщениях_блокчейна.md similarity index 100% rename from Черновик_формата_вложений_в_сообщениях_блокчейна.md rename to docs/Черновики/Черновик_формата_вложений_в_сообщениях_блокчейна.md diff --git a/docs/закомментировано/Личный_дневник.md b/docs/закомментировано/Личный_дневник.md new file mode 100644 index 00000000..6da008e5 --- /dev/null +++ b/docs/закомментировано/Личный_дневник.md @@ -0,0 +1,68 @@ +# Личный дневник — временно отключён в UI + +Статус: **закомментировано / скрыто из интерфейса**. + +Личный дневник SHiNE не удалён из протокола и серверной части. На текущем этапе он только перестал отображаться пользователю в списке каналов. + +## Что это за функция + +«Дневник» — виртуальный персональный канал пользователя. Он формируется не как обычный канал с отдельной цепочкой постов, а собирается сервером из `STATUS_ACTION` записей пользователя. + +Основной read API: + +- `GetPersonalDiary` + +Связанная документация: + +- `docs/API/06_Channels_Read_API.md` — раздел `GetPersonalDiary`; +- `docs/API/09_Operations_Index.md` — операция `GetPersonalDiary`; +- `docs/Blockchain/15_STATUS_ACTION_Blocks.md` — действия, из которых собирается дневник; +- `docs/Blockchain/CHANGELOG.md` — история добавления функции. + +## Что отключено сейчас + +В `shine-UI/js/pages/channels-list.js` отключён запрос `authService.getPersonalDiary(...)` при построении списка каналов. + +Код оставлен рядом в комментариях, а в `mapApiFeed(...)` передаётся `diaryPayload = null`. Благодаря этому карточка «Дневник» не появляется в разделе каналов. + +## Что намеренно НЕ удалено + +Чтобы не ломать обратную совместимость и сохранить возможность вернуть функцию позже, оставлены: + +- серверная операция `GetPersonalDiary`; +- `authService.getPersonalDiary(...)`; +- обработка diary-route в `channel-view.js`; +- форматы `STATUS_ACTION`; +- чтение старых типов сообщений и действий. + +То есть функция сохранена технически, но скрыта из обычной навигации. + +## Как вернуть дневник + +В `shine-UI/js/pages/channels-list.js` вернуть получение `diaryPayload`: + +```js +let diaryPayload = null; +try { + diaryPayload = await authService.getPersonalDiary(state.session.login, 200, 'asc'); +} catch { + diaryPayload = null; +} +``` + +и убрать временное: + +```js +const diaryPayload = null; +``` + +После этого `mapApiFeed(...)` снова сможет добавить виртуальную карточку дневника в список каналов. + +## Связанные типы контента + +В интерфейсе создания новой записи канала сейчас доступны только: + +- `POST (10)` — Пост; +- `TEXT_ENTRYPOINT (100)` — Оглавление канала. + +Варианты `TEXT_EXERCISE (110)`, `TEXT_SERVICE (120)` и `TEXT_COURSE (130)` убраны только из формы создания новой записи. Их константы и обработчики чтения сохранены для совместимости со старыми данными. diff --git a/shine-UI/js/pages/channel-view.js b/shine-UI/js/pages/channel-view.js index 0c6f59cc..6fe5a6cd 100644 --- a/shine-UI/js/pages/channel-view.js +++ b/shine-UI/js/pages/channel-view.js @@ -1217,9 +1217,6 @@ function openAddMessageModal({ channelName, onSubmit, navigate, isActive = () =>
@@ -2211,9 +2208,9 @@ function renderBody(screen, navigate, routeKey, channelData, handlers) { } else if (channelData.isOwnChannel) { screen.append(feed, addMessageButton); } else if (!channelData.isSubscribed && !isStoriesChannel(channelData.channel)) { - screen.append(actionButton, feed, backButton); + screen.append(feed, actionButton); } else { - screen.append(feed, backButton); + screen.append(feed); } const hasPendingScrollTarget = pendingScrollByRoute.has(routeKey); @@ -2319,32 +2316,19 @@ export function render({ navigate, route, chrome }) { const items = [ { label: 'О канале', action: () => { if (aboutRoute) navigate(aboutRoute); } }, ]; - if (apiData?.isSubscribed && !apiData?.isOwnChannel) { - items.push({ - label: 'Отписаться от канала', - danger: true, - action: async () => { - try { - const { login, storagePwd } = requireSigningSession(); - await authService.addBlockFollowChannel({ - login, - storagePwd, - targetBlockchainName: apiData.selector.ownerBlockchainName, - targetBlockNumber: apiData.selector.channelRootBlockNumber, - targetBlockHashHex: apiData.selector.channelRootBlockHash, - unfollow: true, - }); - if (disposed) return; - const feed = await authService.listSubscriptionsFeed(login, 200); - if (disposed) return; - setChannelsFeed(feed, state.channelsIndex); - showToast('Вы отписались от канала'); - void refresh(); - } catch (error) { - showStatus(toUserMessage(error, 'Не удалось отписаться от канала.')); - } - }, - }); + if (!apiData?.isOwnChannel && !isStoriesChannel(apiData?.channel)) { + if (apiData?.isSubscribed) { + items.push({ + label: 'Отписаться от канала', + danger: true, + action: () => unsubscribeFromChannel(apiData), + }); + } else { + items.push({ + label: 'Подписаться на канал', + action: () => subscribeToChannel(apiData), + }); + } } return items; }, @@ -2373,6 +2357,77 @@ export function render({ navigate, route, chrome }) { return { login, storagePwd }; }; + const subscribeToChannel = async (apiData, event = null) => { + animatePress(event?.currentTarget); + try { + const { login, storagePwd } = requireSigningSession(); + if (!apiData?.selector) throw new Error('Не удалось определить канал для подписки.'); + const targetName = `${apiData.channel?.ownerName || 'user'}/${apiData.channel?.name || 'channel'}`; + const ok = window.confirm(`Подписаться на канал ${targetName}?`); + if (!ok) return; + + await authService.addBlockFollowChannel({ + login, + storagePwd, + targetBlockchainName: apiData.selector.ownerBlockchainName, + targetBlockNumber: apiData.selector.channelRootBlockNumber, + targetBlockHashHex: apiData.selector.channelRootBlockHash, + unfollow: false, + }); + if (disposed) return; + + const readSettingKey = buildChannelSettingsKey( + apiData.channel?.ownerBlockchainName || apiData.selector?.ownerBlockchainName, + apiData.channel?.name || apiData.channel?.channelName, + ); + try { + await authService.upsertUserSetting({ + login, + settingType: 1, + settingKey: readSettingKey, + timeMs: Date.now(), + valueText: '', + valueNum: Math.max(0, Number(apiData.messagesCount || 0)), + storagePwd, + }); + } catch (readStateError) { + showStatus(toUserMessage(readStateError, 'Подписка выполнена, но не удалось сохранить, сколько сообщений уже прочитано.')); + } + + const feed = await authService.listSubscriptionsFeed(login, 200); + if (disposed) return; + setChannelsFeed(feed, state.channelsIndex); + softHaptic(15); + showToast('Подписка на канал выполнена'); + void refresh(); + } catch (error) { + showStatus(toUserMessage(error, 'Не удалось подписаться на канал.')); + } + }; + + const unsubscribeFromChannel = async (apiData) => { + try { + const { login, storagePwd } = requireSigningSession(); + if (!apiData?.selector) throw new Error('Не удалось определить канал для отписки.'); + await authService.addBlockFollowChannel({ + login, + storagePwd, + targetBlockchainName: apiData.selector.ownerBlockchainName, + targetBlockNumber: apiData.selector.channelRootBlockNumber, + targetBlockHashHex: apiData.selector.channelRootBlockHash, + unfollow: true, + }); + if (disposed) return; + const feed = await authService.listSubscriptionsFeed(login, 200); + if (disposed) return; + setChannelsFeed(feed, state.channelsIndex); + showToast('Вы отписались от канала'); + void refresh(); + } catch (error) { + showStatus(toUserMessage(error, 'Не удалось отписаться от канала.')); + } + }; + const onToggleLike = async (messageRef, action) => { const actionKey = makeReactionActionKey(messageRef); if (!actionKey) { @@ -2774,53 +2829,7 @@ export function render({ navigate, route, chrome }) { throw new Error(toUserMessage(error, 'Не удалось изменить сообщение.')); } }, - onSubscribeChannel: async (event) => { - animatePress(event?.currentTarget); - try { - const { login, storagePwd } = requireSigningSession(); - if (!apiData.selector) throw new Error('Не удалось определить канал для подписки.'); - const targetName = `${apiData.channel?.ownerName || 'user'}/${apiData.channel?.name || 'channel'}`; - const ok = window.confirm(`Подписаться на канал ${targetName}?`); - if (!ok) return; - - await authService.addBlockFollowChannel({ - login, - storagePwd, - targetBlockchainName: apiData.selector.ownerBlockchainName, - targetBlockNumber: apiData.selector.channelRootBlockNumber, - targetBlockHashHex: apiData.selector.channelRootBlockHash, - unfollow: false, - }); - if (disposed) return; - - const readSettingKey = buildChannelSettingsKey( - apiData.channel?.ownerBlockchainName || apiData.selector?.ownerBlockchainName, - apiData.channel?.name || apiData.channel?.channelName, - ); - try { - await authService.upsertUserSetting({ - login, - settingType: 1, - settingKey: readSettingKey, - timeMs: Date.now(), - valueText: '', - valueNum: Math.max(0, Number(apiData.messagesCount || 0)), - storagePwd, - }); - } catch (readStateError) { - showStatus(toUserMessage(readStateError, 'Подписка выполнена, но не удалось сохранить, сколько сообщений уже прочитано.')); - } - - const feed = await authService.listSubscriptionsFeed(login, 200); - if (disposed) return; - setChannelsFeed(feed, state.channelsIndex); - softHaptic(15); - showToast('Подписка на канал выполнена'); - void refresh(); - } catch (error) { - showStatus(toUserMessage(error, 'Не удалось подписаться на канал.')); - } - }, + onSubscribeChannel: (event) => subscribeToChannel(apiData, event), }); } catch (error) { if (disposed || seq !== refreshSeq) return; diff --git a/shine-UI/js/pages/channels-list.js b/shine-UI/js/pages/channels-list.js index a0a6d119..08188241 100644 --- a/shine-UI/js/pages/channels-list.js +++ b/shine-UI/js/pages/channels-list.js @@ -1067,12 +1067,18 @@ async function loadFeedAndRender({ screen, listState, contentEl, navigate }) { try { const feed = await authService.listSubscriptionsFeed(state.session.login, 200); - let diaryPayload = null; - try { - diaryPayload = await authService.getPersonalDiary(state.session.login, 200, 'asc'); - } catch { - diaryPayload = null; - } + + // FEATURE DISABLED: personal Diary is intentionally hidden from the Channels UI. + // The server/API implementation is preserved so the feature can be restored later. + // See: docs/закомментировано/Личный_дневник.md + // let diaryPayload = null; + // try { + // diaryPayload = await authService.getPersonalDiary(state.session.login, 200, 'asc'); + // } catch { + // diaryPayload = null; + // } + const diaryPayload = null; + const groups = mapApiFeed(feed, listState.notificationsState, diaryPayload); listState.channels = toListModel(groups);