# Черновик дополнений каналов и типов сообщений 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/` открывает именно этот последний `entrypoint`. Если `entrypoint` в канале `0` нет, тогда: - `SHiNE/` открывает обычную ленту публикаций канала `0`. ## Правила открытия каналов и ссылок Согласована следующая логика: - `SHiNE/` - главная страница пользователя, то есть канал `0`; - `SHiNE//` - основная ссылка канала. ### Как открывается `SHiNE//` Если у канала есть хотя бы один `TEXT_ENTRYPOINT`, тогда: - по умолчанию открывается последний актуальный `entrypoint`; - UI показывает, сколько после него было новых сообщений; - UI показывает кнопку перехода в конец канала. Если `entrypoint` нет: - открывается обычная лента канала. Для подписанного пользователя: - логичнее вести его в место новых непрочитанных сообщений; - а не всегда принудительно открывать entrypoint; - при этом сверху можно показывать кнопку `Открыть entrypoint`. ### Ссылки на конкретные сообщения Остаются обычные формы: - `SHiNE///` - `SHiNE////` Если нужно показать старую конкретную версию `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` отложен в отдельный черновик и в первую итерацию не входит.