# 機能設計書 2-記事プレビュー

## 概要

本ドキュメントは、Wikipedia iOSアプリにおける記事プレビュー機能の設計仕様を記述する。記事リンクの長押し時に表示されるPeek/Pop形式のプレビュー機能を提供する。

### 本機能の処理概要

**業務上の目的・背景**：ユーザーが記事リンクをタップして完全に遷移する前に、その記事の概要を確認できるようにする。これにより、ユーザーは意図した記事かどうかを素早く判断でき、不要な画面遷移を減らすことができる。iOSのコンテキストメニュー（3D Touch / Haptic Touch）を活用したネイティブ体験を提供する。

**機能の利用シーン**：
- 記事内のリンクを長押しした時
- 検索結果のリンクを長押しした時
- フィード内の記事を長押しした時
- 関連記事を長押しした時
- 履歴リストの記事を長押しした時

**主要な処理内容**：
1. 記事URLからArticle Summary APIで記事概要を取得
2. ArticleFullWidthImageCollectionViewCellを使用してプレビューUI表示
3. コンテキストメニューアクション（開く、新しいタブで開く、保存、共有、地図で表示）の提供
4. プレビューからの記事全体表示への遷移

**関連システム・外部連携**：
- Article Summary API（記事概要の取得）
- Core Data（WMFArticleエンティティ）
- UIContextMenuConfiguration（iOSのコンテキストメニュー）

**権限による制御**：
- 特別な権限は不要
- 保存機能はログイン状態に関係なく使用可能

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 6 | ArticlePeekPreview画面 | 主画面 | 記事プレビューの表示、コンテキストメニュー提供 |
| 5 | Article画面 | 遷移先画面 | プレビューから記事全体への遷移 |
| 1 | Explore画面 | 呼び出し元 | フィードからのプレビュー表示 |
| 3 | Search画面 | 呼び出し元 | 検索結果からのプレビュー表示 |

## 機能種別

表示処理 / データ取得 / ナビゲーション / インタラクション

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| articleURL | URL | Yes | プレビューする記事のURL | Wikipedia URLであること |
| article | WMFArticle? | No | 既にフェッチ済みの記事オブジェクト | nil許容 |
| dataStore | MWKDataStore | Yes | データストアインスタンス | nil不可 |
| theme | Theme | Yes | 適用するテーマ | 有効なThemeオブジェクト |
| articlePreviewingDelegate | ArticlePreviewingDelegate? | No | デリゲート | nil許容 |
| needsEmptyContextMenuItems | Bool | No | 空のコンテキストメニューが必要か | デフォルトfalse |
| needsRandomOnPush | Bool | No | プッシュ時にランダム記事へ遷移するか | デフォルトfalse |

### 入力データソース

- Article Summary API（プレビュー用記事概要）
- Core Data（キャッシュされた記事情報）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| プレビューUI | UIView | 記事のプレビュー表示 |
| コンテキストメニュー | [UIAction] | 利用可能なアクション |
| preferredContentSize | CGSize | プレビューの最適サイズ |

### 出力先

- UIContextMenuConfiguration（iOSコンテキストメニュー）
- ArticlePreviewingDelegate（アクション結果のコールバック）

## 処理フロー

### 処理シーケンス

```
1. ArticlePeekPreviewViewController初期化
   └─ articleURL、dataStore、themeを受け取り初期化
   └─ articlePreviewingDelegateを設定

2. viewDidLoad
   └─ 背景色の設定
   └─ activityIndicatorViewの表示開始
   └─ expandedArticleViewを非表示でサブビューに追加

3. viewWillAppear
   └─ fetchArticleの呼び出し
   └─ 取得完了後にupdatePreferredContentSizeでサイズ計算

4. fetchArticle（記事取得）
   └─ articleURLからキーを生成
   └─ articleSummaryController.updateOrCreateArticleSummaryForArticle呼び出し
   └─ 成功時: updateViewでUI更新
   └─ 失敗時: activityIndicator停止

5. updateView（UI更新）
   └─ expandedArticleView.configureで記事情報を設定
   └─ extractLabelのnumberOfLinesを5に制限
   └─ activityIndicatorを停止
   └─ expandedArticleViewを表示

6. コンテキストメニュー表示
   └─ contextMenuItemsプロパティで利用可能なアクションを生成
```

### フローチャート

```mermaid
flowchart TD
    A[長押し検出] --> B[ArticlePeekPreviewViewController生成]
    B --> C[viewDidLoad]
    C --> D[ActivityIndicator表示]
    D --> E[viewWillAppear]
    E --> F[fetchArticle]
    F --> G{記事データ取得成功?}
    G -->|Yes| H[updateView]
    G -->|No| I[ActivityIndicator停止]
    H --> J[expandedArticleView表示]
    J --> K[preferredContentSize更新]
    K --> L[プレビュー表示完了]
    L --> M{ユーザーアクション}
    M -->|タップ| N[記事画面へ遷移]
    M -->|メニュー選択| O[選択アクション実行]
    M -->|スワイプキャンセル| P[プレビュー閉じる]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | プレビュー行数制限 | 記事概要は5行に制限して表示 | 常時 |
| BR-002 | 保存トグル | 保存ボタンは保存/保存解除をトグル | 記事がメインネームスペースの場合 |
| BR-003 | 地図表示条件 | 地図表示オプションは位置情報がある記事のみ | article.location != nil |
| BR-004 | タブ上限警告 | タブ上限時は警告メッセージ表示 | tabsCount >= tabsMax |
| BR-005 | 空メニュー | needsEmptyContextMenuItems=trueで空メニュー | 特定の呼び出し元から |

### 計算ロジック

- preferredContentSize: `expandedArticleView.sizeThatFits(CGSize(width: contentWidth, height: UIView.noIntrinsicMetric))`

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 記事取得/作成 | WMFArticle | SELECT/INSERT | 記事サマリーの取得または作成 |
| 保存トグル | SavedPageList | UPDATE | 保存状態のトグル |

### テーブル別操作詳細

#### WMFArticle

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT/INSERT | key | articleURL.wmf_inMemoryKey | サマリー取得時 |

#### SavedPageList

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | - | toggleSavedPage(for: articleURL) | 保存/保存解除 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | API取得失敗 | Article Summary取得失敗 | ActivityIndicator停止のみ、エラー表示なし |
| - | キー生成失敗 | wmf_inMemoryKeyがnil | completion()を即時呼び出し |

### リトライ仕様

- 自動リトライなし（プレビューの性質上、ユーザーが再度長押しすることで再試行）

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

- 保存トグル操作はSavedPageListの標準トランザクションを使用

## パフォーマンス要件

- プレビュー表示までの目標時間：1秒以内
- サマリーAPIはキャッシュ優先で取得

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

- 特別なセキュリティ要件なし
- 認証不要で使用可能

## 備考

- iOS 13以降のUIContextMenuConfigurationを使用
- iPad対応でtraitCollectionDidChangeでフォント更新

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | WMFArticle | `WMF Framework/` | 記事メタデータ（タイトル、説明、画像URL等） |
| 1-2 | ArticlePreviewingDelegate | `Wikipedia/Code/` | プレビューアクションのデリゲートプロトコル |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ArticlePeekPreviewViewController.swift | `Wikipedia/Code/ArticlePeekPreviewViewController.swift` | プレビューのメインViewController |

**主要処理フロー**:
- **23-32行目**: `init` - 初期化、全パラメータを受け取り保持
- **39-61行目**: `fetchArticle` - Article Summary APIからデータ取得
- **70-80行目**: `updateView` - UI更新、expandedArticleViewの設定
- **82-91行目**: `viewDidLoad` - 初期セットアップ、ActivityIndicator設定
- **93-98行目**: `viewWillAppear` - 記事取得開始
- **114-231行目**: `contextMenuItems` - コンテキストメニューアクションの構築

#### Step 3: UIコンポーネントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ArticleFullWidthImageCollectionViewCell | `Wikipedia/Code/` | プレビューのメインUI |

**読解のコツ**: このセルは記事リスト表示にも使用される汎用コンポーネント。`displayType: .pageWithPreview`で設定。

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

`contextMenuItems`プロパティが返すUIActionの一覧：
1. **開く（readAction）**: 120-123行目 - 記事を開く
2. **新しいタブで開く（openInNewTabAction）**: 134-141行目 - 新規タブで開く
3. **バックグラウンドタブで開く（openInNewBackgroundTabAction）**: 144-181行目 - バックグラウンドタブ作成
4. **保存（saveAction）**: 193-202行目 - 保存/保存解除トグル
5. **地図で表示（placeAction）**: 207-211行目 - 位置情報ありの場合のみ
6. **共有（shareAction）**: 216-228行目 - 共有アクティビティ表示

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

```
呼び出し元（ExploreViewController、SearchViewController等）
    │
    └─ UIContextMenuConfiguration(previewProvider:)
           │
           └─ ArticlePeekPreviewViewController
                  │
                  ├─ MWKDataStore.articleSummaryController
                  │      └─ updateOrCreateArticleSummaryForArticle()
                  │
                  ├─ ArticleFullWidthImageCollectionViewCell
                  │      └─ configure(article:displayType:...)
                  │
                  ├─ ArticlePreviewingDelegate (callback)
                  │      ├─ readMoreArticlePreviewActionSelected()
                  │      ├─ openInNewTabArticlePreviewActionSelected()
                  │      ├─ saveArticlePreviewActionSelected()
                  │      ├─ viewOnMapArticlePreviewActionSelected()
                  │      └─ shareArticlePreviewActionSelected()
                  │
                  └─ WMFArticleTabsDataController
                         └─ createArticleTab() / appendArticle()
```

### データフロー図

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

articleURL ─────────────▶ ArticlePeekPreviewViewController
                               │
Article Summary API ────▶      │ (fetchArticle)
                               │
                               ▼
                         WMFArticle取得/作成
                               │
                               ▼
                         expandedArticleView.configure()
                               │
                               ▼
                         [プレビューUI表示]
                               │
                               ▼
                         contextMenuItems構築
                               │
                               ▼
                         [コンテキストメニュー表示]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ArticlePeekPreviewViewController.swift | `Wikipedia/Code/` | ソース | プレビューのメインViewController |
| ArticleFullWidthImageCollectionViewCell.swift | `Wikipedia/Code/` | ソース | プレビューUIコンポーネント |
| ArticlePreviewingDelegate.swift | `Wikipedia/Code/` | ソース | アクションコールバックプロトコル |
| ShareActivityController.swift | `Wikipedia/Code/` | ソース | 共有アクティビティ |
| AddToReadingListActivity.swift | `Wikipedia/Code/` | ソース | リーディングリスト追加アクティビティ |
| WMFArticleTabsDataController.swift | `WMF Framework/` | ソース | タブ管理 |
| MWKDataStore.swift | `WMF Framework/` | ソース | データストア |
| ArticleSummaryController.swift | `WMF Framework/` | ソース | 記事サマリー取得 |
