# 通知設計書 16-リビジョン保存通知

## 概要

本ドキュメントは、Etherpadにおけるリビジョン保存通知の設計仕様を定義する。この通知は、ユーザーが現在のパッド状態をリビジョンとして保存した際に、保存完了を知らせるGritterポップアップ通知である。

### 本通知の処理概要

この通知はリビジョン保存操作が正常に完了したことをユーザーに伝え、保存されたリビジョンをタイムスライダーで確認できることを案内するためのアプリ内通知である。

**業務上の目的・背景**：共同編集環境において、重要なマイルストーン（文書の完成版、レビュー前の状態など）を明示的に保存しておきたい場合がある。リビジョン保存機能は、特定の時点のパッド状態に名前を付けて保存し、後でタイムスライダーから参照できるようにする。この通知により、ユーザーは保存操作が成功したことを即座に確認でき、保存されたリビジョンの確認方法（タイムスライダー）も案内される。

**通知の送信タイミング**：ユーザーがツールバーの「Save Revision」ボタンをクリックし、`pad_savedrevs.saveNow`関数が実行されると、サーバーへ`SAVE_REVISION`メッセージが送信される。通知自体はクライアント側で即座に表示され、サーバーでの保存完了を待たずに表示される（楽観的UI）。

**通知の受信者**：リビジョン保存操作を行ったユーザー本人のみ。他のユーザーには通知されない。

**通知内容の概要**：「This revision is now marked as a saved revision」というタイトルと「You can see saved revisions by visiting the timeslider」という説明文が表示される。これらのテキストはローカライズファイルから取得される。

**期待されるアクション**：ユーザーは通知を確認し、必要に応じてタイムスライダーを開いて保存されたリビジョンを確認する。通知は3秒後に自動的に消える。

## 通知種別

アプリ内通知（Gritterポップアップ）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（クライアント側ローカル処理） |
| 優先度 | 中 |
| リトライ | 無 |

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

リビジョン保存操作を行ったクライアントでのみローカルに通知が表示される。サーバーへの保存リクエストは別途送信されるが、通知表示は操作直後に行われる。

## 通知テンプレート

### ポップアップ通知の場合

| 項目 | 内容 |
|-----|------|
| タイトル | pad.savedrevs.marked（ローカライズキー） |
| メッセージ | pad.savedrevs.timeslider（ローカライズキー） |
| 表示時間 | 3000ミリ秒（3秒） |
| スティッキー | false |
| CSSクラス | saved-revision |

### 本文テンプレート

```javascript
window.$.gritter.add({
  // (string | mandatory) the heading of the notification
  title: html10n.get('pad.savedrevs.marked'),
  // (string | mandatory) the text inside the notification
  text: html10n.get('pad.savedrevs.timeslider') ||
      'You can view saved revisions in the timeslider',
  // (bool | optional) if you want it to fade out on its own or just sit there
  sticky: false,
  time: 3000,
  class_name: 'saved-revision',
});
```

### 添付ファイル

なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| pad.savedrevs.marked | 通知タイトル | src/locales/*.json | Yes |
| pad.savedrevs.timeslider | 通知テキスト | src/locales/*.json | Yes（フォールバックあり） |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| ユーザー操作 | ツールバーのSave Revisionクリック | なし（常に表示） | pad_savedrevs.saveNow実行 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| なし | 操作実行時は常に通知が表示される |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[Save Revisionボタンクリック] --> B[pad_savedrevs.saveNow呼び出し]
    B --> C[SAVE_REVISIONメッセージ送信]
    B --> D[$.gritter.add表示]
    C --> E[サーバー側処理]
    E --> F[handleSaveRevisionMessage]
    F --> G[pad.addSavedRevision]
    D --> H[3秒後に自動消去]
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| pad:{padId} | 現在のリビジョン番号取得 | pad.head |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| pad:{padId} | UPDATE | savedRevisionsに新しいリビジョン情報を追加 |

#### リビジョン保存

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| UPDATE | savedRevisions | リビジョン番号、作者ID、タイムスタンプを追加 | 配列に追加 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| サーバーエラー | 保存処理失敗 | 通知は既に表示済み、エラー通知は別途（現状未実装） |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（リトライなし） |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし（ユーザー操作に応じて表示） |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし

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

- リビジョン保存は認証されたセッションでのみ可能
- 保存されるリビジョン情報にはauthorIdが含まれ、誰が保存したか追跡可能
- 読み取り専用モードではリビジョン保存ボタンは非表示

## 備考

- 楽観的UIパターンを採用しており、サーバーの応答を待たずに通知が表示される
- ローカライズキーにフォールバック値が設定されている（'You can view saved revisions in the timeslider'）
- 保存されたリビジョンはclientVars.savedRevisionsとしてクライアントに送信される
- タイムスライダーで保存済みリビジョンは特別なマーカーで表示される

---

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

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

### 推奨読解順序

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

まず、保存されるリビジョン情報の構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Pad.ts | `src/node/db/Pad.ts` | savedRevisions配列の構造（revNum, savedById, timestamp） |

**読解のコツ**: addSavedRevision関数でどのような情報が保存されるかを確認する。

#### Step 2: クライアント側の処理を理解する

通知表示のエントリーポイントを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | pad_savedrevs.ts | `src/static/js/pad_savedrevs.ts` | saveNow関数（22-35行目）で通知表示とメッセージ送信 |

**主要処理フロー**:
1. **23行目**: `pad.collabClient.sendMessage({type: 'SAVE_REVISION'})`でサーバーへ送信
2. **24-34行目**: `window.$.gritter.add({...})`で通知表示

#### Step 3: サーバー側の処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | PadMessageHandler.ts | `src/node/handler/PadMessageHandler.ts` | handleSaveRevisionMessage関数（444-448行目） |

**主要処理フロー**:
- **446行目**: `padManager.getPad(padId, null, authorId)`でパッド取得
- **447行目**: `pad.addSavedRevision(pad.head, authorId)`でリビジョン保存

#### Step 4: ローカライズを確認

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | en.json | `src/locales/en.json` | pad.savedrevs.marked, pad.savedrevs.timeslider（194-195行目） |

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

```
[ツールバー] Save Revisionボタン
    │
    └─ pad_savedrevs.saveNow()
           │
           ├─ pad.collabClient.sendMessage({type: 'SAVE_REVISION'})
           │      │
           │      └─ [サーバー側]
           │             │
           │             └─ handleSaveRevisionMessage(socket, message)
           │                    │
           │                    └─ pad.addSavedRevision(pad.head, authorId)
           │
           └─ window.$.gritter.add({
                  title: html10n.get('pad.savedrevs.marked'),
                  text: html10n.get('pad.savedrevs.timeslider'),
                  sticky: false,
                  time: 3000,
                  class_name: 'saved-revision'
              })
```

### データフロー図

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

ボタンクリック ───▶ saveNow() ───▶ collabClient.sendMessage
                         │                    │
                         │                    ▼
                         │         handleSaveRevisionMessage
                         │                    │
                         │                    ▼
                         │           pad.addSavedRevision
                         │                    │
                         │                    ▼
                         │           DB保存（savedRevisions更新）
                         │
                         └───────▶ $.gritter.add
                                         │
                                         ▼
                                  ポップアップ表示（3秒後消去）
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| pad_savedrevs.ts | `src/static/js/pad_savedrevs.ts` | ソース | クライアント側リビジョン保存処理 |
| PadMessageHandler.ts | `src/node/handler/PadMessageHandler.ts` | ソース | サーバー側メッセージ処理 |
| Pad.ts | `src/node/db/Pad.ts` | ソース | パッドデータモデル、addSavedRevision実装 |
| en.json | `src/locales/en.json` | 設定 | ローカライズ文字列 |
| gritter.css | `src/static/skins/colibris/src/components/gritter.css` | スタイル | Gritterポップアップのスタイル |
