# 機能設計書 72-通知システム

## 概要

本ドキュメントは、Decap CMSのトースト形式の通知表示機能の設計を記述する。

### 本機能の処理概要

本機能は、CMS管理画面内でユーザーに対して操作結果や警告をトースト形式の通知で表示する仕組みを提供する。Redux アクション、リデューサー、Reactコンポーネントの3層構成で、通知の送信・表示・消去を管理する。

**業務上の目的・背景**：CMSの各種操作（エントリ保存、メディアアップロード、エラー発生等）の結果をユーザーに即座にフィードバックする必要がある。トースト形式の通知は画面遷移を伴わず、作業の中断を最小限にしてユーザーに情報を伝達できる。

**機能の利用シーン**：エントリの保存成功/失敗、メディアアップロード完了、バックエンドAPI接続エラー、認証期限切れ警告、バリデーションエラーなど、あらゆる操作結果のフィードバック時に利用される。

**主要な処理内容**：
1. **通知送信（addNotification）**: 通知メッセージ・タイプ・自動消去時間を含むNotificationPayloadをReduxストアにディスパッチ
2. **通知消去（dismissNotification）**: 特定IDの通知をReduxストアから削除
3. **全通知クリア（clearNotifications）**: すべての通知をReduxストアから一括削除
4. **通知表示（Notifications）**: Reduxストアの通知状態を監視し、react-toastifyのToastContainerにトースト通知を表示
5. **自動消去**: dismissAfter値が設定された通知は、指定ミリ秒後に自動的に消去される
6. **ローカライズ対応**: 通知メッセージがNotificationMessage型（key付き）の場合、react-polyglotのtranslate関数でローカライズされる

**関連システム・外部連携**：react-toastify（トースト表示ライブラリ）、Redux（状態管理）、react-polyglot（国際化）と連携する。

**権限による制御**：通知表示自体には権限制御はない。通知を送信するアクションは各機能の権限制御に依存する。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 全画面共通 | 主画面 | ToastContainerはApp.jsのルートレベルで描画され、全画面で通知を表示可能 |

## 機能種別

ユーザー通知 / UI状態管理

## 入力仕様

### 入力パラメータ

**addNotification アクション**

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| message | string \| NotificationMessage | Yes | 通知メッセージ。文字列またはローカライズキー付きオブジェクト | - |
| type | TypeOptions \| undefined | No | 通知タイプ（'success' / 'error' / 'warning' / 'info'） | react-toastifyのTypeOptions型 |
| dismissAfter | number | No | 自動消去までのミリ秒数 | - |

**NotificationMessage 型**

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| key | string | Yes | ローカライズキー | - |
| details | unknown | No | メッセージの追加パラメータ | - |

**dismissNotification アクション**

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | string | Yes | 消去対象の通知ID | - |

### 入力データソース

- 各機能のReduxアクション（entries, mediaLibrary, editorialWorkflow等）からaddNotificationが呼び出される
- react-toastifyのonChangeコールバックから、ユーザーによるトースト閉じ操作を検知

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| Notification | object | id(UUID), message, dismissAfter, typeを持つ通知オブジェクト |
| ToastContainer | JSX.Element | react-toastifyのトースト表示コンテナ |

### 出力先

- 画面右上（position="top-right"）にトースト通知として表示
- Reduxストアのnotifications.notifications配列に保存

## 処理フロー

### 処理シーケンス

```
1. 通知送信
   └─ アクションクリエイター addNotification(payload) をディスパッチ
2. Reduxリデューサー処理
   └─ NOTIFICATION_SEND: UUIDを付与して通知配列に追加
3. Notificationsコンポーネントの再レンダリング
   └─ useEffectでnotifications配列の変更を検知
4. react-toastifyへの通知登録
   └─ 未表示の通知をfilterし、toast()で表示。idMapで内部ID管理
5. 自動消去タイマー設定
   └─ dismissAfter設定時、setTimeout後にdismissNotificationをディスパッチ
6. ユーザーによる閉じ操作
   └─ toast.onChangeでremoved状態を検知し、dismissNotificationをディスパッチ
```

### フローチャート

```mermaid
flowchart TD
    A[各機能アクション] --> B[addNotification ディスパッチ]
    B --> C[リデューサー: UUID付与して通知追加]
    C --> D[Notifications コンポーネント再レンダリング]
    D --> E{未表示の通知?}
    E -->|Yes| F[toast でトースト表示]
    F --> G{dismissAfter 設定?}
    G -->|Yes| H[setTimeout で自動消去]
    G -->|No| I[ユーザー操作待ち]
    H --> J[dismissNotification ディスパッチ]
    I --> K[toast.onChange: removed検知]
    K --> J
    J --> L[リデューサー: 通知配列からフィルタ除去]
    L --> M[toast.dismiss でトースト非表示]
    E -->|No| N[変更なし]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-72-01 | UUID付与 | 各通知にはuuid v4でユニークIDが付与される | NOTIFICATION_SEND時 |
| BR-72-02 | 自動消去 | dismissAfterが設定された通知は、指定ミリ秒後にdismissNotificationが呼ばれる | dismissAfterが数値の場合 |
| BR-72-03 | ローカライズ表示 | メッセージがNotificationMessage型の場合、t(key, details)でローカライズされる | messageがオブジェクトの場合 |
| BR-72-04 | idMap管理 | react-toastifyのtoastIdとReduxのnotification.idをマッピングする | 常時 |
| BR-72-05 | トースト位置 | 通知はposition="top-right"、theme="colored"で表示される | 常時 |

### 計算ロジック

特になし。

## データベース操作仕様

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

本機能はReduxストア上の状態管理のみであり、データベース操作は行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 表示エラー | react-toastifyの初期化失敗 | injectStyleでCSSが注入される。失敗時はスタイルなし表示 |

### リトライ仕様

リトライは不要。通知表示は非同期処理を伴わない。

## トランザクション仕様

Reduxストアの更新はimmerのproduceによるイミュータブル更新パターンで行われる。

## パフォーマンス要件

- 通知表示は100ms以内にトーストが画面に表示されること
- 大量の通知が同時に送信された場合でも、react-toastifyのキュー管理により順次表示される

## セキュリティ考慮事項

- 通知メッセージにHTMLを含む場合、react-toastifyのデフォルト設定ではHTMLエスケープされる
- ユーザー入力値を直接通知メッセージに含める場合は、XSSリスクに注意が必要

## 備考

- react-toastifyのCSSはinjectStyle()により動的に注入される（CSS-in-JS方式）
- NotificationsコンポーネントはApp.jsのルートレベルに配置される
- 通知のtype値はreact-toastifyのTypeOptions型（'success', 'error', 'warning', 'info', 'default'）に準拠する

---

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

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

### 推奨読解順序

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

通知の型定義とアクション定義を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | notifications.ts (actions) | `packages/decap-cms-core/src/actions/notifications.ts` | NotificationPayload, NotificationMessage, NotificationsAction の型定義とアクションクリエイター |

**読解のコツ**: TypeScriptの型定義に注目。NotificationPayloadのmessageフィールドがstring | NotificationMessageのunion型であることが重要。

**主要処理フロー**:
- **3-6行目**: NotificationMessage型定義（key: ローカライズキー、details: 追加パラメータ）
- **8-12行目**: NotificationPayload型定義（message, dismissAfter, type）
- **14-16行目**: アクションタイプ定数定義
- **18-28行目**: addNotification, dismissNotification, clearNotifications アクションクリエイター

#### Step 2: リデューサーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | notifications.ts (reducers) | `packages/decap-cms-core/src/reducers/notifications.ts` | Notification型、NotificationsState型、リデューサーの状態更新ロジック |

**主要処理フロー**:
- **17-22行目**: Notification型定義（id: UUID, message, dismissAfter, type）
- **24-26行目**: NotificationsState型（notifications配列）
- **28-30行目**: デフォルト状態（空配列）
- **32-50行目**: immer produceによるリデューサー
  - **34-36行目**: NOTIFICATIONS_CLEAR - 配列を空にする
  - **37-38行目**: NOTIFICATION_DISMISS - idでフィルタ除去
  - **40-47行目**: NOTIFICATION_SEND - UUIDを付与して配列に追加

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Notifications.tsx | `packages/decap-cms-core/src/components/UI/Notifications.tsx` | react-toastifyとReduxの統合。idMap管理とトースト表示ロジック |

**主要処理フロー**:
- **15行目**: injectStyle() - react-toastifyのCSSを動的注入
- **25-28行目**: Notificationsコンポーネント。useTranslate, useDispatch, useState(idMap)
- **30-61行目**: useEffect - notifications変更時のトースト表示/消去ロジック
  - **31-33行目**: 未表示通知のフィルタリング
  - **34-42行目**: toast()でトースト表示。ローカライズキーの場合はt()で変換
  - **44-46行目**: idMapの更新
  - **47-51行目**: dismissAfter設定時のsetTimeoutによる自動消去
  - **54-60行目**: 削除された通知に対応するトーストの消去
- **63-70行目**: toast.onChange - ユーザー操作によるトースト閉じの検知とdismissNotification
- **72-76行目**: ToastContainer描画（position="top-right", theme="colored"）
- **79-81行目**: mapStateToProps - Reduxストアからnotifications取得

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

```
各機能アクション (entries.ts, mediaLibrary.ts 等)
    |
    +-- addNotification(payload) [actions/notifications.ts]
    |       |
    |       +-- Redux dispatch -> notifications reducer [reducers/notifications.ts]
    |                |
    |                +-- UUID生成 (uuid v4)
    |                +-- 通知配列更新 (immer produce)
    |
    +-- Notifications コンポーネント [components/UI/Notifications.tsx]
            |
            +-- useEffect: 通知変更検知
            |       +-- toast() [react-toastify]
            |       +-- setTimeout -> dismissNotification
            |
            +-- toast.onChange: ユーザー操作検知
            |       +-- dismissNotification
            |
            +-- ToastContainer [react-toastify]
```

### データフロー図

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

アクション呼び出し       -->  addNotification               -->  Redux store 更新
  (message, type,              (NOTIFICATION_SEND)               (notifications 配列)
   dismissAfter)                      |
                                      v
                              notifications reducer          -->  Notifications コンポーネント
                              (UUID付与、配列追加)                    |
                                                                    v
                                                              react-toastify toast()  -->  トースト通知表示
                                                                    |                       (画面右上)
                                                                    v
                              dismissNotification            <--  自動消去/ユーザー操作
                              (NOTIFICATION_DISMISS)               |
                                      |                            v
                                      v                      toast.dismiss()
                              配列からフィルタ除去            -->  トースト非表示
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| notifications.ts | `packages/decap-cms-core/src/actions/notifications.ts` | ソース | アクションクリエイターと型定義 |
| notifications.ts | `packages/decap-cms-core/src/reducers/notifications.ts` | ソース | リデューサー（状態更新ロジック） |
| Notifications.tsx | `packages/decap-cms-core/src/components/UI/Notifications.tsx` | ソース | 通知表示UIコンポーネント |
| index.js | `packages/decap-cms-core/src/components/UI/index.js` | ソース | UIコンポーネントのエクスポート |
| App.js | `packages/decap-cms-core/src/components/App/App.js` | ソース | Notificationsコンポーネントの配置先 |
