SHA256
Обновить каналы и перенести черновики
This commit is contained in:
+1
-1
@@ -1,2 +1,2 @@
|
||||
client.version=1.12.3
|
||||
client.version=1.12.4
|
||||
server.version=1.10.1
|
||||
|
||||
@@ -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)` убраны только из формы создания новой записи. Их константы и обработчики чтения сохранены для совместимости со старыми данными.
|
||||
@@ -1217,9 +1217,6 @@ function openAddMessageModal({ channelName, onSubmit, navigate, isActive = () =>
|
||||
<div class="channel-message-tools">
|
||||
<select id="channel-message-type" class="input channel-message-type-select">
|
||||
<option value="${10}">Пост</option>
|
||||
<option value="${MSG_SUBTYPE_TEXT_EXERCISE}">Упражнение</option>
|
||||
<option value="${MSG_SUBTYPE_TEXT_SERVICE}">Услуга</option>
|
||||
<option value="${MSG_SUBTYPE_TEXT_COURSE}">Курс</option>
|
||||
<option value="${MSG_SUBTYPE_TEXT_ENTRYPOINT}">Оглавление канала</option>
|
||||
</select>
|
||||
<button class="secondary-btn attachment-trigger-btn" id="channel-message-attach" type="button" aria-label="Добавить вложение" title="Добавить вложение">▣ 📎</button>
|
||||
@@ -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) {
|
||||
if (!apiData?.isOwnChannel && !isStoriesChannel(apiData?.channel)) {
|
||||
if (apiData?.isSubscribed) {
|
||||
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,
|
||||
action: () => unsubscribeFromChannel(apiData),
|
||||
});
|
||||
} else {
|
||||
items.push({
|
||||
label: 'Подписаться на канал',
|
||||
action: () => subscribeToChannel(apiData),
|
||||
});
|
||||
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, 'Не удалось отписаться от канала.'));
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user