# Задача 01: Доработка вкладки «Каналы» (UI + API)

## Кратко и по делу
Нужно довести вторую вкладку «Каналы» до полностью рабочего состояния на реальных данных сервера.

Что должно работать:
- список каналов;
- вход в канал и чтение сообщений;
- вход в тред сообщения (история/ветка);
- ответ на сообщение;
- лайк/снятие лайка;
- подписка на пользователя;
- подписка на канал;
- видимое имя канала в формате `имя_пользователя/имя_канала`.

Запись любых новых сущностей делается через `AddBlock` с подписью на клиенте.
Чтение делается через 3 API:
- `ListSubscriptionsFeed`
- `GetChannelMessages`
- `GetMessageThread`

Техническая особенность (оставляем как есть):
- на экране каналов индикатор непрочитанного = общее число сообщений канала.

---

## Подробное ТЗ

### 1. Цель
Сделать рабочий каналовый сценарий «от списка до треда», где чтение строится на RPC API, а запись действий пользователя — только через `AddBlock`.

### 2. Что уже есть в проекте

#### 2.1 UI (частично)
- Есть страницы:
  - `channels-list`
  - `channel-view`
  - `add-channel-view`
- Есть запросы чтения в клиенте:
  - `authService.listSubscriptionsFeed(...)`
  - `authService.getChannelMessages(...)`
  - `authService.getMessageThread(...)`
- Есть fallback на mock-данные при ошибках сервера.

#### 2.2 API/сервер (уже реализованы)
- `ListSubscriptionsFeed`
- `GetChannelMessages`
- `GetMessageThread`
- `AddBlock`

#### 2.3 Тесты
- Есть интеграционный тест API каналов: `IT_06_ChannelsApi`.
- Есть тесты генерации блоков каналов/связей: `IT_03_AddBlock_NoAuth`.
- Формат `AddBlock` и его сборка/подпись описаны в `AddBlockSender`.

### 3. Проблемы текущей реализации (что надо закрыть)
- Кнопки «подписаться на человека/канал» в списке каналов сейчас UI-only (модалка без реальной записи через `AddBlock`).
- `add-channel-view` пока не создает канал на сервере через `AddBlock` (`CreateChannelBody`), только делает `navigate`.
- `channel-view` добавляет пост локально (в память), а не отправляет блок `TEXT_POST` через `AddBlock`.
- Нет полноценного экрана треда сообщения с реальными `GetMessageThread` и действиями `ответить/лайк/убрать лайк` через блоки.
- Нет гарантированного отображения канала в требуемом формате `ownerLogin/channelName`.

### 4. Функциональные требования

#### 4.1 Список каналов
На вкладке «Каналы» отображать 3 группы:
- Мои каналы
- Каналы пользователей, на кого я подписан
- Каналы, на которые я подписан

Источник данных: `ListSubscriptionsFeed`.

Каждый канал показывать в формате:
- `ownerLogin/channelName`

#### 4.2 Открытие канала
При входе в канал:
- загрузить сообщения через `GetChannelMessages`;
- показать список сообщений в хронологическом порядке (по текущему параметру `sort`);
- оставить техническую особенность непрочитанных как есть.

#### 4.3 Открытие треда сообщения
При клике на сообщение:
- загрузить тред через `GetMessageThread`;
- показать `ancestors`, `focus`, `descendants`;
- из треда должны быть доступны действия:
  - «Ответить»
  - «Лайк»
  - «Убрать лайк»

Запись действий — только `AddBlock`.

#### 4.4 Создание канала
В `add-channel-view` кнопка «Создать» должна:
- отправлять `AddBlock` с телом `CreateChannelBody`;
- после успеха возвращать к списку каналов и обновлять его.

#### 4.5 Подписки
- Подписка на пользователя: `AddBlock` с `ConnectionBody` подтип `CONNECTION_FOLLOW`, target = HEADER пользователя.
- Подписка на канал: `AddBlock` с `ConnectionBody` подтип `CONNECTION_FOLLOW`, target = root блока канала (`CreateChannelBody` или HEADER для канала `0`).

### 5. API (форматы)

## 5.1 ListSubscriptionsFeed (чтение)
Request:
```json
{
  "op": "ListSubscriptionsFeed",
  "requestId": "...",
  "payload": {
    "login": "A1",
    "limit": 200
  }
}
```

Response (смысловые поля):
- `ownedChannels[]`
- `followedUsersChannels[]`
- `followedChannels[]`

---

## 5.2 GetChannelMessages (чтение)
Request:
```json
{
  "op": "GetChannelMessages",
  "requestId": "...",
  "payload": {
    "channel": {
      "ownerBlockchainName": "A1-001",
      "channelRootBlockNumber": 0,
      "channelRootBlockHash": ""
    },
    "limit": 200,
    "sort": "asc"
  }
}
```

Response (смысловые поля):
- `channel`
- `messages[]`

---

## 5.3 GetMessageThread (чтение)
Request:
```json
{
  "op": "GetMessageThread",
  "requestId": "...",
  "payload": {
    "message": {
      "blockchainName": "A1-001",
      "blockNumber": 15,
      "blockHash": "..."
    },
    "depthUp": 20,
    "depthDown": 2,
    "limitChildrenPerNode": 50
  }
}
```

Response (смысловые поля):
- `ancestors[]`
- `focus`
- `descendants[]`

---

## 5.4 AddBlock (запись)
Любое изменение (создать канал, пост, reply, реакция, подписка) записывается через:
```json
{
  "op": "AddBlock",
  "requestId": "...",
  "payload": {
    "blockchainName": "A1-001",
    "blockNumber": 6,
    "prevBlockHash": "<64-hex>",
    "blockBytesB64": "<base64 full block>"
  }
}
```

Важно:
- `blockBytesB64` формируется на клиенте.
- Подпись блока формируется на клиенте приватным blockchain key пользователя.
- Перед добавлением блока клиент берет актуальный курсор цепочки с сервера.

### 6. Типы блоков для каналов и связей (через AddBlock)
- Создание канала: `CreateChannelBody`
- Пост/ответ: `TextBody` (`TEXT_POST`, `TEXT_REPLY`)
- Реакции: `ReactionBody` (лайк/снятие лайка)
- Подписки: `ConnectionBody` (`CONNECTION_FOLLOW`)

### 7. Критерии приемки
- Список каналов отображается с реальными данными API.
- Формат названия канала в UI: `ownerLogin/channelName`.
- Создание канала реально пишет блок и канал появляется после обновления.
- Отправка поста/ответа/реакций реально пишет блок и видна после перечитки API.
- Подписка на пользователя/канал реально пишет блок и отражается в выдаче.
- Переход в тред сообщения показывает реальные `ancestors/focus/descendants`.
- Непрочитанные в списке каналов = общее число сообщений (временное правило).

### 8. Локальный запуск (уже сделано)
Команда:
```bash
./gradlew startLocal
```

Что делает:
- чистит логи;
- билдит сервер;
- запускает локальный WS сервер;
- запускает локальный HTTP сервер клиента;
- открывает браузер по URL с параметром `localWsPort`.
