# 画面設計書 6-ArticlePeekPreview画面

## 概要

本ドキュメントは、Wikipedia iOSアプリのArticlePeekPreview画面の設計仕様を記載したものである。

### 本画面の処理概要

ArticlePeekPreview画面は、記事の長押し（コンテキストメニュー）時に表示されるプレビュー画面である。記事のサムネイル、タイトル、概要テキストを表示し、コンテキストメニューからの操作を可能にする。

**業務上の目的・背景**：ユーザーがリンク先記事の内容を画面遷移なしで素早く確認できるようにする。iOS標準のPeek & Pop（コンテキストメニュープレビュー）に対応し、効率的な記事探索体験を提供。

**画面へのアクセス方法**：記事リンク、検索結果、保存記事リストなどで記事セルを長押し。

**主要な操作・処理内容**：
1. 記事プレビューの表示（タイトル、サムネイル、概要）
2. コンテキストメニューからの操作
   - 記事を開く
   - 新しいタブで開く
   - バックグラウンドタブで開く
   - 保存/保存解除
   - 地図で表示（位置情報付き記事）
   - 共有
3. プレビューからの記事画面への遷移（Pop）

**画面遷移**：
- 遷移元：Explore画面、Search画面、Saved画面、Places画面、Article画面（リンク）
- 遷移先：Article画面（Pop操作時）、AddArticlesToReadingList画面（共有から）、Places画面（地図表示から）

**権限による表示制御**：コンテキストメニュー項目は記事の状態（保存済み、位置情報有無）により動的に変化。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | 記事閲覧 | 主機能 | プレビュー表示、記事画面への遷移 |
| 13 | オフライン保存 | 補助機能 | コンテキストメニューからの保存操作 |
| 18 | 近くの記事 | 補助機能 | 地図で表示アクション |
| 61 | タブ一覧 | 補助機能 | 新しいタブで開く、バックグラウンドタブで開く |

## 画面種別

プレビュー（コンテキストメニュー）

## URL/ルーティング

- エントリーポイント：長押しジェスチャーからUIContextMenuConfigurationで表示
- Storyboard ID：`ArticlePeekPreviewViewController`
- Objective-C名：`WMFArticlePeekPreviewViewController`

## 入出力項目

| 項目名 | 入力/出力 | データ型 | 説明 |
|--------|----------|----------|------|
| 記事URL | 入力 | URL | articleURL プロパティ |
| 記事データ | 入力/出力 | WMFArticle | article プロパティ |
| データストア | 入力 | MWKDataStore | dataStore プロパティ |
| テーマ | 入力 | Theme | theme プロパティ |
| デリゲート | 入力 | ArticlePreviewingDelegate | articlePreviewingDelegate |

## 表示項目

| 項目名 | データソース | 表示条件 | 備考 |
|--------|-------------|----------|------|
| 記事サムネイル | WMFArticle.imageURL | 画像存在時 | expandedArticleView内 |
| 記事タイトル | WMFArticle.displayTitle | 常時 | expandedArticleView内 |
| 記事概要 | WMFArticle.wikidataDescription | 常時 | 最大5行表示 |
| ローディングインジケータ | - | データ取得中 | activityIndicatorView |

## イベント仕様

### 1-プレビュー表示

`viewWillAppear`で`fetchArticle`メソッドが呼び出され、`articleSummaryController.updateOrCreateArticleSummaryForArticle`で記事データを取得。取得完了後`updateView(with:)`でUIを更新。

**ソースコード参照**: ArticlePeekPreviewViewController.swift 行39-61（fetchArticle関数）

### 2-コンテキストメニュー構築

`contextMenuItems`プロパティで動的にメニュー項目を生成。

**提供されるアクション**:
- **Open**: 記事を通常表示（readAction）
- **Open in new tab**: 新しいタブで記事を開く（openInNewTabAction）
- **Open in background tab**: バックグラウンドで新しいタブを作成（openInNewBackgroundTabAction）
- **Save/Remove from saved**: 保存状態の切り替え（saveAction）
- **View on a map**: 位置情報付き記事の地図表示（placeAction）
- **Share**: 共有シート表示（shareAction）

**ソースコード参照**: ArticlePeekPreviewViewController.swift 行114-231（contextMenuItems）

### 3-保存アクション

`saveAction`内で`dataStore.savedPageList.toggleSavedPage(for:)`を呼び出し、保存状態を切り替え。VoiceOverアナウンスメントで状態変更を通知。

**ソースコード参照**: ArticlePeekPreviewViewController.swift 行194-202

### 4-タブ操作

`WMFArticleTabsDataController`を使用して新しいタブを作成。タブ数上限（tabsMax）に達している場合は現在のタブに追加し、警告トーストを表示。

**ソースコード参照**: ArticlePeekPreviewViewController.swift 行144-182

### 5-共有アクション

`sharingActivityViewController`でUIActivityViewControllerを生成。`AddToReadingListActivity`カスタムアクティビティを追加し、リーディングリストへの追加も可能。

**ソースコード参照**: ArticlePeekPreviewViewController.swift 行214-227, 233-257

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 保存トグル | WMFArticle | UPDATE | savedDate の更新 |
| タブ作成 | ArticleTab, ArticleTabItem | INSERT | 新しいタブの作成 |
| 記事取得 | WMFArticle | SELECT/UPDATE | サマリー情報の取得・更新 |

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

#### WMFArticle

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | savedDate | 現在時刻/nil | 保存/保存解除トグル |
| UPDATE | summary | API取得値 | サマリー情報更新 |

## メッセージ仕様

| メッセージID | メッセージ内容 | 表示条件 | 種別 |
|-------------|---------------|----------|------|
| accessibility-saved-notification | Article saved | 保存成功時（VoiceOver） | 情報 |
| accessibility-unsaved-notification | Article removed from saved | 保存解除時（VoiceOver） | 情報 |
| article-tabs-limit-toast | You've reached the limit of %d tabs | タブ上限到達時 | 警告 |

## 例外処理

- **記事取得失敗**：ローディングインジケータを停止し、空状態のまま表示
- **タブ作成失敗**：DDLogErrorでエラーログを出力

## 備考

- ArticlePeekPreviewViewControllerはUIViewControllerを直接継承
- ArticleFullWidthImageCollectionViewCellを再利用してプレビュー表示
- needsEmptyContextMenuItemsフラグで空のコンテキストメニューを要求可能
- needsRandomOnPushフラグでランダム記事機能に対応
- ArticlePreviewingDelegateで呼び出し元にアクション完了を通知

---

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

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

### 推奨読解順序

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

プレビューで使用されるデータモデルを理解。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | WMFArticle+CoreDataClass.swift | `WMF/Sources/WMF/DataModel/WMFArticle+CoreDataClass.swift` | displayTitle, wikidataDescription, imageURL |
| 1-2 | ArticlePreviewingDelegate.swift | `Wikipedia/Code/ArticlePreviewingDelegate.swift` | デリゲートプロトコルの定義 |

**読解のコツ**: ArticlePreviewingDelegateの各メソッドがどのアクションに対応するか確認。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ArticlePeekPreviewViewController.swift | `Wikipedia/Code/ArticlePeekPreviewViewController.swift` | 全体260行程度の小規模VC |

**主要処理フロー**:
1. **行23-32 init**: 記事URL、データストア、テーマを初期化
2. **行82-91 viewDidLoad**: activityIndicatorView、expandedArticleViewを設定
3. **行93-98 viewWillAppear**: fetchArticle()で記事データ取得
4. **行70-80 updateView**: 取得完了後にUIを更新

#### Step 3: コンテキストメニュー生成を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ArticlePeekPreviewViewController.swift 行114-231 | `Wikipedia/Code/` | contextMenuItemsプロパティ |
| 3-2 | WMFArticleTabsDataController.swift | `WMFData/Sources/` | タブ操作ロジック |

#### Step 4: 呼び出し元を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | 各CollectionViewControllerのcontextMenuConfiguration | `Wikipedia/Code/` | コンテキストメニュー表示トリガー |

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

```
Long Press Gesture → UIContextMenuConfiguration
    │
    ▼
ArticlePeekPreviewViewController
    │
    ├─ fetchArticle()
    │      └─ ArticleSummaryController.updateOrCreateArticleSummaryForArticle()
    │
    ├─ expandedArticleView (ArticleFullWidthImageCollectionViewCell)
    │      └─ configure(article:displayType:index:theme:layoutOnly:)
    │
    └─ contextMenuItems → [UIAction]
           ├─ readAction → articlePreviewingDelegate.readMoreArticlePreviewActionSelected()
           ├─ openInNewTabAction → articlePreviewingDelegate.openInNewTabArticlePreviewActionSelected()
           ├─ openInNewBackgroundTabAction → WMFArticleTabsDataController.createArticleTab()
           ├─ saveAction → dataStore.savedPageList.toggleSavedPage()
           ├─ placeAction → articlePreviewingDelegate.viewOnMapArticlePreviewActionSelected()
           └─ shareAction → ShareActivityController
```

### データフロー図

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

Article URL ───────▶ ArticleSummaryController ───▶ API Request
                           │
                           ▼
WMFArticle ────────▶ updateView() ───▶ expandedArticleView
                           │
                           ▼
Long Press ────────▶ contextMenuItems ───▶ UIMenu
                           │
                           ▼
Action Selection ──▶ ArticlePreviewingDelegate ───▶ Parent VC
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ArticlePeekPreviewViewController.swift | `Wikipedia/Code/` | ソース | メインViewController（260行） |
| ArticlePreviewingDelegate.swift | `Wikipedia/Code/` | ソース | デリゲートプロトコル |
| ArticleFullWidthImageCollectionViewCell.swift | `Wikipedia/Code/` | ソース | プレビュー表示セル |
| WMFArticleTabsDataController.swift | `WMFData/Sources/` | ソース | タブ管理 |
| AddToReadingListActivity.swift | `Wikipedia/Code/` | ソース | カスタム共有アクティビティ |
| ShareActivityController.swift | `Wikipedia/Code/` | ソース | 共有シート |
| ArticleSummaryController.swift | `WMF/Sources/WMF/` | ソース | サマリー取得 |
