# 画面設計書 73-OnThisDay画面

## 概要

本ドキュメントは、Wikipedia iOSアプリの「今日の出来事」詳細画面（OnThisDayViewController）の設計を記述したものである。

### 本画面の処理概要

OnThisDay画面は、Explore画面のフィードに表示される「今日の出来事（On This Day）」カードから遷移し、歴史的な出来事の詳細一覧をタイムライン形式で表示する画面である。今日の日付に過去何年も前に起こった歴史的イベントを年代順に表示し、各イベントに関連するWikipedia記事へのリンクを提供する。

**業務上の目的・背景**：「今日の出来事」機能は、Wikipediaの豊富な歴史コンテンツを活用してユーザーに知的好奇心を喚起するための重要な機能である。毎日異なる歴史的イベントを提供することで、ユーザーの継続的なアプリ利用を促進し、Wikipediaの多様なコンテンツへの導線を作り出している。タイムライン形式のビジュアルデザインにより、時間の流れを直感的に理解できるようにしている。

**画面へのアクセス方法**：Explore画面に表示される「今日の出来事」カード（On This Day）をタップすることで本画面に遷移する。カード選択時に特定のイベントを選択している場合は、そのイベントの位置まで自動スクロールされる。

**主要な操作・処理内容**：
1. 歴史的イベントをタイムライン形式で一覧表示（年代順）
2. 各イベントにはタイムラインドットとアニメーションが表示される
3. 各イベントには関連記事が横スクロール可能なコレクションビューで表示される
4. 記事サムネイルをタップするとArticle画面へ遷移
5. コンテキストメニュー（長押し）で記事のプレビュー表示が可能
6. 初期表示時に特定イベントへの自動スクロール機能

**画面遷移**：
- 遷移元：Explore画面（今日の出来事カードタップ）
- 遷移先：Article画面（関連記事選択時）

**権限による表示制御**：特になし。全ユーザーが同一の操作・表示を利用可能。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 8 | 今日の出来事 | 主機能 | 今日の出来事詳細表示 |

## 画面種別

一覧（タイムライン形式）

## URL/ルーティング

ネイティブ画面のため、URLベースのルーティングは存在しない。ExploreViewControllerからナビゲーションコントローラ経由でプッシュ遷移される。

## 入出力項目

| 項目名 | 型 | 入力/出力 | 説明 |
|--------|-----|----------|------|
| events | [WMFFeedOnThisDayEvent] | 入力 | 今日の出来事イベント配列 |
| dataStore | MWKDataStore | 入力 | データストアインスタンス |
| midnightUTCDate | Date | 入力 | UTC真夜中の日付 |
| contentGroup | WMFContentGroup | 入力 | コンテンツグループ |
| theme | Theme | 入力 | 適用するテーマ |
| initialEvent | WMFFeedOnThisDayEvent? | 入力 | 初期表示時のスクロール先イベント（オプション） |

## 表示項目

| 項目名 | データソース | 説明 |
|--------|-------------|------|
| ヘッダー（日付・イベント数） | events配列とmidnightUTCDate | イベント総数と日付範囲の表示 |
| 年表示 | WMFFeedOnThisDayEvent.yearString | イベントが起きた年 |
| イベント説明 | WMFFeedOnThisDayEvent.text | イベントの説明テキスト |
| タイムラインドット | TimelineView | アニメーション付きのタイムラインビジュアル |
| 関連記事サムネイル | WMFArticle.imageURL | 関連記事のサムネイル画像 |
| 関連記事タイトル | WMFArticle.displayTitle | 関連記事のタイトル |

## イベント仕様

### 1-関連記事タップ

横スクロールの関連記事コレクションビュー内の記事をタップすると、ArticleCoordinatorを経由して該当記事のArticle画面へ遷移する。

### 2-コンテキストメニュー表示

記事を長押しすると、ArticlePeekPreviewViewControllerによるプレビューが表示される。プレビューからさらにタップするとArticle画面へ遷移する。

### 3-初期スクロール

viewWillAppearで、initialEventが設定されている場合は該当イベントの位置まで自動スクロールする。scrollToInitialEvent()がこの処理を担当。

### 4-フッターボタン押下

collectionViewFooterButtonWasPressedでnavigationController?.popViewController()が呼ばれ、前の画面に戻る。

### 5-タイムラインアニメーション

セルが画面に表示されるとドットアニメーションが開始され、画面外に出るとアニメーションが一時停止される。

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| - | - | - | 本画面ではデータベース更新は発生しない（読み取りのみ） |

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

本画面は読み取り専用であり、データベース更新は発生しない。

## メッセージ仕様

| メッセージID | 種別 | 表示条件 | メッセージ内容 |
|-------------|------|---------|---------------|
| on-this-day | ヘッダー | 常時（非表示設定） | CommonStrings.onThisDayTitle（ナビゲーションバータイトル、実際は非表示） |
| ヘッダー情報 | セクション | セクション1 | イベント数と日付範囲 |

## 例外処理

| 例外ケース | 処理内容 |
|-----------|---------|
| イベントなし | セクション0のみ表示（ヘッダー用） |
| 関連記事なし | 空のコレクションビュー表示 |
| initialEventが配列にない | スクロールは行わない |

## 備考

- ColumnarCollectionViewControllerを継承
- hidesBottomBarWhenPushedがtrueに設定されており、タブバーは非表示になる
- イベントログはcategory: .feed、label: .onThisDayで記録される
- タイムラインドットにはアニメーションが含まれ、pauseDotsAnimationプロパティで制御される
- セルの表示/非表示に連動してアニメーションが開始/停止される

---

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

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

### 推奨読解順序

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

まず、画面で扱うデータモデルを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | WMFFeedOnThisDayEvent | `WMF Framework/` | 今日の出来事イベントのデータモデル |
| 1-2 | WMFContentGroup | `WMF Framework/` | フィードコンテンツグループの構造 |

**読解のコツ**: WMFFeedOnThisDayEventはyearStringプロパティを持ち、イベントが発生した年を文字列で提供する。

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

処理の起点となるファイル・関数を特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | OnThisDayViewController.swift | `Wikipedia/Code/OnThisDayViewController.swift` | 本画面のメインViewController |

**主要処理フロー**:
1. **行16-25**: initでevents、dataStore、midnightUTCDate、contentGroup、themeを受け取り初期化
2. **行35-40**: viewDidLoadでセルとヘッダーを登録
3. **行42-46**: viewWillAppearで初期スクロールとナビゲーションバーを設定
4. **行54-60**: scrollToInitialEvent()でinitialEventの位置へスクロール

#### Step 3: セル表示を理解する

タイムラインセルの表示ロジックを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | OnThisDayCollectionViewCell.swift | `Wikipedia/Code/OnThisDayCollectionViewCell.swift` | タイムライン表示用カスタムセル |
| 3-2 | TimelineView | `Wikipedia/Code/` | タイムラインビジュアルコンポーネント |
| 3-3 | SideScrollingCollectionViewCell | `Wikipedia/Code/` | 横スクロール機能を持つ親セル |

**主要処理フロー**:
- **OnThisDayCollectionViewCell 行5**: TimelineViewを保持
- **行39-43**: pauseDotsAnimationプロパティでアニメーション制御
- **行18-31**: sizeThatFitsでタイムライン幅を考慮したレイアウト計算

#### Step 4: 画面遷移を理解する

記事選択時の遷移処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | OnThisDayViewController.swift | `Wikipedia/Code/OnThisDayViewController.swift` | SideScrollingCollectionViewCellDelegate実装 |
| 4-2 | ArticleCoordinator | `Wikipedia/Code/` | 記事画面遷移のコーディネーター |

**主要処理フロー**:
- **行216-222**: sideScrollingCollectionViewCell(_:didSelectArticleWithURL:at:)でArticleCoordinatorを起動

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

```
OnThisDayViewController
    │
    ├─ init(events:dataStore:midnightUTCDate:contentGroup:theme:) [行16-25]
    │      └─ super.init()
    │
    ├─ viewDidLoad() [行35-40]
    │      ├─ layoutManager.register(OnThisDayCollectionViewCell)
    │      ├─ layoutManager.register(OnThisDayViewControllerHeader)
    │      └─ layoutManager.register(OnThisDayViewControllerBlankHeader)
    │
    ├─ viewWillAppear(_:) [行42-46]
    │      ├─ scrollToInitialEvent() [行54-60]
    │      └─ configureNavigationBar() [行48-52]
    │
    ├─ numberOfSections(in:) [行128-130]
    │      └─ events.count + 1
    │
    ├─ collectionView(_:cellForItemAt:) [行143-157]
    │      ├─ dequeueReusableCell()
    │      └─ onThisDayCell.configure(with: event)
    │
    └─ sideScrollingCollectionViewCell(_:didSelectArticleWithURL:at:) [行217-222]
           └─ ArticleCoordinator.start()
```

### データフロー図

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

events ───────────────▶ numberOfSections() ─────────▶ セクション数
    │                           │
    │                           ▼
    │                    event(for: section)
    │                           │
    └─────────────────────────▶ ▼
                         OnThisDayCollectionViewCell
                         .configure(with: event)
                                │
                                ├─▶ TimelineView
                                │   (タイムライン表示)
                                │
                                ├─▶ titleLabel (年表示)
                                │
                                └─▶ SideScrollingCollectionView
                                    (関連記事横スクロール)
                                         │
                                         ▼
                              ArticleCoordinator.start()
                                   (記事画面遷移)

initialEvent ──────────▶ scrollToInitialEvent() ──▶ 自動スクロール
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| OnThisDayViewController.swift | `Wikipedia/Code/OnThisDayViewController.swift` | ソース | 本画面のメインViewController |
| OnThisDayCollectionViewCell.swift | `Wikipedia/Code/OnThisDayCollectionViewCell.swift` | ソース | タイムライン表示用カスタムセル |
| OnThisDayViewControllerHeader.swift | `Wikipedia/Code/OnThisDayViewControllerHeader.swift` | ソース | ヘッダービュー（日付・イベント数表示） |
| OnThisDayViewControllerBlankHeader.swift | `Wikipedia/Code/OnThisDayViewController.swift` | ソース | 空のヘッダービュー（行121-123で定義） |
| TimelineView.swift | `Wikipedia/Code/TimelineView.swift` | ソース | タイムラインビジュアルコンポーネント |
| SideScrollingCollectionViewCell.swift | `Wikipedia/Code/SideScrollingCollectionViewCell.swift` | ソース | 横スクロールセルの親クラス |
| ColumnarCollectionViewController.swift | `Wikipedia/Code/ColumnarCollectionViewController.swift` | ソース | カラムレイアウトの基盤 |
| ArticleCoordinator.swift | `Wikipedia/Code/ArticleCoordinator.swift` | ソース | 記事画面遷移のコーディネーター |
| ArticlePeekPreviewViewController.swift | `Wikipedia/Code/ArticlePeekPreviewViewController.swift` | ソース | 記事プレビュー表示 |
| WMFFeedOnThisDayEvent | `WMF Framework/` | ソース | 今日の出来事イベントデータモデル |
