# 機能設計書 24-編集履歴

## 概要

本ドキュメントは、Wikipedia iOSアプリケーションにおける編集履歴機能の設計を記載する。この機能により、ユーザーは記事の編集履歴（リビジョン一覧）を閲覧し、任意の2つのリビジョンを比較できる。

### 本機能の処理概要

編集履歴機能は、記事の全リビジョンを時系列で表示し、各リビジョンの編集者、編集日時、サイズ変更、コメントを確認できる。また、2つのリビジョンを選択して差分を比較する機能を提供する。

**業務上の目的・背景**：Wikipediaは版管理システムを持ち、すべての編集がリビジョンとして記録される。編集履歴の閲覧により、記事の変遷を追跡し、誰がいつ何を編集したかを確認できる。これは荒らしの検出や、記事の品質管理に重要な機能である。

**機能の利用シーン**：ユーザーが記事の変更履歴を確認したい場合、特定の編集を調査したい場合、または以前の版に戻したい場合に使用される。「比較」モードでは2つのリビジョンを選択して差分を確認できる。

**主要な処理内容**：
1. リビジョン一覧のページング取得
2. 編集統計情報の表示（総編集数、初回編集日、編集者種別内訳、時系列グラフ）
3. リビジョンセルの表示（日時、編集者、サイズ差分、コメント）
4. 単一リビジョン詳細への遷移
5. 比較モードでの2リビジョン選択
6. 差分比較画面への遷移

**関連システム・外部連携**：MediaWiki API（revisions、edit counts、edit metrics）、PageHistoryFetcher

**権限による制御**：閲覧は全ユーザー可能。差分比較からの元に戻し操作は編集権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 記事画面 | 呼び出し元 | オーバーフローメニューから遷移 |
| - | 編集履歴画面 | 主画面 | リビジョン一覧表示 |
| - | 差分比較画面 | 遷移先 | リビジョン選択後に遷移 |
| - | 比較選択バー | 埋め込み | 2リビジョン選択UI |

## 機能種別

データ取得、一覧表示、選択操作、UI制御

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| pageTitle | String | Yes | 記事タイトル | 空文字列以外 |
| pageURL | URL | Yes | 記事URL | 有効なWikipedia記事URL |
| articleSummaryController | ArticleSummaryController | Yes | 記事サマリコントローラ | 非nil |
| authenticationManager | WMFAuthenticationManager | Yes | 認証マネージャ | 非nil |

### 入力データソース

- PageHistoryFetcher（リビジョン一覧、編集統計、編集メトリクス）
- MediaWiki API

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| pageHistorySections | [PageHistorySection] | 日付でグループ化されたリビジョンセクション |
| totalEditCount | Int? | 総編集数 |
| firstEditDate | Date? | 初回編集日 |
| editCountsGroupedByType | EditCountsGroupedByType? | 編集者種別ごとの編集数 |
| timeseriesOfEditsCounts | [NSNumber]? | 時系列編集数データ |

### 出力先

- PageHistoryCollectionViewCell（リビジョン表示）
- PageHistoryCountsView（統計ヘッダー）
- DiffContainerViewController（差分比較画面）

## 処理フロー

### 処理シーケンス

```
1. 編集履歴画面の初期化
   ├─ CollectionViewのセットアップ
   ├─ 比較選択ViewControllerの埋め込み
   └─ テーマ適用

2. viewDidLoad
   ├─ getEditCounts()：編集統計の取得
   │   ├─ 初回リビジョン取得
   │   ├─ 総編集数取得
   │   ├─ 編集者種別ごとの編集数取得
   │   └─ 時系列メトリクス取得
   │
   └─ getPageHistory()：リビジョン一覧の取得

3. ページング
   └─ scrollViewDidScroll でコンテンツ末端に近づいたら追加取得

4. 通常モード（idle）
   └─ リビジョンタップ → 単一リビジョン差分画面へ遷移

5. 比較モード（editing）
   ├─ 「比較」ボタンタップで比較モード開始
   ├─ 最大2つのリビジョンを選択
   ├─ 選択完了後「比較」ボタンが有効化
   └─ 比較実行 → 差分比較画面へ遷移
```

### フローチャート

```mermaid
flowchart TD
    A[編集履歴開始] --> B[統計・リビジョン取得]
    B --> C[一覧表示]
    C --> D{状態}
    D -->|idle| E[リビジョンタップ]
    E --> F[単一リビジョン差分表示]
    D -->|「比較」タップ| G[editing状態へ]
    G --> H[1つ目選択]
    H --> I[2つ目選択]
    I --> J[「比較」有効化]
    J --> K[比較実行]
    K --> L[差分比較画面]
    G --> M[キャンセル]
    M --> D
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 最大選択数 | 比較モードでは最大2つのリビジョンを選択可能 | editing状態時 |
| BR-002 | 選択順序の自動調整 | 比較時は古いリビジョンがfrom、新しいリビジョンがtoになる | 2リビジョン選択完了時 |
| BR-003 | ページング | スクロール位置がコンテンツ末端から200px以内で追加取得 | batchComplete=false時 |
| BR-004 | 日付セクション | リビジョンは日付ごとにセクション分け | 常時 |
| BR-005 | 編集者アイコン | 匿名/一時アカウント/登録ユーザーでアイコンが異なる | 常時 |
| BR-006 | マイナー編集表示 | マイナー編集は専用表示 | isMinor=true時 |

### 計算ロジック

| 計算項目 | 計算式・ロジック |
|---------|----------------|
| サイズ差分 | 現リビジョンサイズ - 前リビジョンサイズ（バイト） |
| ページング判定 | maxY >= collectionView.contentSize.height - 200 |

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

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

本機能はローカルデータベースへの操作は行わない。すべてのデータはAPI経由で取得。

### API呼び出し一覧

| API | 操作 | 概要 |
|-----|------|------|
| fetchRevisionInfo | GET | リビジョン一覧取得（ページング対応） |
| fetchFirstRevision | GET | 初回リビジョン取得（編集開始日の特定） |
| fetchEditCounts | GET | 編集数統計取得 |
| fetchEditMetrics | GET | 時系列編集メトリクス取得 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| networkError | 接続エラー | ネットワーク接続なし | エラーアラート表示 |
| apiError | API エラー | API からのエラー応答 | エラーメッセージ表示 |

### リトライ仕様

自動リトライなし。スクロールによるページング再トリガーで再取得可能。

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

該当なし（読み取り専用操作）

## パフォーマンス要件

- 初回リビジョン取得：3秒以内
- ページング取得：スムーズなスクロール体験のため、事前取得（200px手前）
- セルレイアウトキャッシュによる再計算の回避

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

- 編集履歴は公開情報のため、認証なしで閲覧可能
- EditHistoryCompareFunnelによる利用ログ記録

## 備考

- State enumでidle/editingの2状態を管理
- SelectionOrderで1つ目/2つ目の選択を区別
- SelectionThemeModelで選択状態のテーマカラーを管理
- DiffRevisionRetrievingプロトコルで差分画面との連携

---

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

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

### 推奨読解順序

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

編集履歴で使用される状態管理と選択順序を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | PageHistoryViewController.swift | `Wikipedia/Code/PageHistoryViewController.swift` | SelectionOrder enum（L6-17） |
| 1-2 | PageHistoryViewController.swift | `Wikipedia/Code/PageHistoryViewController.swift` | State enum（L88-91）, SelectionThemeModel（L559-583） |

**読解のコツ**: SelectionOrderは比較用の1つ目/2つ目の選択を表す。Stateはidle（通常）/editing（比較モード）の状態を管理する。

#### Step 2: 初期化とデータ取得を理解する

編集履歴画面の初期化とAPI呼び出しを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | PageHistoryViewController.swift | `Wikipedia/Code/PageHistoryViewController.swift` | init, viewDidLoad（L56-182） |

**主要処理フロー**:
- **L56-64**: init - pageTitle, pageURL, PageHistoryFetcher初期化
- **L164-182**: viewDidLoad - CollectionView設定、比較選択VC埋め込み、getEditCounts/getPageHistory呼び出し

#### Step 3: 編集統計取得を理解する

編集数と統計情報の取得処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | PageHistoryViewController.swift | `Wikipedia/Code/PageHistoryViewController.swift` | getEditCounts（L188-259） |

**主要処理フロー**:
- **L189-225**: fetchFirstRevision → fetchEditCounts(.edits) で総編集数と初回編集日取得
- **L227-241**: fetchEditCounts(複数タイプ) で編集者種別ごとの統計取得
- **L243-258**: fetchEditMetrics で時系列データ取得

#### Step 4: リビジョン一覧取得を理解する

ページング対応のリビジョン取得を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | PageHistoryViewController.swift | `Wikipedia/Code/PageHistoryViewController.swift` | getPageHistory, shouldLoadNewData（L38-47, L307-327） |

**主要処理フロー**:
- **L38-47**: shouldLoadNewData - ページング判定（末端200px以内）
- **L307-327**: getPageHistory - fetchRevisionInfo呼び出しとセクション追加
- **L329-335**: scrollViewDidScroll - ページングトリガー

#### Step 5: 比較モードを理解する

比較用のリビジョン選択と状態遷移を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | PageHistoryViewController.swift | `Wikipedia/Code/PageHistoryViewController.swift` | state設定, compare, cancelComparison（L103-128, L337-344） |

**主要処理フロー**:
- **L103-128**: state didSet - idle/editingの状態遷移処理
- **L337-340**: compare - 比較モード開始
- **L342-344**: cancelComparison - 比較モードキャンセル

#### Step 6: リビジョン選択と差分表示を理解する

選択操作と差分画面への遷移を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 6-1 | PageHistoryViewController.swift | `Wikipedia/Code/PageHistoryViewController.swift` | didSelectItemAt, showDiff（L355-365, L706-750） |
| 6-2 | PageHistoryViewController.swift | `Wikipedia/Code/PageHistoryViewController.swift` | pageHistoryComparisonSelectionViewControllerDidTapCompare（L814-841） |

**主要処理フロー**:
- **L706-750**: didSelectItemAt - idle時は単一リビジョン差分、editing時は選択処理
- **L355-365**: showDiff - DiffContainerViewController生成と遷移
- **L814-841**: 比較実行 - 選択された2リビジョンの日付比較と差分表示

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

```
ArticleViewController / TalkPageViewController
    │
    └─ PageHistoryViewController.init(pageTitle:pageURL:)
           │
           ├─ viewDidLoad()
           │      ├─ getEditCounts()
           │      │      ├─ PageHistoryFetcher.fetchFirstRevision()
           │      │      ├─ PageHistoryFetcher.fetchEditCounts()
           │      │      └─ PageHistoryFetcher.fetchEditMetrics()
           │      │
           │      └─ getPageHistory()
           │             └─ PageHistoryFetcher.fetchRevisionInfo()
           │
           ├─ scrollViewDidScroll()
           │      └─ getPageHistory()（ページング）
           │
           ├─ compare() → state = .editing
           │
           ├─ collectionView(_:didSelectItemAt:)
           │      ├─ [idle] pushToSingleRevisionDiff()
           │      │            └─ showDiff() → DiffContainerViewController
           │      │
           │      └─ [editing] リビジョン選択処理
           │
           └─ pageHistoryComparisonSelectionViewControllerDidTapCompare()
                  └─ showDiff() → DiffContainerViewController
```

### データフロー図

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

pageTitle, pageURL    ───▶ PageHistoryFetcher               ───▶ CollectionView
                              │
                              ├─ fetchFirstRevision()
                              │        │
                              │        ▼
                              │  firstEditDate, firstRevision
                              │
                              ├─ fetchEditCounts()
                              │        │
                              │        ▼
                              │  totalEditCount, editCountsGroupedByType
                              │
                              ├─ fetchEditMetrics()
                              │        │
                              │        ▼
                              │  timeseriesOfEditsCounts
                              │
                              └─ fetchRevisionInfo()（ページング）
                                       │
                                       ▼
                              pageHistorySections
                                       │
                              ┌────────┴────────┐
                              ▼                 ▼
                       PageHistoryCountsView  PageHistoryCollectionViewCell
                         （統計ヘッダー）        （リビジョンセル）
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| PageHistoryViewController.swift | `Wikipedia/Code/PageHistoryViewController.swift` | ソース | 編集履歴メインコントローラー |
| PageHistoryCollectionViewCell.swift | `Wikipedia/Code/PageHistoryCollectionViewCell.swift` | ソース | リビジョン表示セル |
| PageHistoryCountsView.swift | `Wikipedia/Code/PageHistoryCountsView.swift` | ソース | 統計ヘッダービュー |
| PageHistoryComparisonSelectionViewController.swift | `Wikipedia/Code/PageHistoryComparisonSelectionViewController.swift` | ソース | 比較選択バー |
| PageHistoryFetcher.swift | `WMF Framework/` | ソース | リビジョン・統計データ取得 |
| DiffContainerViewController.swift | `Wikipedia/Code/DiffContainerViewController.swift` | ソース | 差分比較画面 |
| PageHistorySection.swift | `WMF Framework/` | ソース | リビジョンセクションモデル |
| WMFPageHistoryRevision.swift | `WMF Framework/` | ソース | リビジョンモデル |
| EditHistoryCompareFunnel.swift | `Wikipedia/Code/EditHistoryCompareFunnel.swift` | ソース | 比較操作のログ記録 |
