Обновить каналы и перенести черновики

This commit is contained in:
AidarKC
2026-09-09 15:17:31 +03:00
parent f827c3e493
commit 9f77c54955
8 changed files with 168 additions and 85 deletions
@@ -0,0 +1,60 @@
# Черновик CHANNEL_MEMBERSHIP для каналов
## Статус документа
Этот файл является отдельным отложенным черновиком.
Важно:
- тема временно вынесена из основной первой итерации;
- в текущую реализацию не входит;
- документ нужен, чтобы не потерять уже согласованные мысли и вернуться к ним позже.
## Зачем вынесено отдельно
`CHANNEL_MEMBERSHIP` пока решено не делать вместе с `TEXT` и `STATUS_ACTION`, чтобы:
- не перегружать текущую реализацию;
- не смешивать две разные задачи;
- спокойно завершить первую итерацию по контенту и статусам;
- вернуться к membership позже отдельным этапом.
## Отложенная схема ссылок
На текущий момент в отложенный черновик заносится такая схема:
- `JOIN_REQUEST` ссылается на root-блок канала;
- `JOIN_ACCEPTED` ссылается на `JOIN_REQUEST`;
- `LEFT` ссылается на root-блок канала;
- `REMOVED` ссылается на последнее membership-событие этого участника в этом канале, лучше всего на `JOIN_ACCEPTED`.
## Предварительные подтипы
Если тема будет возвращена в реализацию, предварительно рассматриваются:
- `subType=10``CHANNEL_JOIN_REQUEST`
- `subType=20``CHANNEL_JOIN_ACCEPTED`
- `subType=30``CHANNEL_LEFT`
- `subType=40``CHANNEL_REMOVED`
## Что ещё нужно будет отдельно решить позже
Перед возвратом к теме нужно будет отдельно утвердить:
- точный байтовый формат `CHANNEL_MEMBERSHIP`;
- в чьём блокчейне пишутся membership-события;
- политику прав доступа:
- кто может писать `JOIN_ACCEPTED`;
- кто может писать `REMOVED`;
- кто и как подтверждает актуальный состав канала;
- как сервер строит read-model текущего состава канала;
- как UI показывает pending-заявки, принятых участников, вышедших и исключённых.
## Краткий итог
`CHANNEL_MEMBERSHIP` не отменён, а именно отложен.
Следующий рекомендуемый шаг:
- сначала завершить первую итерацию `TEXT + STATUS_ACTION`;
- потом отдельным этапом вернуться к membership-логике каналов.
@@ -0,0 +1,297 @@
# Черновик `TEXT_CHANNEL_META` для каналов SHiNE
## Статус документа
Этот файл является рабочим черновиком и не считается финальной спецификацией.
Важно:
- это не окончательный формат;
- точная версия должна быть зафиксирована отдельно при начале реализации;
- до реализации допустимы изменения синтаксиса, ограничений и полей БД;
- документ временно лежит в корне проекта как согласованный draft.
## Назначение
Нужен специальный скрытый тип текстового сообщения канала, который:
- технически хранится в блокчейне как `TEXT`-блок;
- не показывается пользователям как обычное сообщение ленты;
- может писать только владелец канала;
- не редактируется;
- задаёт полное текущее состояние метаданных канала.
Через этот блок должны обновляться:
- человекочитаемое заглавие канала;
- аватар канала;
- описание канала.
## Место в формате блокчейна
- `msg_type = 1`
- `subType = 70`
- имя подтипа: `TEXT_CHANNEL_META`
Этот подтип относится к текстовым блокам, но трактуется как скрытое техническое сообщение канала.
## Общая модель применения
`TEXT_CHANNEL_META`:
- пишется в линию конкретного канала;
- задаёт полный снимок метаданных;
- при чтении канала сервер берёт последний валидный `TEXT_CHANNEL_META` в линии;
- более старые `TEXT_CHANNEL_META` остаются в истории блокчейна, но не участвуют в вычислении текущего состояния;
- если meta-блок не найден, используются базовые данные канала без override-полей.
## Общий формат текста
Внутри `TEXT_CHANNEL_META` текст строится так:
1. в начале идут технические теги `<SHiNE:...>`;
2. после тегов идёт обычный UTF-8 текст;
3. этот хвостовой текст считается описанием канала.
Пример:
```text
<SHiNE:title;v=1;Моё имя канала>
<SHiNE:avatar;v=1;size=248193;sha256=3f2c8a...;ar=AbCdEf...>
Тут описание канала.
```
## Поддерживаемые теги
### 1. Тег заглавия
Формат:
```text
<SHiNE:title;v=1;Моё имя канала>
```
Смысл:
- задаёт красивое отображаемое имя канала;
- хранится как обычный UTF-8 текст;
- это не технический slug и не системное имя.
### 2. Тег аватара
Формат:
```text
<SHiNE:avatar;v=1;size=248193;sha256=3f2c8a...;ar=AbCdEf...>
```
Смысл:
- задаёт аватар канала;
- `ar` хранит только `txId`, без полного URL;
- конкретный gateway/URL уже выбирает клиент или сервер.
## Правила порядка
- теги должны идти только в начале текста;
- после начала обычного описания новые теги больше не допускаются;
- рекомендуемый порядок:
1. `title`
2. `avatar`
3. текст описания
- каждый тег допустим не более одного раза.
## Правила отсутствующих полей
- если `title` отсутствует, используется обычное техническое имя канала;
- если `avatar` отсутствует, аватар у канала отсутствует;
- если текста после тегов нет, описание считается пустым;
- если тегов нет вообще, но текст есть, это означает пустые `title` и `avatar`, а текст используется как описание.
## Полный снимок состояния
Каждый новый `TEXT_CHANNEL_META` задаёт полное состояние канала целиком.
Это значит:
- новый блок не дозаполняет старые поля;
- новый блок не наследует значения из прошлого meta-блока;
- если в новом блоке нет `title`, то красивое имя сбрасывается к обычному имени канала;
- если в новом блоке нет `avatar`, аватар сбрасывается;
- если в новом блоке нет текста описания, описание становится пустым.
## Ограничения для тега `title`
`title` хранится как UTF-8 строка, но для упрощения формата запрещаются следующие символы:
- `<`
- `>`
- `;`
- табуляция
- перевод строки
- `\0`
Дополнительно:
- максимальная длина `title``50` символов;
- допускаются обычные Unicode-символы, включая кириллицу, латиницу, эмодзи и декоративные символы, если они не нарушают запреты выше.
## Ограничения для описания
- описание хранится как обычный UTF-8 текст;
- максимальная длина — `250` символов.
Если описание длиннее лимита, meta-блок должен считаться невалидным.
## Ограничения для тега `avatar`
Обязательные поля тега:
- `v`
- `size`
- `sha256`
- `ar`
Правила:
- `size` — размер файла в байтах;
- `sha256` — SHA-256 файла в hex;
- `sha256` должен иметь длину ровно `64` hex-символа;
- `ar` — только `txId` Arweave;
- длина `ar` должна соответствовать длине идентификатора Arweave-транзакции;
- имя файла в meta-теге аватара не хранится.
## Валидация блока
Блок `TEXT_CHANNEL_META` считается валидным только если одновременно соблюдены все условия:
- теги находятся строго в начале текста;
- каждый тип тега встречается не более одного раза;
- `title` удовлетворяет ограничениям;
- `avatar`-тег имеет все обязательные поля и корректные значения;
- длина описания не превышает лимит.
Если блок невалиден:
- весь meta-блок не применяется целиком;
- сервер не должен частично брать из него только отдельные поля.
## Что именно хранится в текущем состоянии канала
Текущее вычисленное состояние канала должно включать:
- техническое имя канала;
- красивое отображаемое имя;
- описание;
- `ava_ar`;
- `ava_sha256`;
- `ava_size`;
- время последнего meta-обновления.
## База данных
Базовую таблицу каналов предлагается не менять концептуально, а расширить существующую модель хранения каналов.
Текущая основа уже есть в таблице:
- `channel_names_state`
В неё предлагается добавить поля:
- `ava_ar`
- `ava_sha256`
- `ava_size`
- `meta_updated_at_ms`
При этом:
- техническое имя канала продолжает храниться отдельно;
- красивое имя хранится в `display_name`;
- описание хранится в `channel_description`.
## Поведение API и чтения каналов
Обычным пользователям `TEXT_CHANNEL_META` не должен приходить как обычное сообщение ленты.
Это означает:
- в `GetChannelMessages` такие блоки не показываются как сообщения;
- в обычных списках каналов клиент получает уже вычисленное текущее состояние канала;
- в debug/raw-чтении сам блок может оставаться доступным как часть блокчейна.
## UI-заметки
### 1. В обычной ленте
`TEXT_CHANNEL_META` не показывается как обычное текстовое сообщение.
### 2. Системная карточка
UI может показывать специальную системную карточку:
- для текущего состояния канала;
- для стартового состояния при создании канала.
### 3. Что показывать по нажатию
По нажатию на системную карточку нужно показывать полную информацию:
- дата и время;
- заглавие;
- аватар;
- описание.
### 4. История UI
На первом этапе UI показывает только текущую системную карточку состояния.
Полная история всех прошлых meta-обновлений в UI на этом этапе не требуется.
## Поведение при создании канала
Для UI полезно также иметь отдельный визуальный системный блок в начале канала:
- «канал создан»;
- по нажатию можно показать стартовые данные канала;
- если на момент создания уже есть заглавие/ава/описание, UI показывает именно их.
Это UI-заметка и не требует отдельного нового блокчейн-формата поверх `TEXT_CHANNEL_META`.
## Краткое резюме согласованной схемы
- `msg_type=1`
- `subType=70`
- имя: `TEXT_CHANNEL_META`
- это скрытый технический текстовый блок канала
- порядок: теги в начале, потом описание
- теги:
- `<SHiNE:title;v=1;...>`
- `<SHiNE:avatar;v=1;size=...;sha256=...;ar=...>`
- `title`:
- UTF-8
- максимум `50` символов
- нельзя `<`, `>`, `;`, таб, перевод строки, `\0`
- описание:
- UTF-8
- максимум `250` символов
- `avatar`:
- `ar` только `txId`
- хранится без имени файла
- новый meta-блок задаёт полный снимок состояния
- сервер применяет только последний валидный meta-блок
- невалидный meta-блок не применяется целиком
- БД расширяется в той же модели каналов
- в UI обычным сообщением блок не показывается
## Что ещё потребуется зафиксировать позже
При переводе черновика в официальную спецификацию нужно будет дополнительно утвердить:
- точный номер версии формата;
- точный способ подсчёта длины `title` и описания:
- по Unicode code points;
- по Java `String.length()`;
- или по UTF-8 байтам;
- точную проверку длины `ar`;
- список API-ответов, куда должны быть добавлены новые поля;
- правила миграции старых каналов без meta-блоков.
@@ -0,0 +1,723 @@
# Черновик дополнений каналов и типов сообщений SHiNE
## Статус документа
Этот файл является рабочим черновиком первой итерации.
Важно:
- это не финальная спецификация;
- формат блокчейна этим документом пока не меняется автоматически;
- документ фиксирует согласованную на текущий момент концепцию;
- в первую итерацию входят только `TEXT` и `STATUS_ACTION`;
- всё, что касается `CHANNEL_MEMBERSHIP`, вынесено в отдельный отложенный черновик:
- `Черновик_CHANNEL_MEMBERSHIP_для_каналов.md`
## Цель
Нужно расширить текущую модель каналов SHiNE так, чтобы канал был не только лентой обычных постов, но и пространством:
- материалов;
- упражнений;
- услуг / процедур;
- курсов;
- входных страниц каналов;
- истории действий пользователей по этим материалам.
При этом важно:
- не ломать существующий блокчейн без отдельного согласованного шага;
- минимально менять верхнеуровневые `type`;
- по возможности переиспользовать уже существующий текстовый тип сообщений;
- отделять собственно контент от событий состояния.
## Базовая идея первой итерации
На текущий момент согласована следующая модель:
- старый `type=1`, который раньше назывался `TEXT`, по коду можно не менять;
- в документации и логике его можно трактовать шире как основной контентный тип сообщений;
- все новые смысловые материалы каналов добавляются как новые `subType` внутри `type=1`;
- отдельным верхнеуровневым `type` в первой итерации становится только:
- `type=5` — действия пользователя по материалам.
Иначе говоря:
- `type=1` = сообщения и материалы;
- `type=5` = статусные действия пользователя;
- `type=6` = тема вынесена в отдельный отложенный черновик и в первую итерацию не входит.
## Текущие старые верхнеуровневые типы
Они сохраняются:
- `type=0``TECH`
- `type=1``TEXT`
- `type=2``REACTION`
- `type=3``CONNECTION`
- `type=4``USER_PARAM`
## Верхнеуровневые типы первой итерации
Используются:
- `0``TECH`
- `1``TEXT`
- `2``REACTION`
- `3``CONNECTION`
- `4``USER_PARAM`
- `5``STATUS_ACTION`
Отдельно:
- `6``CHANNEL_MEMBERSHIP`
- зарезервирован как следующая тема;
- вынесен в отдельный черновик;
- в текущую реализацию не входит.
## Подтипы внутри `type=1`
Согласованная таблица первой итерации:
- `subType=10``TEXT_POST`
- `subType=11``TEXT_EDIT_POST`
- `subType=20``TEXT_REPLY`
- `subType=21``TEXT_EDIT_REPLY`
- `subType=30``TEXT_RATING`
- `subType=50``TEXT_REPOST`
- `subType=90``TEXT_CHANNEL_META`
- `subType=100``TEXT_ENTRYPOINT`
- `subType=110``TEXT_EXERCISE`
- `subType=120``TEXT_SERVICE`
- `subType=130``TEXT_COURSE`
## Принцип форматов внутри `type=1`
### Line-based сообщения
Следующие подтипы считаются line-based и используют тот же body, что и обычный `TEXT_POST`:
- `TEXT_POST`
- `TEXT_REPOST`
- `TEXT_CHANNEL_META`
- `TEXT_ENTRYPOINT`
- `TEXT_EXERCISE`
- `TEXT_SERVICE`
- `TEXT_COURSE`
### Target-based сообщения
Следующие подтипы считаются target-based:
- `TEXT_REPLY`
- `TEXT_EDIT_REPLY`
- `TEXT_RATING`
### Edit для `TEXT`
В первой итерации согласовано оставить в коде два технических edit-подтипа:
- `TEXT_EDIT_POST`
- edit для сообщений линии канала;
- `TEXT_EDIT_REPLY`
- edit для reply-сообщений.
То есть в документации больше не используется старое упрощённое описание “один общий `TEXT_EDIT`”.
## Смысл подтипов `type=1`
### `TEXT_POST`
Обычный текстовый пост в канале.
### `TEXT_EDIT_POST`
Редактирование line-based текстового сообщения канала.
Принцип:
- edit всегда ссылается на оригинальный блок;
- edit не должен ссылаться на предыдущий edit;
- фактический тип и правила берутся из оригинального сообщения.
### `TEXT_REPLY`
Обычный ответ / комментарий на сообщение.
Важно:
- reply остаётся единым;
- отвечать можно на `post`, `rating`, `exercise`, `service`, `course`, `entrypoint`, `status_action`;
- reply сам является target-based сообщением.
### `TEXT_EDIT_REPLY`
Редактирование reply-сообщения.
### `TEXT_RATING`
Текстовый отзыв / мнение / оценка на конкретный блок.
Смысл:
- это не line-based пост;
- это target-based сообщение-отзыв;
- оно всегда ссылается на конкретный блок, который оценивает;
- оценку пользователя как отдельную сущность в эту итерацию не включаем.
### `TEXT_REPOST`
Отложенная будущая заготовка.
На текущем этапе:
- код зарезервирован;
- бизнес-логика не входит в первую итерацию;
- детальная реализация может быть возвращена позже.
### `TEXT_CHANNEL_META`
Специальное скрытое сообщение метаданных канала.
Через него задаются:
- красивое имя канала;
- аватар;
- описание;
- и другие общие channel meta.
Оно:
- не является обычным пользовательским сообщением;
- не должно показываться в ленте как обычный пост;
- должно парситься по отдельным техническим правилам.
### `TEXT_ENTRYPOINT`
Входная / главная страница канала.
Смысл:
- это не отдельный верхнеуровневый тип;
- это специальный line-based текстовый материал;
- он служит входной страницей канала;
- он может объяснять структуру канала, давать ссылки, вводить человека в тему.
Принцип:
- `TEXT_ENTRYPOINT` не редактируется через `TEXT_EDIT_POST`;
- новая версия создаётся новым сообщением `TEXT_ENTRYPOINT`;
- если в канале несколько `entrypoint`, актуальным считается последний.
### `TEXT_EXERCISE`
Упражнение или комплекс упражнений.
Смысл:
- материал, который можно выполнять много раз;
- материал, который можно выучить;
- по нему удобно строить статистику выполнений и освоения.
### `TEXT_SERVICE`
Услуга / процедура.
Смысл:
- материал, который пользователь проходит;
- обычно не “учится выполнять”, а именно получает / проходит;
- прохождение фиксируется отдельным `STATUS_ACTION`.
### `TEXT_COURSE`
Курс.
Смысл:
- материал, по которому есть путь обучения;
- пользователь может заинтересоваться, начать, учиться, завершить, бросить.
## Канал `0`
Согласована новая трактовка канала `0`.
Канал `0` становится:
- обычным каналом публикаций пользователя по умолчанию;
- местом его основной ленты;
- местом его главной страницы, если там есть `entrypoint`.
### Что это значит
Если у пользователя в канале `0` существует `TEXT_ENTRYPOINT`, тогда:
- ссылка `SHiNE/<login>` открывает именно этот последний `entrypoint`.
Если `entrypoint` в канале `0` нет, тогда:
- `SHiNE/<login>` открывает обычную ленту публикаций канала `0`.
## Правила открытия каналов и ссылок
Согласована следующая логика:
- `SHiNE/<login>`
- главная страница пользователя, то есть канал `0`;
- `SHiNE/<login>/<channel>`
- основная ссылка канала.
### Как открывается `SHiNE/<login>/<channel>`
Если у канала есть хотя бы один `TEXT_ENTRYPOINT`, тогда:
- по умолчанию открывается последний актуальный `entrypoint`;
- UI показывает, сколько после него было новых сообщений;
- UI показывает кнопку перехода в конец канала.
Если `entrypoint` нет:
- открывается обычная лента канала.
Для подписанного пользователя:
- логичнее вести его в место новых непрочитанных сообщений;
- а не всегда принудительно открывать entrypoint;
- при этом сверху можно показывать кнопку `Открыть entrypoint`.
### Ссылки на конкретные сообщения
Остаются обычные формы:
- `SHiNE/<login>/<channel>/<messageNumber>`
- `SHiNE/<login>/<channel>/<messageNumber>/<hash>`
Если нужно показать старую конкретную версию `entrypoint`, даётся ссылка именно на номер нужного сообщения.
## Новый тип `STATUS_ACTION`
`type=5` вводится для действий пользователя по контенту.
## Принцип `STATUS_ACTION`
Согласовано:
- `STATUS_ACTION` всегда является target-based сообщением;
- статусное действие всегда ссылается на конкретный блок-материал;
- это не обычный пост в канале;
- из таких сообщений можно собирать виртуальную ленту пользователя.
### Как трактуется текст в `STATUS_ACTION`
Основной смысл блока задаётся самим статусным событием.
Текст внутри такого блока:
- это комментарий пользователя к действию, если он есть;
- не является главным смыслом записи;
- служит пояснением.
Примеры:
- “Начал сегодня”
- “Решил пройти серьёзно”
- “Сделал это после практики”
- “Выучил базовый комплекс”
## Подтипы `STATUS_ACTION`
Предлагаемая таблица:
- `subType=10``STATUS_DONE_ONCE`
- `subType=20``STATUS_INTERESTED`
- `subType=30``STATUS_STARTED`
- `subType=40``STATUS_IN_STUDY`
- `subType=50``STATUS_COMPLETED`
- `subType=60``STATUS_ABANDONED`
- `subType=70``STATUS_LEARNED`
- `subType=80``STATUS_CONFIRMED`
## Смысл подтипов `STATUS_ACTION`
### `STATUS_DONE_ONCE`
Факт одного выполнения / прохождения.
Это накопительное событие.
### `STATUS_INTERESTED`
Пользователя заинтересовал материал.
### `STATUS_STARTED`
Пользователь начал.
Это начальный статус процесса, но ещё не “устойчивое обучение”.
### `STATUS_IN_STUDY`
Пользователь уже полноценно находится в обучении.
Смысл:
- не просто попробовал;
- а реально учится;
- это отдельный статус, более сильный, чем `started`.
### `STATUS_COMPLETED`
Пользователь завершил курс / прохождение.
### `STATUS_ABANDONED`
Пользователь бросил.
### `STATUS_LEARNED`
Пользователь выучил упражнение или комплекс и знает, как его делать.
Это не то же самое, что “сделал один раз”.
### `STATUS_CONFIRMED`
Подтверждение чужого status-события.
Важно:
- подтверждается не курс вообще;
- не упражнение вообще;
- а конкретный `STATUS_ACTION` конкретного пользователя.
## Кто ставит статусы
Согласовано правило:
- любой базовый статус ставит сам пользователь от своего имени;
- другие люди не ставят статус за него;
- другие люди могут только подтверждать его status-событие через `STATUS_CONFIRMED`.
То есть:
- `INTERESTED`, `STARTED`, `IN_STUDY`, `COMPLETED`, `ABANDONED`, `LEARNED`, `DONE_ONCE`
- ставит сам пользователь;
- `CONFIRMED`
- ставят другие люди на конкретный статусный блок.
### Вес подтверждений
В базовой версии веса подтверждений не вводятся.
Но в будущем можно добавить:
- более значимое подтверждение от создателя курса;
- более значимое подтверждение от создателя упражнения;
- весовые коэффициенты от близких / доверенных людей;
- слабые подтверждения от обычных пользователей.
## Матрица допустимости статусов по типам контента
### Для `TEXT_SERVICE`
Разрешены:
- `STATUS_DONE_ONCE`
- `STATUS_CONFIRMED`
Не разрешены:
- `INTERESTED`
- `STARTED`
- `IN_STUDY`
- `COMPLETED`
- `ABANDONED`
- `LEARNED`
### Для `TEXT_EXERCISE`
Разрешены:
- `STATUS_DONE_ONCE`
- `STATUS_LEARNED`
- `STATUS_CONFIRMED`
Логика:
- упражнение можно выполнять много раз;
- упражнение можно выучить;
- `LEARNED` и `DONE_ONCE` не конфликтуют и живут параллельно.
### Для `TEXT_COURSE`
Разрешены:
- `STATUS_INTERESTED`
- `STATUS_STARTED`
- `STATUS_IN_STUDY`
- `STATUS_COMPLETED`
- `STATUS_ABANDONED`
- `STATUS_CONFIRMED`
### Для `TEXT_ENTRYPOINT`
Статусные действия не ставятся.
### Для `TEXT_POST`, `TEXT_REPLY`, `TEXT_RATING`, `TEXT_CHANNEL_META`
Статусные действия не ставятся.
## Накопительные и текущие состояния
Нужно различать:
- накопительные события;
- текущий статус.
### Накопительные события
К ним относится:
- `STATUS_DONE_ONCE`
Они могут встречаться сколько угодно раз.
### Текущий статус
Для некоторых статусов есть “актуальное состояние”.
Например:
- `INTERESTED`
- `STARTED`
- `IN_STUDY`
- `COMPLETED`
- `ABANDONED`
- `LEARNED`
Текущий статус определяется как последнее событие соответствующей оси.
### Для упражнения
У упражнения есть две независимые оси:
- количественная: сколько раз выполнено;
- качественная: выучено или нет.
Это значит:
- `STATUS_DONE_ONCE` накапливается;
- `STATUS_LEARNED` живёт как отдельный качественный статус.
### Для курса
У курса текущий статус вычисляется по последнему из:
- `INTERESTED`
- `STARTED`
- `IN_STUDY`
- `COMPLETED`
- `ABANDONED`
## Виртуальная лента достижений пользователя
Согласована важная идея:
- не нужно создавать отдельный физический канал для истории выполнений пользователя;
- вместо этого строится виртуальная лента из его `STATUS_ACTION`.
### Что это даёт
Можно одновременно получить:
- историю человека;
- статистику по самому материалу.
Например, по упражнению можно увидеть:
- сколько разных людей его выполняли;
- сколько всего выполнений было;
- сколько раз конкретный человек его выполнял;
- кто его выучил.
### Что попадает в виртуальную ленту
Попадают status-события пользователя:
- выполненные упражнения;
- выученные упражнения;
- интерес к курсам;
- начатые курсы;
- обучение в процессе;
- завершённые курсы;
- брошенные курсы;
- прохождения услуг;
- подтверждения к этим событиям.
### Можно ли это обсуждать
Да.
Такие статусные сообщения остаются обычными объектами обсуждения:
- на них можно отвечать;
- их можно комментировать;
- на них можно писать отзывы;
- их можно подтверждать.
То есть:
- лента виртуальная;
- но сами записи реальные и обсуждаемые.
## Как это должно выглядеть в интерфейсе
### В каналах
Для разных типов сообщений UI должен понимать роль сообщения.
Например:
- у `TEXT_EXERCISE` можно показать кнопки:
- `Выполнил`
- `Выучил`
- у `TEXT_SERVICE`:
- `Прошёл`
- у `TEXT_COURSE`:
- `Интересно`
- `Начал`
- `Учусь`
- `Завершил`
- `Бросил`
### Для `entrypoint`
Если канал открывается по основной ссылке и у него есть `entrypoint`, UI:
- показывает сам последний `entrypoint`;
- показывает число новых сообщений после него;
- показывает кнопку перехода в конец канала.
Если пользователь уже подписан на канал:
- открывать лучше место новых непрочитанных сообщений;
- но с кнопкой открытия последнего `entrypoint`.
### Для виртуальной ленты достижений
В профиле пользователя UI может показывать:
- отдельную вкладку / раздел;
- где лента строится из его `STATUS_ACTION`.
## Что именно не нужно делать в первой итерации
### Не нужен отдельный физический канал достижений
Технически не нужен.
### Не нужны отдельные edit-подтипы для каждого вида контента
Не нужны:
- `TEXT_EDIT_EXERCISE`
- `TEXT_EDIT_SERVICE`
- `TEXT_EDIT_COURSE`
Достаточно текущего разделения:
- `TEXT_EDIT_POST`
- `TEXT_EDIT_REPLY`
### Не нужен отдельный верхнеуровневый тип для `entrypoint`
Он остаётся подтипом `type=1`.
### Не нужен `EDIT` для status-событий
`STATUS_ACTION` не редактируются.
Если нужно изменить смысл, пишется новое событие.
### Не нужен `CHANNEL_MEMBERSHIP` в первой итерации
Эта тема отложена в отдельный черновик.
## Сводная таблица
### Верхнеуровневые `type`
| Код | Имя | Статус |
|---|---|---|
| 0 | TECH | старый |
| 1 | TEXT | старый код, новое расширенное смысловое описание |
| 2 | REACTION | старый |
| 3 | CONNECTION | старый |
| 4 | USER_PARAM | старый |
| 5 | STATUS_ACTION | первая итерация |
| 6 | CHANNEL_MEMBERSHIP | вынесено в отдельный отложенный черновик |
### Подтипы `type=1`
| subType | Имя | Смысл |
|---|---|---|
| 10 | TEXT_POST | обычный post в линии канала |
| 11 | TEXT_EDIT_POST | edit line-based сообщения |
| 20 | TEXT_REPLY | target-based reply |
| 21 | TEXT_EDIT_REPLY | edit reply |
| 30 | TEXT_RATING | target-based отзыв на конкретный блок |
| 50 | TEXT_REPOST | отложенная будущая заготовка |
| 90 | TEXT_CHANNEL_META | скрытые метаданные канала |
| 100 | TEXT_ENTRYPOINT | входная страница канала |
| 110 | TEXT_EXERCISE | упражнение |
| 120 | TEXT_SERVICE | услуга / процедура |
| 130 | TEXT_COURSE | курс |
### Подтипы `type=5`
| subType | Имя | Смысл |
|---|---|---|
| 10 | STATUS_DONE_ONCE | выполнил / прошёл один раз |
| 20 | STATUS_INTERESTED | заинтересовался |
| 30 | STATUS_STARTED | начал |
| 40 | STATUS_IN_STUDY | учится полноценно |
| 50 | STATUS_COMPLETED | завершил |
| 60 | STATUS_ABANDONED | бросил |
| 70 | STATUS_LEARNED | выучил |
| 80 | STATUS_CONFIRMED | подтверждение status-события |
## Что ещё нужно отдельно утвердить перед реализацией
Перед началом реальной реализации желательно отдельно утвердить:
- точный байтовый формат новых line-based `subType` внутри `type=1`;
- точный байтовый формат `TEXT_RATING` как target-based отзыва;
- точный байтовый формат `STATUS_ACTION`;
- правила target-ссылок для status-событий;
- правила target-ссылок для отзывов на status-события;
- поведение удаления:
- можно ли логически удалять `TEXT_EXERCISE` / `TEXT_SERVICE` / `TEXT_COURSE`;
- можно ли удалять старые `entrypoint` или только оставлять их в истории;
- серверные read-model таблицы и индексы.
## Предварительная оценка готовности к реализации
На текущий момент концепция уже достаточно зрелая, чтобы начинать проектирование реализации первой итерации.
Реализовывать это уже можно, если дополнительно утвердить:
- байтовые форматы;
- правила валидации;
- API чтения новых сущностей;
- UI-матрицу действий по каждому типу контента.
## Краткий итог
Согласованная модель первой итерации сейчас такая:
- почти всё новое содержимое каналов живёт внутри `type=1`;
- `entrypoint` — это специальное текстовое сообщение, а не отдельный верхнеуровневый тип;
- `entrypoint` не редактируется, а версионируется новыми сообщениями;
- канал `0` становится каналом публикаций пользователя и его главной страницей;
- действия пользователя по материалам выносятся в `STATUS_ACTION`;
- `TEXT_RATING` трактуется как target-based отзыв на конкретный блок;
- `CHANNEL_MEMBERSHIP` отложен в отдельный черновик и в первую итерацию не входит.
@@ -0,0 +1,173 @@
# Черновик формата вложений в сообщениях блокчейна SHiNE
## Статус документа
Этот файл пока является черновиком и рабочим описанием идеи.
Важно:
- это не финальная спецификация;
- точная версия формата будет зафиксирована после начала реальной реализации;
- до момента реализации допустимы изменения полей, синтаксиса и правил отображения;
- документ специально лежит в корне проекта как временная рабочая заметка.
## Цель
Нужен минимальный формат, позволяющий прикладывать файлы к текстовым сообщениям в каналах и тредах, не превращая само сообщение в отдельный файловый контейнер.
Основная идея:
- само сообщение остаётся обычным текстом;
- вложения описываются специальными техническими вставками в начале текста;
- сами файлы хранятся отдельно в архиве/Arweave;
- в сообщении хранятся только метаданные, достаточные для отображения и скачивания.
## Общий принцип
Если в начале текста сообщения стоит один или несколько блоков:
```text
<SHiNE:attach;...>
```
клиент трактует их как описания вложений.
После этих блоков идёт обычный пользовательский текст сообщения.
Если технических блоков нет, сообщение считается обычным текстовым сообщением без вложений.
## Формат одного блока вложения
Общий вид:
```text
<SHiNE:attach;v=1;name=photo.jpg;size=248193;sha256=abcdef...;ar=tx_or_url>
```
Где:
- `attach` — тип технической вставки;
- `v=1` — текущая рабочая версия чернового формата;
- `name` — имя файла;
- `size` — размер файла в байтах;
- `sha256` — SHA-256 файла в hex;
- `ar` — идентификатор файла в архиве или адрес файла в Arweave.
## Несколько вложений
Если к одному сообщению приложено несколько файлов, блоки просто идут подряд в начале текста:
```text
<SHiNE:attach;v=1;name=photo.jpg;size=248193;sha256=aaa...;ar=...>
<SHiNE:attach;v=1;name=video.mp4;size=5820193;sha256=bbb...;ar=...>
Текст сообщения
```
## Обязательные поля
Для чернового варианта обязательными считаются:
- `v`
- `name`
- `size`
- `sha256`
- `ar`
Если какого-то из этих полей нет, клиент может:
- игнорировать конкретный битый блок;
- не считать его валидным вложением;
- при этом продолжать обрабатывать остальные корректные блоки.
## Почему без `mime` и без `kind`
В минимальном варианте тип файла отдельно не хранится.
Причины:
- это экономит место;
- тип всё равно обычно определяется клиентом;
- для первого варианта достаточно имени файла, адреса, хэша и размера;
- превью и способ отображения клиент может решать сам по расширению имени файла или по попытке открыть файл.
То есть в сообщении не хранится:
- `mime`;
- `kind`;
- `width`;
- `height`;
- `duration`;
- `thumbnail`.
Все эти поля можно добавить позже отдельной версией, если они реально понадобятся.
## Логика клиента
Клиент получает:
- имя файла;
- размер;
- хэш;
- адрес файла.
После этого клиент сам решает, как рендерить вложение:
- если расширение похоже на изображение, можно пробовать показать preview;
- если расширение похоже на видео, можно пробовать встроенный видеоплеер;
- если расширение похоже на аудио, можно пробовать аудиоплеер;
- иначе показывать обычную кнопку скачивания.
Если браузер или клиент не смог показать preview, должен быть fallback на скачивание файла.
## Правила совместимости
- Сообщение без `<SHiNE:attach;...>` остаётся обычным текстом.
- Старые клиенты, которые не умеют распознавать этот формат, могут показывать сообщение как обычный текст целиком.
- Новые клиенты могут скрывать технические блоки и показывать вместо них UI вложений.
## Ограничения чернового варианта
В текущем виде значения полей не предполагают сложного экранирования.
Значит нужно отдельно договориться, как безопасно хранить `name`, если там встретятся символы:
- `;`
- `=`
- `>`
На этапе реальной реализации нужно выбрать один из вариантов:
- жёстко ограничить допустимые символы в `name`;
- хранить имя файла в безопасно закодированном виде;
- добавить отдельное правило escape/encoding.
Пока этот вопрос считается открытым.
## Примеры
### Одно вложение
```text
<SHiNE:attach;v=1;name=photo.jpg;size=248193;sha256=3f2c8a...;ar=6sYk...>
Привет, вот фото
```
### Два вложения
```text
<SHiNE:attach;v=1;name=photo.jpg;size=248193;sha256=3f2c8a...;ar=6sYk...>
<SHiNE:attach;v=1;name=report.pdf;size=845221;sha256=9ab01e...;ar=K9Lp...>
Смотри, прикрепил фото и документ
```
## Что надо зафиксировать позже
Перед переводом этого черновика в официальную спецификацию нужно отдельно утвердить:
- точное место этого формата в общей документации блокчейна;
- где именно используются такие вложения: каналы, треды, комментарии или иные текстовые блоки;
- финальные правила кодирования `name`;
- допустим ли полный URL в `ar` или только короткий `txId`;
- нужно ли ограничение на количество вложений в одном сообщении;
- нужна ли отдельная серверная или клиентская проверка расширения файла;
- как именно это будет отображаться в web UI и других клиентах.
@@ -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)` убраны только из формы создания новой записи. Их константы и обработчики чтения сохранены для совместимости со старыми данными.