# 画面設計書 3-Saved画面

## 概要

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

### 本画面の処理概要

Saved画面は、ユーザーが保存した記事とリーディングリストを管理する画面である。「すべての記事」と「リーディングリスト」の2つのビューモードを持つ。

**業務上の目的・背景**：ユーザーがオフラインで読むために保存した記事や、テーマ別に整理したリーディングリストを効率的に管理できるようにする。オフライン読書、記事のブックマーク、コレクション管理の中核機能を提供。

**画面へのアクセス方法**：タブバーの3番目の項目（Saved）をタップすることでアクセス。

**主要な操作・処理内容**：
1. 保存済み記事の一覧表示（すべての記事ビュー）
2. リーディングリストの一覧表示と管理（リーディングリストビュー）
3. 記事の検索・フィルタリング
4. 記事の並び替え（タイトル順、日付順など）
5. 記事の削除（スワイプまたは編集モード）
6. リーディングリストの作成・編集・削除
7. バッチ編集（複数選択による一括操作）
8. オフラインキャッシュの同期

**画面遷移**：
- 遷移元：タブバー
- 遷移先：Article画面、ReadingListDetail画面、AddArticlesToReadingList画面、Search画面、プロファイル画面、タブ一覧画面

**権限による表示制御**：ログイン状態によりリーディングリスト同期機能の利用可否が変化。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 13 | オフライン保存 | 主機能 | 記事のオフライン保存・キャッシュ管理 |
| 14 | リーディングリスト | 主機能 | リーディングリストの作成・管理 |
| 15 | 記事整理 | 補助機能 | 保存記事の並び替え・フィルタリング |
| 16 | リーディングリスト同期 | 補助機能 | クラウドとのリーディングリスト同期（ログイン時） |
| 11 | 記事検索 | 補助機能 | 検索バーからの記事検索 |
| 44 | プロファイル画面 | 遷移先機能 | プロファイルボタンタップでプロファイル画面へ遷移 |
| 61 | タブ一覧 | 遷移先機能 | タブボタンタップでタブ一覧画面へ遷移 |

## 画面種別

一覧（タブ切替）

## URL/ルーティング

- エントリーポイント：タブバー経由
- Storyboard ID：`SavedViewController`
- Objective-C名：`WMFSavedViewController`

## 入出力項目

| 項目名 | 入力/出力 | データ型 | 説明 |
|--------|----------|----------|------|
| 表示モード | 入力 | SavedViewMode | .all / .readingLists |
| 検索テキスト | 入力 | String | 検索バーに入力されたテキスト |
| ソート順序 | 入力 | SortOrder | タイトル順/日付順 |
| 保存記事リスト | 出力 | [WMFArticle] | すべての保存記事 |
| リーディングリスト | 出力 | [ReadingList] | リーディングリスト一覧 |

## 表示項目

| 項目名 | データソース | 表示条件 | 備考 |
|--------|-------------|----------|------|
| セグメントコントロール | - | 常時 | すべて/リーディングリスト切替 |
| 記事リスト | WMFArticle | すべてビュー時 | SavedArticlesCollectionViewController |
| リーディングリスト | ReadingList | リーディングリストビュー時 | ReadingListsCollectionViewController |
| 編集ボタン | - | 記事/リスト存在時 | バッチ編集モード開始 |
| ソートボタン | - | 記事/リスト存在時 | 並び替えオプション表示 |
| 空状態ビュー | - | 記事/リストなし時 | 保存方法の説明を表示 |

## イベント仕様

### 1-ビューモード切替

セグメントコントロールで「すべての記事」と「リーディングリスト」を切り替え。`savedViewMode`プロパティが更新され、対応するコレクションビューコントローラが表示される。

### 2-記事タップ

保存済み記事セルをタップすると、ArticleViewControllerが表示される。閲覧履歴も更新される。

### 3-リーディングリストタップ

リーディングリストセルをタップすると、ReadingListDetailViewControllerが表示され、そのリスト内の記事一覧が表示される。

### 4-スワイプ削除

セルを左スワイプすると削除ボタンが表示される。タップで記事の保存解除またはリーディングリストの削除を実行。

### 5-編集モード

編集ボタンタップで編集モードに移行。複数選択が可能になり、バッチ操作（削除、リーディングリストへ追加など）が実行可能。

### 6-新規リスト作成

リーディングリストビューで「+」ボタンをタップすると、CreateReadingListViewControllerが表示され、新規リストを作成できる。

### 7-ソート変更

ソートボタンタップでUIAlertControllerが表示され、並び替え順序を選択。`sortOrder`が更新され、表示がリフレッシュされる。

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 記事保存解除 | WMFArticle | UPDATE | savedDate = nil |
| リスト作成 | ReadingList | INSERT | 新規リーディングリスト作成 |
| リスト削除 | ReadingList, ReadingListEntry | DELETE | リストと関連エントリの削除 |
| リストへ追加 | ReadingListEntry | INSERT | 記事をリストに追加 |

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

#### WMFArticle

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | savedDate IS NOT NULL | 保存済み記事の取得 | フィルタ条件 |
| UPDATE | savedDate | nil | 保存解除時 |
| UPDATE | isDownloaded | true/false | オフラインキャッシュ状態 |

#### ReadingList

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | name, description | ユーザー入力値 | リスト作成 |
| UPDATE | name, description | ユーザー入力値 | リスト編集 |
| DELETE | isDefault = false | デフォルトリスト以外削除可 | 削除制約 |

## メッセージ仕様

| メッセージID | メッセージ内容 | 表示条件 | 種別 |
|-------------|---------------|----------|------|
| saved-empty-title | No saved articles yet | 保存記事がない場合 | 情報 |
| reading-lists-empty-title | No reading lists yet | リーディングリストがない場合 | 情報 |
| sync-in-progress | Syncing reading lists... | 同期中 | 情報 |
| delete-reading-list-confirm | Delete reading list? | リスト削除確認 | 確認 |

## 例外処理

- **同期エラー**：リーディングリスト同期失敗時、エラーバナーを表示しリトライオプションを提供
- **ストレージ不足**：オフライン保存に必要な容量が不足している場合、警告を表示
- **削除確認**：デフォルトリーディングリスト（Saved）は削除不可

## 備考

- SavedViewControllerはUIViewControllerを継承しコンテナとして機能
- 子VCとしてSavedArticlesCollectionViewController、ReadingListsCollectionViewControllerを持つ
- Core DataのNSFetchedResultsControllerでリアルタイム更新を監視
- オフラインキャッシュはArticleCacheControllerで管理

---

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

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

### 推奨読解順序

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

保存記事とリーディングリストのデータモデルを理解。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | WMFArticle+CoreDataClass.swift | `WMF/Sources/WMF/DataModel/WMFArticle+CoreDataClass.swift` | savedDateによる保存状態管理 |
| 1-2 | ReadingList+CoreDataClass.swift | `WMF/Sources/WMF/DataModel/ReadingList+CoreDataClass.swift` | リーディングリストモデル |
| 1-3 | ReadingListEntry+CoreDataClass.swift | `WMF/Sources/WMF/DataModel/ReadingListEntry+CoreDataClass.swift` | リスト-記事の関連 |

**読解のコツ**: savedDateがnilでないことが「保存済み」の条件。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | SavedViewController.swift | `Wikipedia/Code/SavedViewController.swift` | コンテナVC、行90-107でviewDidLoad |

**主要処理フロー**:
1. **viewDidLoad**: セグメントコントロール設定、子VC追加
2. **updateCurrentView**: モード切替に応じたビュー表示切替
3. **currentViewForMode**: 現在のモードに対応するVC取得

#### Step 3: 記事一覧表示層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | SavedArticlesCollectionViewController.swift | `Wikipedia/Code/SavedArticlesCollectionViewController.swift` | 保存記事一覧の表示 |
| 3-2 | ReadingListsCollectionViewController.swift | `Wikipedia/Code/ReadingListsCollectionViewController.swift` | リーディングリスト一覧表示 |

#### Step 4: リーディングリスト管理層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | ReadingListsController.swift | `WMF/Sources/WMF/ReadingLists/ReadingListsController.swift` | リーディングリストのCRUD操作 |
| 4-2 | ReadingListsSyncCoordinator.swift | `Wikipedia/Code/ReadingListsSyncCoordinator.swift` | クラウド同期ロジック |

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

```
SavedViewController
    │
    ├─ SavedArticlesCollectionViewController
    │      ├─ NSFetchedResultsController (WMFArticle)
    │      └─ ArticleCacheController
    │
    ├─ ReadingListsCollectionViewController
    │      ├─ NSFetchedResultsController (ReadingList)
    │      └─ ReadingListsController
    │
    ├─ ReadingListDetailViewController
    │      └─ NSFetchedResultsController (ReadingListEntry)
    │
    └─ CreateReadingListViewController
           └─ ReadingListsController.create()
```

### データフロー図

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

User Save Action ───▶ MWKDataStore ───▶ Core Data (WMFArticle)
                           │
                           ▼
                    ReadingListsController
                           │
                           ▼
Sync Event ──────────▶ ReadingListsSyncCoordinator ───▶ API
                           │
                           ▼
NSFetchedResultsController ───▶ CollectionView Update
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| SavedViewController.swift | `Wikipedia/Code/SavedViewController.swift` | ソース | コンテナViewController |
| SavedArticlesCollectionViewController.swift | `Wikipedia/Code/SavedArticlesCollectionViewController.swift` | ソース | 保存記事一覧VC |
| ReadingListsCollectionViewController.swift | `Wikipedia/Code/ReadingListsCollectionViewController.swift` | ソース | リーディングリスト一覧VC |
| ReadingListDetailViewController.swift | `Wikipedia/Code/ReadingListDetailViewController.swift` | ソース | リスト詳細VC |
| CreateReadingListViewController.swift | `Wikipedia/Code/CreateReadingListViewController.swift` | ソース | リスト作成VC |
| ReadingListsController.swift | `WMF/Sources/WMF/ReadingLists/ReadingListsController.swift` | ソース | リスト管理ロジック |
| ArticleCacheController.swift | `WMF/Sources/WMF/ArticleCache/ArticleCacheController.swift` | ソース | オフラインキャッシュ |
| ReadingList+CoreDataClass.swift | `WMF/Sources/WMF/DataModel/` | ソース | リストデータモデル |
