# 画面設計書 32-DiffContainer画面

## 概要

本ドキュメントは、Wikipedia iOSアプリにおけるDiffContainer画面の設計仕様を定義するものです。

### 本画面の処理概要

DiffContainer画面は、Wikipedia記事の2つのリビジョン間の差分（Diff）を表示するコンテナ画面です。編集履歴から選択した2つのバージョンの比較、単一リビジョンの表示、前後のリビジョンへのナビゲーション、感謝機能、取り消し/ロールバック機能など、包括的な差分閲覧機能を提供します。

**業務上の目的・背景**：
編集者やリーダーが記事の変更内容を詳細に確認するための画面です。どのような編集が行われたかを視覚的に把握し、必要に応じて編集を取り消したり、編集者に感謝を送ったりすることができます。Watchlist機能と連携し、監視対象記事の変更追跡を容易にします。

**画面へのアクセス方法**：
1. PageHistory画面（記事編集履歴）からリビジョンを選択
2. Watchlist画面からの遷移
3. 通知センターからの遷移
4. ディープリンク（revisionIDを指定したURL）

**主要な操作・処理内容**：
1. 2つのリビジョン間の差分表示（追加・削除・変更のハイライト）
2. 単一リビジョン（最初の編集）の内容表示
3. 前後のリビジョンへのナビゲーション
4. 編集者への感謝（Thanks）送信
5. 編集の取り消し（Undo）
6. ロールバック（管理者権限）
7. 差分URLの共有
8. ウォッチリストへの追加/削除

**画面遷移**：
- 遷移元：PageHistory画面、Watchlist画面、NotificationsCenter画面
- 遷移先：Article画面（記事タイトルタップ）、ユーザーページ、前後のDiffContainer画面

**権限による表示制御**：
- ログインユーザー：感謝ボタン、取り消しボタン使用可能
- 管理者権限保持者：ロールバックボタン表示
- 未ログインユーザー：閲覧のみ

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 25 | 差分比較 | 主機能 | 版間の差分を表示するコンテナ |
| 24 | 編集履歴 | 遷移元機能 | PageHistory画面からの遷移 |
| 51 | ウォッチリスト管理 | 補助機能 | 記事のウォッチ/アンウォッチ |

## 画面種別

詳細（差分表示）

## URL/ルーティング

ディープリンク対応：
- `fromRevisionID`: 比較元リビジョンID
- `toRevisionID`: 比較先リビジョンID
- `articleTitle`: 記事タイトル

## 入出力項目

| 項目名 | 入力/出力 | データ型 | 説明 |
|--------|----------|---------|------|
| siteURL | 入力 | URL | WikipediaサイトURL |
| fromRevisionID | 入力 | Int? | 比較元リビジョンID |
| toRevisionID | 入力 | Int? | 比較先リビジョンID |
| articleTitle | 入力 | String? | 記事タイトル |
| fromModel | 入力 | WMFPageHistoryRevision? | 比較元リビジョンモデル |
| toModel | 入力 | WMFPageHistoryRevision? | 比較先リビジョンモデル |
| theme | 入力 | Theme | テーマ設定 |

## 表示項目

| 項目名 | データ型 | 説明 |
|--------|---------|------|
| DiffListViewController | 子VC | 差分リスト表示 |
| DiffToolbarView | UIView | 下部ツールバー（前後/感謝/取消/More） |
| EmptyViewController | 子VC | 差分なし時の空状態表示 |
| DiffHeaderView | UIView | ヘッダー（記事タイトル、バイト差分） |

## イベント仕様

### 1-前のリビジョン表示

ツールバーの「Previous」ボタンをタップすると、現在の比較元リビジョンの前のリビジョンとの差分画面に遷移します。アニメーション付きでスライド遷移します。

### 2-次のリビジョン表示

ツールバーの「Next」ボタンをタップすると、現在の比較先リビジョンの次のリビジョンとの差分画面に遷移します。

### 3-感謝送信

ツールバーの「Thanks」ボタンをタップすると、比較先リビジョンの編集者に感謝を送信します。匿名ユーザーには送信不可。

### 4-取り消し（Undo）

ツールバーの「Undo」ボタンをタップすると、確認ダイアログを表示し、編集理由を入力後に取り消しを実行します。

### 5-ロールバック

Moreメニューから「Rollback」を選択すると、確認ダイアログ後に該当ユーザーの編集をロールバックします。

### 6-共有

Moreメニューから「Share」を選択すると、差分のWeb URLを共有します。

### 7-ウォッチ/アンウォッチ

Moreメニューから記事をウォッチリストに追加または削除します。

### 8-記事編集履歴表示

Moreメニューから「Article edit history」を選択すると、PageHistory画面に遷移します。

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 感謝送信 | RemoteNotifications | INSERT | 感謝通知記録 |
| 取り消し | - | API Call | MediaWiki API経由で編集取り消し |
| ロールバック | - | API Call | MediaWiki API経由でロールバック |
| ウォッチ/アンウォッチ | - | API Call | MediaWiki API経由でウォッチリスト更新 |

### テーブル別更新項目詳細

本画面はローカルデータベースを直接更新せず、主にMediaWiki APIを通じてサーバー側のデータを更新します。

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|--------------|---------|
| diff-undo-message | 確認 | "This will undo the changes made by the revisions(s)..." | 取り消し確認時 |
| diff-rollback-alert-title | 確認 | "Rollback edits" | ロールバック確認時 |
| diff-rollback-alert-message | 確認 | "Are you sure you want to rollback the edits?" | ロールバック確認時 |
| diff-thanks-sent-already | 警告 | "You've already sent a 'Thanks' for this edit" | 重複感謝時 |
| diff-thanks-anonymous-no-thanks | 警告 | "Anonymous users cannot be thanked" | 匿名ユーザーへの感謝時 |
| diffRollbackSuccess | 成功 | ロールバック成功メッセージ | ロールバック完了時 |
| diffUndoSuccess | 成功 | 取り消し成功メッセージ | 取り消し完了時 |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| ネットワークエラー | エラーアラート表示、リトライ可能 |
| 差分取得失敗 | EmptyViewControllerでエラー状態表示 |
| 感謝送信失敗 | エラーヒント表示 |
| 取り消し/ロールバック失敗 | エラーアラート表示 |
| 権限不足 | ブロックパネル表示 |

## 備考

- DiffRevisionTransitionによるカスタムアニメーション遷移
- WatchlistFunnelによるアナリティクスロギング
- 初回表示時にDiffEducationalPanelを表示（一度のみ）
- VoiceOver対応

---

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

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

### 推奨読解順序

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

差分表示に使用される主要なデータ構造を把握します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | WMFPageHistoryRevision | `WMF/` | リビジョン情報モデル（revisionID, user, timestamp等） |
| 1-2 | DiffContainerViewModel | `Wikipedia/Code/` | 画面状態管理（loading/data/empty/error） |
| 1-3 | DiffHeaderViewModel | `Wikipedia/Code/` | ヘッダー表示用ViewModel |

**読解のコツ**: DiffContainerViewModel.Stateの4つの状態（loading, data, empty, error）が画面表示を制御します。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | DiffContainerViewController.swift | `Wikipedia/Code/DiffContainerViewController.swift` | 2つのinitパターン（ディープリンク用/PageHistory用） |

**主要処理フロー**:
1. **100-124行目**: ディープリンク用init（revisionIDから初期化）
2. **126-151行目**: PageHistory用init（モデルから直接初期化）
3. **157-196行目**: viewDidLoadでセットアップ開始
4. **395-398行目**: startSetup()でツールバー設定、loading状態へ
5. **400-412行目**: midSetup()でヘッダー、リスト、画像取得
6. **414-431行目**: completeSetup()で差分取得実行

#### Step 3: 差分取得処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | DiffController | `Wikipedia/Code/` | 差分取得ロジック（API呼び出し） |

**主要処理フロー**:
- **786-814行目**: fetchDiff()で2リビジョン間の差分取得
- **743-770行目**: fetchFirstDiff()で最初のリビジョン差分取得

#### Step 4: ツールバー処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | DiffContainerViewController.swift | `Wikipedia/Code/DiffContainerViewController.swift` | DiffToolbarViewDelegateの実装 |

**主要処理フロー**:
- **1101-1125行目**: tappedPrevious()で前リビジョンへ遷移
- **1127-1142行目**: tappedNext()で次リビジョンへ遷移
- **1175-1181行目**: tappedThankButton()で感謝送信
- **1206-1249行目**: tappedUndo()で取り消し処理
- **1275-1298行目**: tappedRollback()でロールバック処理

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

```
DiffContainerViewController
    │
    ├─ init (ディープリンク or PageHistory)
    │
    ├─ viewDidLoad
    │      │
    │      ├─ startSetup() → setupToolbarIfNeeded()
    │      │
    │      ├─ populateModelsFromDeepLink() [ディープリンク時]
    │      │
    │      ├─ midSetup()
    │      │      ├─ populateNewHeaderViewModel()
    │      │      ├─ setupDiffListViewControllerIfNeeded()
    │      │      ├─ fetchLeadImageIfNeeded()
    │      │      └─ fetchWatchAndRollbackStatus()
    │      │
    │      └─ completeSetup()
    │             ├─ fetchDiff() / fetchFirstDiff()
    │             └─ populatePrevNextModelsForToolbar()
    │
    ├─ DiffToolbarViewDelegate
    │      ├─ tappedPrevious() → 新DiffContainerVC push
    │      ├─ tappedNext() → 新DiffContainerVC push
    │      ├─ tappedThankButton() → ThanksGiving
    │      ├─ tappedUndo() → WMFWatchlistDataController.undo()
    │      └─ tappedRollback() → WMFWatchlistDataController.rollback()
    │
    └─ WatchlistControllerDelegate
           ├─ didSuccessfullyWatchTemporarily()
           ├─ didSuccessfullyWatchPermanently()
           └─ didSuccessfullyUnwatch()
```

### データフロー図

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

fromRevisionID        ──────▶ DiffController.             ──────▶ DiffListViewController
toRevisionID                   fetchDiff()                         (差分リスト表示)
                               │
                               ▼
WMFPageHistoryRevision ──────▶ DiffHeaderViewModel        ──────▶ DiffHeaderView
(from/to)                      │                                   (ヘッダー表示)
                               ▼
User Action            ──────▶ DiffToolbarViewDelegate    ──────▶ API呼び出し
(ボタンタップ)                  │                                   (感謝/取消/ロールバック)
                               ▼
API Response           ──────▶ State更新                  ──────▶ UI更新
                               (data/empty/error)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| DiffContainerViewController.swift | `Wikipedia/Code/DiffContainerViewController.swift` | ソース | メインコンテナVC |
| DiffListViewController.swift | `Wikipedia/Code/DiffListViewController.swift` | ソース | 差分リスト表示 |
| DiffToolbarView.swift | `Wikipedia/Code/DiffToolbarView.swift` | ソース | 下部ツールバー |
| DiffController.swift | `Wikipedia/Code/DiffController.swift` | ソース | 差分取得ロジック |
| DiffContainerViewModel.swift | `Wikipedia/Code/DiffContainerViewModel.swift` | ソース | 状態管理ViewModel |
| DiffHeaderViewModel.swift | `Wikipedia/Code/DiffHeaderViewModel.swift` | ソース | ヘッダーViewModel |
| WatchlistController.swift | `Wikipedia/Code/WatchlistController.swift` | ソース | ウォッチリスト制御 |
| WMFWatchlistDataController | `WMFData/` | ソース | 取消/ロールバックAPI |
| DiffRevisionTransition.swift | `Wikipedia/Code/DiffRevisionTransition.swift` | ソース | 遷移アニメーション |
