# 画面設計書 83-メール設定ページ

## 概要

本ドキュメントは、Ghost会員向けポータル（Portal）のメール設定ページ（AccountEmailPage）の画面設計書である。会員がニュースレター購読設定とコメント通知設定を管理するための画面を定義する。

### 本画面の処理概要

メール設定ページは、Ghost会員サイトのPortalウィジェット内で、会員が受信するメールの設定を一元管理するための画面である。

**業務上の目的・背景**：ニュースレター購読型サイトにおいて、会員が受信するメールを自身でコントロールできることは、スパム扱いされるリスクを軽減し、配信停止による収益への影響を抑える上で重要である。会員が特定のニュースレターのみ購読解除できる柔軟な設定機能により、完全な購読解除ではなく選択的な購読を可能にし、エンゲージメント維持と会員体験の向上を両立する。

**画面へのアクセス方法**：Portalウィジェットを開き、アカウントホームページから「メール設定」をクリック、購読解除リンクからのリダイレクト、またはURL直接アクセス（`#/portal/accountEmail`）によりアクセスする。サインイン済みの会員のみがアクセス可能であり、未サインイン時はサインインページにリダイレクトされる。

**主要な操作・処理内容**：
1. 購読中ニュースレター一覧の表示
2. 各ニュースレターの購読ON/OFF切り替え
3. コメント返信通知のON/OFF切り替え（コメント機能有効時）
4. 全メール購読解除機能
5. 購読解除リンクからのアクセス時の自動購読解除処理

**画面遷移**：
- 遷移元：アカウントホームページ（accountHome）、購読解除リンク（メール内）
- 遷移先：アカウントホームページ（戻るボタン押下時）、サインインページ（未認証時）、メール受信FAQ（email_suppressionがある場合）

**権限による表示制御**：
- サインイン済み会員のみアクセス可能
- 有料会員向けメッセージ（購読解除しても有料プランは解約されない旨）
- ニュースレター送信機能無効時は一部コンテンツ非表示

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 20 | ニュースレター管理 | 主機能 | メール購読設定の管理 |
| 71 | Portal | 補助機能 | Portalウィジェット内でのUI表示 |

## 画面種別

設定

## URL/ルーティング

- ハッシュルート：`#/portal/accountEmail`
- Pages.jsでの登録キー：`accountEmail`
- クエリパラメータ：
  - `newsletterUuid`: 購読解除対象ニュースレターのUUID
  - `action`: 'unsubscribe'（自動購読解除時）
  - `comments`: コメント通知購読解除フラグ

## 入出力項目

| 項目名 | 項目ID | 型 | 必須 | 入力/出力 | 説明 |
|--------|--------|-----|------|----------|------|
| ニュースレター購読状態 | subscribedNewsletters | array | - | 入力 | 購読中ニュースレターのIDリスト |
| コメント通知設定 | enableCommentNotifications | boolean | - | 入力 | コメント返信通知の有効/無効 |

## 表示項目

| 項目名 | データソース | 説明 |
|--------|-------------|------|
| ページタイトル | - | "Email preferences" 固定 |
| ニュースレター一覧 | site.newsletters | 利用可能なニュースレター |
| ニュースレター名 | newsletter.name | 各ニュースレターの名前 |
| ニュースレター説明 | newsletter.description | 各ニュースレターの説明文 |
| 購読解除通知 | pageData | 購読解除完了メッセージ |
| 有料会員向けメッセージ | - | 購読解除と有料プランの関係説明 |

## イベント仕様

### 1-ニュースレター購読切り替え

**トリガー**：ニュースレター行のSwitchコンポーネントトグル

**処理フロー**：
1. トグル状態に応じてsubscribedNewsletters配列を更新
2. `doAction('updateNewsletterPreference', {newsletters: updatedNewsletters})` 実行
3. 成功通知「Email preferences updated.」表示

**データ更新**：
- state.subscribedNewsletters: 更新されたニュースレターリスト

### 2-コメント通知切り替え

**トリガー**：CommentsセクションのSwitchコンポーネントトグル

**処理フロー**：
1. `doAction('updateNewsletterPreference', {enableCommentNotifications: checked})` 実行
2. 成功通知「Comment preferences updated.」表示

**データ更新**：
- member.enable_comment_notifications: true/false

### 3-全メール購読解除

**トリガー**：「Unsubscribe from all emails」ボタンクリック

**処理フロー**：
1. subscribedNewslettersを空配列に設定
2. コメント通知も無効化（commentsEnabled時）
3. `doAction('updateNewsletterPreference', {newsletters: [], enableCommentNotifications: false})` 実行
4. 成功通知「Unsubscribed from all emails.」表示

**データ更新**：
- state.subscribedNewsletters: []
- member.enable_comment_notifications: false

### 4-購読解除リンクからのアクセス

**トリガー**：メール内購読解除リンクによるページ表示（newsletterUuid && action === 'unsubscribe'）

**処理フロー**：
1. useEffectで自動購読解除処理実行
2. 対象ニュースレターをsubscribedNewslettersから除外
3. `doAction('updateNewsletterPreference', {newsletters: remainingNewsletters})` 実行
4. 購読解除完了メッセージを表示（HeaderNotification）

### 5-戻るボタン

**トリガー**：BackButtonクリック

**処理フロー**：
1. `doAction('back')` 実行
2. 前のページ（通常はaccountHome）に戻る

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| ニュースレター購読切り替え | members_newsletters | INSERT/DELETE | 会員とニュースレターの関連更新 |
| コメント通知切り替え | members | UPDATE | enable_comment_notificationsフラグ更新 |
| 全メール購読解除 | members_newsletters, members | DELETE, UPDATE | 全購読解除とフラグ更新 |

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

#### members_newsletters

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | member_id, newsletter_id | 会員ID、ニュースレターID | 購読追加時 |
| DELETE | - | member_id、newsletter_idで特定 | 購読解除時 |

#### members

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | enable_comment_notifications | true/false | コメント通知設定 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|--------------|---------|
| MSG-001 | ラベル | "Email preferences" | ページタイトル |
| MSG-002 | ラベル | "Comments" | コメント通知セクション見出し |
| MSG-003 | 説明 | "Get notified when someone replies to your comment" | コメント通知説明 |
| MSG-004 | ボタン | "Unsubscribe from all emails" | 全購読解除ボタン |
| MSG-005 | 成功 | "Email preferences updated." | ニュースレター更新成功時 |
| MSG-006 | 成功 | "Comment preferences updated." | コメント通知更新成功時 |
| MSG-007 | 成功 | "Unsubscribed from all emails." | 全購読解除成功時 |
| MSG-008 | 情報 | "{memberEmail} will no longer receive {newsletterName} newsletter." | 購読解除リンクからのアクセス時 |
| MSG-009 | 情報 | "Unsubscribing from emails will not cancel your paid subscription to {title}" | 有料会員向け注意書き |
| MSG-010 | リンク | "Not receiving emails?" / "Get help" | メール未受信時のヘルプリンク |

## 例外処理

| 例外条件 | 処理内容 | 表示メッセージ |
|---------|---------|--------------|
| 未認証状態 | サインインページへリダイレクト | - |
| ニュースレター送信無効 | 一部コンテンツ非表示 | - |
| API通信エラー | エラー状態、再試行を促す | - |
| email_suppression状態 | ヘルプリンク表示 | "Not receiving emails?" |

## 備考

- ニュースレター送信機能が無効（editor_default_email_recipients === 'disabled'）の場合、ニュースレター一覧は表示されない
- コメント機能が無効（comments_enabled === 'off'）の場合、コメント通知セクションは表示されない
- 有料会員の場合、購読解除しても有料プランは解約されない旨のメッセージを表示
- 購読解除ボタンは、全てのニュースレター・コメント通知がOFFの場合は無効化される

---

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

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

### 推奨読解順序

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

メール設定ページで扱う主要なデータ構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | app-context.js | `apps/portal/src/app-context.js` | AppContextの構造（site, member, pageData） |
| 1-2 | helpers.js | `apps/portal/src/utils/helpers.js` | getSiteNewsletters、hasNewsletterSendingEnabled関数 |

**読解のコツ**: site.newslettersにニュースレター一覧が、member.newslettersに購読中ニュースレターが格納される。

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

ページコンポーネントの構造と初期化処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | account-email-page.js | `apps/portal/src/components/pages/account-email-page.js` | AccountEmailPage関数コンポーネント |
| 2-2 | pages.js | `apps/portal/src/pages.js` | ページルーティング定義 |

**主要処理フロー**:
1. **8行目**: 関数コンポーネントとして定義
2. **21-27行**: useEffectで未認証チェック
3. **30-39行**: useEffectで購読解除リンクからのアクセス時の自動処理
4. **90-122行**: NewsletterManagementコンポーネントに各種props渡し

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

ニュースレター管理の共通UIを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | newsletter-management.js | `apps/portal/src/components/common/newsletter-management.js` | NewsletterManagement共通コンポーネント |

**主要処理フロー**:
- **22-49行**: NewsletterPrefSectionでニュースレター行のUI
- **52-79行**: CommentsSectionでコメント通知UI
- **81-97行**: NewsletterPrefsでニュースレター一覧
- **108-188行**: NewsletterManagementメインコンポーネント

#### Step 4: API連携を理解する

バックエンドAPIとの連携処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | api.js | `apps/portal/src/utils/api.js` | member.update関数 |

**主要処理フロー**:
- **209-234行**: member.update APIでnewsletters、enable_comment_notificationsを更新

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

```
AccountEmailPage (account-email-page.js)
    │
    ├─ useContext(AppContext)
    │      └─ member, doAction, site, pageData 取得
    │
    ├─ useEffect (未認証チェック)
    │      └─ doAction('switchPage', {page: 'signin'})
    │
    ├─ useEffect (自動購読解除)
    │      └─ newsletterUuid && action === 'unsubscribe' 時
    │             └─ doAction('updateNewsletterPreference', {...})
    │
    └─ NewsletterManagement
           ├─ NewsletterPrefs
           │      └─ NewsletterPrefSection (各ニュースレター)
           │             └─ Switch コンポーネント
           │
           ├─ CommentsSection
           │      └─ Switch コンポーネント
           │
           ├─ updateSubscribedNewsletters()
           │      └─ doAction('updateNewsletterPreference', {newsletters})
           │
           ├─ updateCommentNotifications()
           │      └─ doAction('updateNewsletterPreference', {enableCommentNotifications})
           │
           └─ unsubscribeAll()
                  └─ doAction('updateNewsletterPreference', {newsletters: [], ...})
```

### データフロー図

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

site.newsletters ──────▶ getSiteNewsletters() ─────────▶ ニュースレター一覧
member.newsletters ────▶ useState ─────────────────────▶ 購読状態表示
site.comments_enabled ─▶ 表示制御 ─────────────────────▶ コメント通知セクション

URLパラメータ
  │
  └─ newsletterUuid, action ──▶ useEffect ──────────────▶ 自動購読解除

ユーザー操作
  │
  ├─ Switchトグル ─────────────▶ setSubscribedNewsletters()
  │                                    │
  │                                    └─ doAction('updateNewsletterPreference')
  │                                           └─▶ api.member.update()
  │                                                  └─▶ members_newsletters更新
  │
  └─ 全購読解除ボタン ────────▶ unsubscribeAll()
                                     │
                                     └─ doAction('updateNewsletterPreference', {...})
                                            └─▶ 全購読解除処理
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| account-email-page.js | `apps/portal/src/components/pages/account-email-page.js` | ソース | メインページコンポーネント |
| newsletter-management.js | `apps/portal/src/components/common/newsletter-management.js` | ソース | ニュースレター管理共通コンポーネント |
| pages.js | `apps/portal/src/pages.js` | ソース | ページルーティング定義 |
| app-context.js | `apps/portal/src/app-context.js` | ソース | アプリケーションコンテキスト定義 |
| api.js | `apps/portal/src/utils/api.js` | ソース | API通信処理 |
| helpers.js | `apps/portal/src/utils/helpers.js` | ソース | ヘルパー関数群 |
| switch.js | `apps/portal/src/components/common/switch.js` | ソース | Switchトグルコンポーネント |
| action-button.js | `apps/portal/src/components/common/action-button.js` | ソース | アクションボタンコンポーネント |
| back-button.js | `apps/portal/src/components/common/back-button.js` | ソース | 戻るボタンコンポーネント |
| i18n.js | `apps/portal/src/utils/i18n.js` | ソース | 国際化ユーティリティ |
