# 通知設計書 10-エントリ公開成功通知

## 概要

本ドキュメントは、Decap CMSの編集ワークフローにおいてエントリの公開が正常に完了した場合に表示される成功トースト通知の設計仕様を定義する。ワークフロー上でエントリを公開（publish）し、バックエンドでの公開処理が成功した際にフィードバックとして表示される。

### 本通知の処理概要

本通知は、Decap CMSの編集ワークフロー機能においてエントリの公開処理が成功した場合に発火する成功トースト通知である。`publishUnpublishedEntry` 関数内で `backend.publishUnpublishedEntry(entry)` のPromiseが正常にresolveした直後のtryブロック内で通知が発行される。公開後にはメディアライブラリの再読み込みやエントリの再読み込みなどの後処理も実行される。

**業務上の目的・背景**：編集ワークフローにおいて、レビューが完了したエントリを公開する操作のフィードバックを提供する。公開が正常に完了したことをコンテンツ編集者やレビュアーに即座に伝え、公開サイトへの反映が行われたことを確認させる。コンテンツ管理チームのワークフロー運用を支援する重要な通知である。

**通知の送信タイミング**：バックエンドの `backend.publishUnpublishedEntry(entry)` のPromiseが正常にresolveし、メディアライブラリの再読み込み（`loadMedia()`）が dispatch された直後に通知が発行される。

**通知の受信者**：エントリの公開操作を行った当該ユーザー本人。ブラウザ画面上のトースト通知として表示される。

**通知内容の概要**：i18nキー `ui.toast.entryPublished` に基づくメッセージが表示される。英語ロケールでは `Entry published` という簡潔な固定メッセージ。

**期待されるアクション**：ユーザーは公開成功を確認し、公開サイトでの表示確認を行うか、次のエントリの編集・公開作業に移る。特別な対応は不要。

## 通知種別

アプリ内通知（トースト通知）

react-toastify ライブラリを使用したブラウザ内トースト通知。画面右上（`top-right`）に `colored` テーマで表示される。

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（Redux dispatch → react-toastify） |
| 優先度 | 中（成功フィードバック） |
| リトライ | なし |

### 送信先決定ロジック

公開操作を実行したユーザーのブラウザセッションに対して直接表示する。

## 通知テンプレート

### メール通知の場合

該当なし

### 本文テンプレート

```
Entry published
```

i18nキー `ui.toast.entryPublished` の英語テンプレート。動的変数を含まない固定メッセージ。

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| （なし） | 本通知には動的変数がない | - | - |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 画面操作 | エントリ公開操作 | `publishUnpublishedEntry` 内で `backend.publishUnpublishedEntry(entry)` が正常に完了 | ワークフローからのエントリ公開成功時（editorialWorkflow.ts 493-499行目） |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| 公開失敗時 | APIが例外をスローした場合は本通知を発行せず、エントリ公開失敗通知（No.11）が発行される |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[ユーザーが公開操作] --> B[publishUnpublishedEntry 発火]
    B --> C[dispatch unpublishedEntryPublishRequest]
    C --> D[backend.publishUnpublishedEntry 呼び出し]
    D -->|成功| E[dispatch loadMedia - メディアライブラリ再読み込み]
    D -->|失敗| F[エントリ公開失敗通知]
    E --> G[dispatch addNotification - 成功トースト表示]
    G --> H[dispatch unpublishedEntryPublished]
    H --> I{collection.has nested?}
    I -->|Yes| J[dispatch loadEntries / loadEntry / navigateToEntry]
    I -->|No| K[dispatch loadEntry]
    J --> L[Notifications コンポーネントがトースト描画]
    K --> L
    L --> M[4秒後に自動消去]
    M --> N[終了]
    F --> N
```

## データベース参照・更新仕様

### 参照テーブル一覧

該当なし

### 更新テーブル一覧

該当なし（バックエンドでの公開処理は別途実行されるが、通知の発行に関してはデータベース更新はない）

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 該当なし | 本通知は成功時のみ発行されるため、エラーケースは存在しない | - |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし |
| リトライ間隔 | 該当なし |
| リトライ対象エラー | 該当なし |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | なし |
| 1日あたり上限 | なし |

### 配信時間帯

制限なし

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

- 固定メッセージのみが表示されるため、セキュリティ上の懸念はない

## 備考

- 公開成功後の後処理として以下が実行される：
  1. `dispatch(loadMedia())` - メディアライブラリの再読み込み（editorialWorkflow.ts 492行目）
  2. `dispatch(unpublishedEntryPublished(collectionName, slug))` - Redux storeの公開状態更新（editorialWorkflow.ts 500行目）
  3. コレクションが `nested` 属性を持つ場合: `loadEntries` + `loadEntry` + `navigateToEntry`（editorialWorkflow.ts 502-508行目）
  4. コレクションが `nested` 属性を持たない場合: `loadEntry`（editorialWorkflow.ts 510行目）
- `slugFromCustomPath` 関数でnestedコレクションの場合にカスタムパスからスラグを導出する処理がある（editorialWorkflow.ts 504行目）
- 本通知は編集ワークフロー（`publish_mode: editorial_workflow`）が有効な場合にのみ使用される機能に関連する

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | notifications.ts (actions) | `packages/decap-cms-core/src/actions/notifications.ts` | 成功通知のペイロード構造。`type: 'success'`, `dismissAfter: 4000` |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | editorialWorkflow.ts | `packages/decap-cms-core/src/actions/editorialWorkflow.ts` | `publishUnpublishedEntry` 関数（482-523行目）全体のフローを理解する。特にtryブロック（489-511行目）に注目。 |

**主要処理フロー**:
1. **488行目**: `dispatch(unpublishedEntryPublishRequest(collectionName, slug))` - 公開リクエスト状態
2. **490行目**: `await backend.publishUnpublishedEntry(entry)` - バックエンドへ公開リクエスト
3. **492行目**: `dispatch(loadMedia())` - メディアライブラリの再読み込み
4. **493-498行目**: `addNotification` で成功トースト通知を発行（type: 'success', dismissAfter: 4000）
5. **500行目**: `dispatch(unpublishedEntryPublished(collectionName, slug))` - 公開状態更新
6. **501-510行目**: コレクション種別に応じたエントリ再読み込み・ナビゲーション処理

#### Step 3: i18nメッセージテンプレートを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.js (en) | `packages/decap-cms-locales/src/en/index.js` | 276行目: `entryPublished: 'Entry published'` |

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

```
publishUnpublishedEntry (editorialWorkflow.ts:482)
    |
    +-- dispatch(unpublishedEntryPublishRequest()) (editorialWorkflow.ts:488)
    |
    +-- await backend.publishUnpublishedEntry(entry) (editorialWorkflow.ts:490)
    |
    +-- [成功時] try ブロック (editorialWorkflow.ts:489)
    |       |
    |       +-- dispatch(loadMedia()) (editorialWorkflow.ts:492)
    |       |
    |       +-- dispatch(addNotification({key:'ui.toast.entryPublished', type:'success'}))
    |       |       |
    |       |       +-- [Redux] NOTIFICATION_SEND → notifications reducer → toast()
    |       |
    |       +-- dispatch(unpublishedEntryPublished()) (editorialWorkflow.ts:500)
    |       |
    |       +-- [nested] dispatch(loadEntries()) + loadEntry() + navigateToEntry()
    |       |
    |       +-- [非nested] dispatch(loadEntry())
    |
    +-- [失敗時] catch ブロック (editorialWorkflow.ts:512)
            +-- エントリ公開失敗通知
```

### データフロー図

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

公開操作               -->  backend.publishUnpublished  -->  [成功] void
(collectionName, slug)      Entry(entry)                      |
                                                              v
                                                         loadMedia()
                                                              |
                                                              v
(固定メッセージ)       -->  addNotification()          -->  Redux store
                           (i18nキー: ui.toast.               (notifications配列)
                            entryPublished, type:'success')    |
                                                              v
notifications          -->  Notifications コンポーネント -->  react-toastify
(Redux state)               (t() でi18n変換)                 トースト UI表示
                                                              (画面右上, 4秒で消去)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| editorialWorkflow.ts | `packages/decap-cms-core/src/actions/editorialWorkflow.ts` | ソース | `publishUnpublishedEntry` 関数が公開成功時にトースト通知を発行 |
| notifications.ts (actions) | `packages/decap-cms-core/src/actions/notifications.ts` | ソース | 通知アクション作成関数 |
| notifications.ts (reducers) | `packages/decap-cms-core/src/reducers/notifications.ts` | ソース | 通知リデューサー |
| Notifications.tsx | `packages/decap-cms-core/src/components/UI/Notifications.tsx` | ソース | 通知UIコンポーネント |
| index.js (en) | `packages/decap-cms-locales/src/en/index.js` | i18nリソース | 英語ロケール定義 |
| backend.ts | `packages/decap-cms-core/src/backend.ts` | ソース | `publishUnpublishedEntry` メソッドの実装 |
