# 機能設計書 30-トークページ表示

## 概要

本ドキュメントは、Wikipedia iOSアプリケーションにおけるトークページ表示機能の設計を記載する。この機能により、ユーザーは記事やユーザーのトークページを閲覧し、トピック（話題）への返信や新規トピックの投稿ができる。

### 本機能の処理概要

トークページ表示機能は、MediaWiki Discussion APIを使用してトークページのトピックとコメントを取得・表示し、ユーザーが議論に参加できるインターフェースを提供する。

**業務上の目的・背景**：トークページはWikipediaのコミュニティにおける議論の場であり、記事の内容や編集方針について話し合う。ユーザーが議論に参加しやすいネイティブUIを提供することで、コミュニティへの貢献を促進する。

**機能の利用シーン**：記事ページから「トーク」タブをタップ、または通知からトークページに遷移した際に使用する。トピック一覧の閲覧、スレッド展開、返信、新規トピック投稿が可能。

**主要な処理内容**：
1. トークページデータの取得と表示
2. トピック一覧のCollectionView表示
3. スレッド展開/折りたたみ
4. コメントへの返信投稿
5. 新規トピック投稿
6. トピック購読管理
7. ページ内検索（Find in Page）
8. 言語切り替え
9. 編集履歴・アーカイブへの遷移

**関連システム・外部連携**：MediaWiki Discussion API、MediaWiki Edit API

**権限による制御**：閲覧は全ユーザー可能、投稿は編集権限必要（一時アカウント対応）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 記事表示画面 | 遷移元 | トークページ表示を開始 |
| - | トークページ画面 | メイン画面 | トピック一覧表示 |
| - | 返信作成ビュー | サブ画面 | コメント入力 |
| - | トピック作成画面 | サブ画面 | 新規トピック投稿 |
| - | トークページアーカイブ画面 | サブ画面 | アーカイブ閲覧 |
| - | 編集履歴画面 | 参照 | ページ履歴表示 |

## 機能種別

データ表示、API連携、テキスト入力

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| theme | Theme | Yes | テーマ設定 | - |
| viewModel | TalkPageViewModel | Yes | トークページビューモデル | - |
| pageType | TalkPageType | Yes | .article / .user | - |
| pageTitle | String | Yes | ページタイトル（例: Talk:Cat） | - |
| siteURL | URL | Yes | サイトURL | - |

### 入力データソース

- MediaWiki Discussion API（トピック・コメント取得）
- ArticleSummaryController（記事サマリー情報）
- ユーザー入力（返信テキスト、トピックタイトル/本文）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| topics | [TalkPageCellViewModel] | トピック一覧 |
| postReply | Void | 返信投稿結果 |
| postTopic | Void | 新規トピック投稿結果 |

### 出力先

- CollectionView（トピック表示）
- MediaWiki Edit API（投稿）
- 購読通知

## 処理フロー

### 処理シーケンス

```
1. 初期化
   ├─ TalkPageViewModel生成
   ├─ TalkPageView設定
   └─ オーバーフローメニュー設定

2. データ取得
   ├─ dataController.fetchTalkPage
   ├─ トピックデータ解析
   ├─ ヘッダー情報設定
   └─ CollectionView更新

3. ユーザー操作
   ├─ スレッド展開/折りたたみ
   ├─ 返信タップ → ReplyComposeController
   ├─ トピック追加 → TopicComposeViewController
   └─ 購読ボタン → subscribe API

4. 投稿処理
   ├─ postReply / postTopic
   ├─ ページ再取得
   └─ 成功通知/エラーハンドリング
```

### フローチャート

```mermaid
flowchart TD
    A[トークページ表示開始] --> B[fetchTalkPage]
    B --> C{取得成功?}
    C -->|Yes| D[トピック一覧表示]
    C -->|No| E[エラービュー表示]
    D --> F{ユーザー操作}
    F -->|スレッド展開| G[isThreadExpanded toggle]
    F -->|返信| H[ReplyComposeController表示]
    F -->|新規トピック| I[TopicComposeViewController表示]
    F -->|購読| J[subscribe API]
    H --> K{投稿?}
    K -->|Yes| L[postReply]
    I --> M{投稿?}
    M -->|Yes| N[postTopic]
    L --> O[fetchTalkPage再取得]
    N --> O
    O --> D
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | スレッド初期状態 | スレッドは初期状態で折りたたみ | 常時 |
| BR-002 | 購読通知 | 購読トピックへの新規コメント時に通知 | 購読時 |
| BR-003 | 一時アカウント対応 | 非ログイン時は一時アカウント警告表示 | 投稿時 |
| BR-004 | オンボーディング | 初回投稿時にオンボーディング表示 | wmf_userHasOnboardedToContributingToTalkPages=false時 |
| BR-005 | RTL対応 | 言語に応じてセマンティックコンテンツ属性設定 | 常時 |
| BR-006 | ディープリンク | 通知からの遷移時に特定トピック/コメントにスクロール | ディープリンク時 |

### 計算ロジック

| 計算項目 | 計算式・ロジック |
|---------|----------------|
| semanticContentAttribute | `MWKLanguageLinkController.semanticContentAttribute(forContentLanguageCode:)` |
| headerTitle | `pageTitle.namespaceAndTitleOfWikiResourcePath(with:).title` |

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

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

本機能はローカルDBを直接操作しない。API経由でMediaWikiに反映。

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 処理内容 |
|-----------|---------|---------|
| fetchFailure | トークページ取得失敗 | エラービュー表示、「再試行」ボタン |
| networkError | ネットワーク接続なし | オフラインアラート表示 |
| postReplyFailure | 返信投稿失敗 | エラーアラート、投稿画面維持 |
| postTopicFailure | トピック投稿失敗 | エラーアラート、投稿画面維持 |
| subscriptionFailure | 購読API失敗 | 購読状態ロールバック、エラーアラート |

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

該当なし

## パフォーマンス要件

- トピック取得後のUI更新は非同期で実行
- CollectionViewの差分更新でスムーズなスクロール

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

- 一時アカウント警告の表示
- IPアドレス公開警告の表示

## 備考

- TalkPageTypeは.article（記事トーク）と.user（ユーザートーク）の2種類
- Coffee Rollはトークページ上部の導入テキスト
- ページ内検索（Find in Page）はスレッド全展開後に使用

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | TalkPageViewModel.swift | `Wikipedia/Code/TalkPageViewModel.swift` | プロパティ定義（L6-45） |
| 1-2 | TalkPageViewModel.swift | `Wikipedia/Code/TalkPageViewModel.swift` | DeepLinkData構造体（L8-11） |
| 1-3 | TalkPageCellViewModel.swift | `Wikipedia/Code/TalkPageCellViewModel.swift` | セルビューモデル定義 |

**読解のコツ**: TalkPageViewModelが保持するtopics配列がCollectionViewのデータソースとなる。

#### Step 2: View Controller初期化を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | TalkPageViewController.swift | `Wikipedia/Code/TalkPageViewController.swift` | init（L161-166） |
| 2-2 | TalkPageViewController.swift | `Wikipedia/Code/TalkPageViewController.swift` | viewDidLoad（L205-225） |

**主要処理フロー**:
- **L208**: オーバーフローメニュー設定
- **L210-211**: CollectionView delegate/dataSource設定
- **L215**: fetchTalkPage呼び出し
- **L216**: ツールバー設定

#### Step 3: データ取得処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | TalkPageViewController.swift | `Wikipedia/Code/TalkPageViewController.swift` | fetchTalkPage（L177-203） |
| 3-2 | TalkPageViewModel.swift | `Wikipedia/Code/TalkPageViewModel.swift` | fetchTalkPage（L107-130） |

**主要処理フロー**:
- **L179**: `viewModel.fetchTalkPage` 呼び出し
- **L187**: `talkPageView.configure(viewModel:)` でビュー設定
- **L191**: `collectionView.reloadData()` でデータ反映

#### Step 4: CollectionViewを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | TalkPageViewController.swift | `Wikipedia/Code/TalkPageViewController.swift` | UICollectionViewDataSource（L963-1007） |
| 4-2 | TalkPageViewController.swift | `Wikipedia/Code/TalkPageViewController.swift` | TalkPageCellDelegate（L1011-1059） |

**主要処理フロー**:
- **L965-967**: numberOfItemsInSectionでtopics.count返却
- **L969-982**: cellForItemAtでTalkPageCell設定
- **L1013-1032**: userDidTapDisclosureButtonでスレッド展開

#### Step 5: 返信投稿を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | TalkPageViewController.swift | `Wikipedia/Code/TalkPageViewController.swift` | TalkPageCellReplyDelegate（L1073-1104） |
| 5-2 | TalkPageViewController.swift | `Wikipedia/Code/TalkPageViewController.swift` | TalkPageReplyComposeDelegate（L1106-1215） |

**主要処理フロー**:
- **L1074-1103**: tappedReplyでReplyComposeController表示
- **L1124-1206**: tappedPublishで投稿処理

#### Step 6: 新規トピック投稿を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 6-1 | TalkPageViewController.swift | `Wikipedia/Code/TalkPageViewController.swift` | showAddTopic（L430-453） |
| 6-2 | TalkPageViewController.swift | `Wikipedia/Code/TalkPageViewController.swift` | TalkPageTopicComposeViewControllerDelegate（L1219-1296） |

**主要処理フロー**:
- **L442**: TopicComposeViewController生成
- **L1220-1295**: tappedPublishで投稿処理

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

```
ArticleViewController / NotificationCenter
    │
    └─ TalkPageViewController(theme:, viewModel:)
           │
           ├─ viewDidLoad()
           │      ├─ setupOverflowMenu()
           │      ├─ fetchTalkPage()
           │      │      └─ viewModel.fetchTalkPage()
           │      │             └─ dataController.fetchTalkPage()
           │      │
           │      └─ setupToolbar()
           │
           ├─ [UICollectionView]
           │      ├─ cellForItemAt()
           │      │      └─ TalkPageCell.configure()
           │      │
           │      └─ didSelectItemAt()
           │             └─ userDidTapDisclosureButton()
           │
           ├─ [Reply]
           │      ├─ tappedReply()
           │      │      └─ replyComposeController.setupAndDisplay()
           │      │
           │      └─ tappedPublish()
           │             └─ viewModel.postReply()
           │                    └─ dataController.postReply()
           │
           ├─ [New Topic]
           │      ├─ userDidTapAddTopicButton()
           │      │      └─ showAddTopic()
           │      │             └─ TalkPageTopicComposeViewController
           │      │
           │      └─ tappedPublish(topicTitle:, topicBody:)
           │             └─ viewModel.postTopic()
           │                    └─ dataController.postTopic()
           │
           └─ [Subscribe]
                  └─ userDidTapSubscribeButton()
                         └─ viewModel.subscribe()
                                └─ dataController.subscribeToTopic()
```

### データフロー図

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

pageTitle/siteURL      ───▶ TalkPageViewModel
                                │
                                ▼
                         dataController.fetchTalkPage()
                                │
                                ▼
                         [TalkPageItem] ───▶ populateCellData() ───▶ topics
                                                                       │
                                                                       ▼
                                                               CollectionView表示

返信テキスト           ───▶ replyComposeController
                                │
                                ▼
                         viewModel.postReply()
                                │
                                ▼
                         dataController.postReply() ───▶ MediaWiki API
                                │
                                ▼
                         fetchTalkPage() ───▶ UI更新

トピックタイトル/本文  ───▶ TopicComposeViewController
                                │
                                ▼
                         viewModel.postTopic()
                                │
                                ▼
                         dataController.postTopic() ───▶ MediaWiki API
                                │
                                ▼
                         fetchTalkPage() ───▶ UI更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| TalkPageViewController.swift | `Wikipedia/Code/TalkPageViewController.swift` | ソース | トークページメインVC |
| TalkPageViewModel.swift | `Wikipedia/Code/TalkPageViewModel.swift` | ソース | トークページビューモデル |
| TalkPageView.swift | `Wikipedia/Code/TalkPageView.swift` | ソース | トークページカスタムビュー |
| TalkPageCell.swift | `Wikipedia/Code/TalkPageCell.swift` | ソース | トピックセル |
| TalkPageCellViewModel.swift | `Wikipedia/Code/TalkPageCellViewModel.swift` | ソース | トピックセルビューモデル |
| TalkPageHeaderView.swift | `Wikipedia/Code/TalkPageHeaderView.swift` | ソース | ヘッダービュー |
| TalkPageReplyComposeController.swift | `Wikipedia/Code/TalkPageReplyComposeController.swift` | ソース | 返信作成コントローラー |
| TalkPageTopicComposeViewController.swift | `Wikipedia/Code/TalkPageTopicComposeViewController.swift` | ソース | トピック作成VC |
| TalkPageDataController.swift | `Wikipedia/Code/TalkPageDataController.swift` | ソース | データ取得/投稿コントローラー |
| TalkPageArchivesViewController.swift | `Wikipedia/Code/TalkPageArchivesViewController.swift` | ソース | アーカイブ表示VC |
| TalkPageFindInPageState.swift | `Wikipedia/Code/TalkPageFindInPageState.swift` | ソース | ページ内検索状態管理 |
