# 画面設計書 11-コミュニケーション画面

## 概要

本ドキュメントは、Etherpad Lite管理画面における「コミュニケーション画面」の設計について記載する。この画面は管理者が接続中の全ユーザーに対してメッセージを一斉送信するためのインターフェースを提供する。

### 本画面の処理概要

この画面では、Etherpadに現在接続しているすべてのユーザーに対して、リアルタイムでメッセージを送信することができる。送信されたメッセージは各パッド編集画面上にポップアップ通知として表示される。

**業務上の目的・背景**：
この画面は、システム管理者がユーザーへの重要なお知らせ（メンテナンス予告、サービス障害通知、利用規約変更など）を迅速に全ユーザーへ周知するために必要である。従来のメール通知などと異なり、パッドを編集中のユーザーにリアルタイムで情報を届けることができるため、緊急性の高い連絡に適している。また、stickyオプションを有効にすることで、メッセージをユーザーが明示的に閉じるまで表示し続けることも可能であり、重要度に応じた通知が実現できる。

**画面へのアクセス方法**：
1. 管理者としてログイン後、管理画面（/admin）にアクセス
2. サイドメニューから「Communication」を選択
3. URLパス: /admin/shout に直接アクセスすることも可能（要認証）

**主要な操作・処理内容**：
1. 接続中ユーザー数の確認 - 画面上部に現在接続中のユーザー数が表示される
2. メッセージ入力 - テキスト入力欄にメッセージを入力
3. Stickyオプション切り替え - Switchコンポーネントでstickyメッセージの有効/無効を設定
4. メッセージ送信 - 送信ボタンまたはEnterキーでメッセージを送信
5. 送信履歴の確認 - 画面中央のメッセージ履歴エリアで過去の送信メッセージを確認

**画面遷移**：
- 遷移元: 管理者ログイン画面（認証成功後）、プラグイン管理画面、設定管理画面、ヘルプ/情報画面、パッド管理画面（サイドメニュー経由）
- 遷移先: プラグイン管理画面、設定管理画面、ヘルプ/情報画面、パッド管理画面（サイドメニュー経由）

**権限による表示制御**：
- 管理者権限（is_admin）を持つユーザーのみアクセス可能
- 非管理者がアクセスした場合、ログイン画面へリダイレクト
- サーバー側でも管理者権限チェックが行われ、権限がない場合はSocket.IO接続時に処理が中断される

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 37 | クライアントメッセージ送信 | 主機能 | 接続中の全ユーザーへメッセージを送信 |
| 64 | 統計情報取得 | 補助機能 | 現在接続中のユーザー数を取得して表示 |

## 画面種別

入力・送信画面

## URL/ルーティング

| 項目 | 値 |
|------|-----|
| URLパス | /admin/shout |
| ルーティング設定 | `admin/src/main.tsx` の React Router で定義 |
| コンポーネント | `ShoutPage` (`admin/src/pages/ShoutPage.tsx`) |

## 入出力項目

### 入力項目

| 項目名 | データ型 | 必須 | 最大長 | バリデーション | 説明 |
|--------|---------|------|--------|---------------|------|
| message | string | 必須 | - | HTML5 required属性 | 送信するメッセージ本文 |
| sticky | boolean | 任意 | - | - | trueの場合、ユーザーが閉じるまで表示し続ける固定メッセージ |

### 出力項目

| 項目名 | データ型 | 説明 |
|--------|---------|------|
| totalUsers | number | 現在接続中の総ユーザー数 |
| shouts | ShoutType[] | 送信済みメッセージの履歴一覧 |

## 表示項目

### ヘッダー部

| 項目 | 説明 |
|------|------|
| タイトル | "Communication" |
| ユーザー数表示 | "There is/are currently X user(s) online" |

### メッセージ履歴エリア

| 項目 | データ型 | 説明 |
|------|---------|------|
| メッセージ本文 | string | 送信されたメッセージの内容 |
| 送信日時 | string | メッセージの送信時刻（ローカル時間形式で表示） |

### 入力エリア

| 項目 | 種別 | 説明 |
|------|------|------|
| Sticky切り替え | Switch | Radix UI Switchコンポーネント。ONで固定メッセージ |
| メッセージ入力欄 | input[type="text"] | メッセージ入力フィールド |
| 送信ボタン | アイコンボタン | SendHorizonal アイコン（Lucide React） |

## イベント仕様

### 1-メッセージ送信

**トリガー**: 送信ボタンクリック または フォームsubmit（Enterキー）

**処理フロー**:
1. フォームのsubmitイベントでpreventDefault()を実行
2. sendMessage関数を呼び出し
3. settingsSocket経由で'shout'イベントを発行
4. ペイロード: `{ message: string, sticky: boolean }`
5. 入力フィールドをクリア

**サーバー側処理**:
1. `/settings`名前空間でshoutイベントを受信
2. メッセージオブジェクトを構築（タイプ、ペイロード、タイムスタンプ）
3. `/settings`名前空間の全クライアントにshoutイベントを送信（管理画面への反映）
4. メインのSocket.IO名前空間（io.sockets）の全クライアントにshoutイベントを送信（パッド編集画面への反映）

**クライアント側（パッド編集画面）での表示**:
- `$.gritter.add()` を使用してポップアップ通知を表示
- タイトル: "Admin message"
- 本文: 送信時刻とメッセージ内容
- sticky: 送信時のstickyフラグに従う

### 2-ユーザー数取得

**トリガー**: コンポーネントマウント時（pluginSocketが接続された時点）

**処理フロー**:
1. useEffectでpluginSocketの接続を監視
2. 接続後、'getStats'イベントを発行
3. 'results:stats'イベントで統計情報を受信
4. totalUsersステートを更新

### 3-Sticky切り替え

**トリガー**: Switchコンポーネントのクリック

**処理フロー**:
1. onCheckedChangeハンドラが発火
2. stickyステートをトグル
3. 次回送信時にstickyフラグとして使用

## データベース更新仕様

### 操作別データベース影響一覧

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| メッセージ送信 | - | - | データベース操作なし（リアルタイム配信のみ） |

**備考**: この画面で送信されるメッセージはデータベースに永続化されない。Socket.IOを通じてリアルタイムで接続中のクライアントに配信されるのみであり、ページリロード後は履歴も消える。

## メッセージ仕様

| メッセージID | 種別 | 表示条件 | メッセージ内容 |
|-------------|------|---------|---------------|
| MSG-001 | 情報 | ユーザー数が1以上の場合 | "There is/are currently X user(s) online" |

## 例外処理

| 状況 | 対応 |
|------|------|
| Socket未接続 | socket?.emit() でオプショナルチェイニングを使用し、未接続時は何も送信しない |
| 認証失敗 | App.tsxの認証チェックでログイン画面へリダイレクト |
| 権限不足 | サーバー側でis_adminチェック。権限がない場合は接続処理を中断 |
| メッセージ未入力 | HTML5のrequired属性によりフォーム送信をブロック |

## 備考

- メッセージ送信履歴はブラウザのメモリ上（React State）にのみ保持され、ページ更新で消失する
- 送信されたメッセージは、送信元の管理画面にも即座に反映される（shoutイベントをリッスンしているため）
- パッド編集画面側では、メッセージは jQuery Gritter プラグインによるポップアップ通知として表示される
- stickyフラグがtrueの場合、ユーザーが手動で閉じるまで通知が表示され続ける
- 統計情報の取得には `/pluginfw/installer` 名前空間を使用し、メッセージ送信には `/settings` 名前空間を使用する

---

## コードリーディングガイド

本画面を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、メッセージの型定義とストア構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ShoutType.ts | `admin/src/components/ShoutType.ts` | ShoutType型の構造。メッセージ、sticky、タイムスタンプの階層構造を理解 |
| 1-2 | store.ts | `admin/src/store/store.ts` | Zustandストアの構造。settingsSocketとpluginsSocketの管理方法 |

**読解のコツ**: ShoutType.tsは非常にシンプルだが、ネストされた構造（data.payload.message.message）に注意。この構造は`COLLABROOM`メッセージフォーマットに準拠している。

#### Step 2: エントリーポイントを理解する

管理画面のルーティングとSocket.IO接続の初期化を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | main.tsx | `admin/src/main.tsx` | React Routerの設定。/shoutパスがShoutPageにルーティングされる点 |
| 2-2 | App.tsx | `admin/src/App.tsx` | Socket.IO接続の初期化。settingsSocketとpluginsSocketの2つの接続を確立 |

**主要処理フロー**:
1. **34-36行目**: settingsSocket（/settings名前空間）の接続初期化
2. **38-40行目**: pluginsSocket（/pluginfw/installer名前空間）の接続初期化
3. **42-44行目**: pluginsSocket接続成功時にストアへ保存
4. **47-52行目**: settingsSocket接続成功時にストアへ保存しloadイベントを発行

#### Step 3: 画面コンポーネントを理解する

ShoutPageコンポーネントの実装を詳細に読む。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ShoutPage.tsx | `admin/src/pages/ShoutPage.tsx` | 画面コンポーネントの全体像。ステート管理、イベントハンドラ、UI構造 |

**主要処理フロー**:
- **8-13行目**: ステート定義（totalUsers, message, sticky, shouts）
- **16-26行目**: Socket接続時のイベントリスナー設定（shout受信、stats受信）
- **29-33行目**: pluginSocket接続時にgetStatsイベントを発行
- **35-41行目**: sendMessage関数 - shoutイベントを発行してメッセージ送信
- **43-81行目**: JSX - UI構造の定義

#### Step 4: バックエンド処理を理解する

サーバー側のSocket.IOハンドラを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | adminsettings.ts | `src/node/hooks/express/adminsettings.ts` | /settings名前空間のハンドラ。shoutイベントの処理とブロードキャスト |
| 4-2 | adminplugins.ts | `src/node/hooks/express/adminplugins.ts` | /pluginfw/installer名前空間のハンドラ。getStatsイベントの処理 |

**主要処理フロー（adminsettings.ts）**:
- **21-25行目**: Socket.IO接続と管理者権限チェック
- **53-56行目**: ShoutMessage型定義
- **58-72行目**: shoutイベントハンドラ - メッセージ構築とブロードキャスト
- **70行目**: /settings名前空間へのブロードキャスト（管理画面へ）
- **71行目**: メイン名前空間へのブロードキャスト（パッド編集画面へ）

**主要処理フロー（adminplugins.ts）**:
- **42-45行目**: getStatsイベントハンドラ - 統計情報をJSON形式で返却

#### Step 5: クライアント側メッセージ受信処理を理解する

パッド編集画面でのメッセージ表示処理を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | pad.ts | `src/static/js/pad.ts` | パッド編集画面でのshoutイベント受信処理。Gritter通知の表示 |

**主要処理フロー（pad.ts）**:
- **266-278行目**: shoutイベントリスナー
- **267行目**: COLLABROOMタイプの確認
- **268行目**: タイムスタンプからDateオブジェクト生成
- **269-276行目**: $.gritter.add()でポップアップ通知を表示

### プログラム呼び出し階層図

```
[ShoutPage.tsx]
    │
    ├─ useStore() ─────────────────┬─ settingsSocket
    │                              └─ pluginsSocket
    │
    ├─ useEffect (Socket接続時)
    │      ├─ socket.on('shout') ─── setShouts() [履歴更新]
    │      └─ pluginSocket.on('results:stats') ─── setTotalUsers()
    │
    ├─ useEffect (pluginSocket接続時)
    │      └─ pluginSocket.emit('getStats')
    │
    └─ sendMessage()
           └─ socket.emit('shout', {message, sticky})
                   │
                   ▼
           [adminsettings.ts - サーバー]
                   │
                   ├─ io.of('/settings').emit('shout') ─── [ShoutPage.tsx 履歴更新]
                   │
                   └─ io.sockets.emit('shout') ─── [pad.ts - パッド編集画面]
                                                       └─ $.gritter.add() [通知表示]
```

### データフロー図

```
[入力]                    [処理]                         [出力]

message (string)  ───▶ sendMessage()
                              │
sticky (boolean) ────▶       │
                              ▼
                       socket.emit('shout')
                              │
                              ▼
                       [adminsettings.ts]
                              │
                              ├──▶ io.of('/settings').emit()
                              │           │
                              │           ▼
                              │    [ShoutPage.tsx]
                              │           │
                              │           ▼
                              │    shouts[] state更新
                              │           │
                              │           ▼
                              │    メッセージ履歴表示
                              │
                              └──▶ io.sockets.emit()
                                          │
                                          ▼
                                   [pad.ts]
                                          │
                                          ▼
                                   $.gritter.add()
                                          │
                                          ▼
                                   ポップアップ通知表示


[getStats フロー]

pluginSocket接続 ───▶ emit('getStats')
                              │
                              ▼
                       [adminplugins.ts]
                              │
                              ▼
                       stats.toJSON()
                              │
                              ▼
                       emit('results:stats')
                              │
                              ▼
                       [ShoutPage.tsx]
                              │
                              ▼
                       totalUsers state更新
                              │
                              ▼
                       ユーザー数表示
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ShoutPage.tsx | `admin/src/pages/ShoutPage.tsx` | ソース | コミュニケーション画面のReactコンポーネント |
| ShoutType.ts | `admin/src/components/ShoutType.ts` | ソース | メッセージの型定義 |
| store.ts | `admin/src/store/store.ts` | ソース | Zustandストア定義。Socket接続状態の管理 |
| main.tsx | `admin/src/main.tsx` | ソース | React Routerの設定 |
| App.tsx | `admin/src/App.tsx` | ソース | 管理画面のレイアウトとSocket.IO接続初期化 |
| adminsettings.ts | `src/node/hooks/express/adminsettings.ts` | ソース | /settings名前空間のSocket.IOハンドラ |
| adminplugins.ts | `src/node/hooks/express/adminplugins.ts` | ソース | /pluginfw/installer名前空間のSocket.IOハンドラ |
| stats.ts | `src/node/stats.ts` | ソース | measured-coreベースの統計情報コレクション |
| pad.ts | `src/static/js/pad.ts` | ソース | パッド編集画面のクライアントサイドJS。shoutメッセージの受信と表示 |
| App.css | `admin/src/App.css` | スタイル | 管理画面のCSS（Switchコンポーネント、送信フォームのスタイル含む） |
