# 画面設計書 71-モデレーション設定画面

## 概要

本ドキュメントは、ActivityPub連携機能におけるモデレーション設定画面の設計書です。

### 本画面の処理概要

この画面では、ActivityPub（ソーシャルWeb）連携においてブロックしたユーザーやドメインを管理できます。ブロック解除やブロック追加の操作を通じて、自サイトとの連携を制御します。

**業務上の目的・背景**：ActivityPub連携により、Ghost外部のフェディバースユーザーが自サイトのコンテンツをフォロー・インタラクションできるようになりますが、スパムや不適切なアカウント・ドメインからの保護が必要です。本画面は、サイト管理者がそのようなアカウントやドメインを一元管理し、ブロック状態を制御するために提供されています。

**画面へのアクセス方法**：管理画面のActivityPubセクションから、設定画面（Preferences）を開き、「Moderation」リンクをクリックしてアクセスします。URLパスは `/activitypub/preferences/moderation` です。

**主要な操作・処理内容**：
1. ブロック済みユーザー一覧の表示・閲覧
2. ブロック済みドメイン一覧の表示・閲覧
3. ブロック済みユーザーの解除（Unblock）
4. ブロック済みドメインの解除（Unblock）
5. 一度解除したユーザー・ドメインの再ブロック

**画面遷移**：
- 遷移元：ActivityPub設定画面（/activitypub/preferences）
- 遷移先：なし（同画面内で操作完結）
- 戻り先：ActivityPub設定画面

**権限による表示制御**：ActivityPub機能へのアクセス権限を持つ管理者ユーザーのみがこの画面にアクセス可能です。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 57 | ActivityPub | 主機能 | ActivityPubモデレーション設定 |

## 画面種別

一覧・編集（ブロック済みユーザー/ドメインの一覧表示と解除操作）

## URL/ルーティング

- パス: `/activitypub/preferences/moderation`
- ルート定義: `apps/activitypub/src/routes.tsx` (L99-103)

## 入出力項目

| 項目名 | 入出力 | データ型 | 必須 | 説明 |
|--------|--------|----------|------|------|
| blockedAccounts | 出力 | Account[] | - | ブロック済みアカウント一覧 |
| blockedDomains | 出力 | Domain[] | - | ブロック済みドメイン一覧 |

## 表示項目

### ブロック済みユーザータブ

| 項目名 | データ型 | 説明 |
|--------|----------|------|
| avatarUrl | string | ユーザーのアバター画像URL |
| name | string | ユーザーの表示名 |
| handle | string | ユーザーのハンドル（@user@domain形式） |
| Blocked/Unblockボタン | - | ブロック状態切替ボタン |

### ブロック済みドメインタブ

| 項目名 | データ型 | 説明 |
|--------|----------|------|
| hostname | string | ドメイン名（URLから抽出） |
| Blocked/Unblockボタン | - | ブロック状態切替ボタン |

## イベント仕様

### 1-タブ切替

- トリガー: タブ（Blocked users / Blocked domains）クリック
- 処理: 対応するタブコンテンツを表示
- 状態変更: UIのみ

### 2-ユーザーブロック解除

- トリガー: ブロック済みユーザーの「Blocked」ボタンをホバー→「Unblock」表示→クリック
- 処理:
  1. 楽観的UIで即座にブロック解除状態を反映
  2. `unblockMutation.mutate(account)` を実行
  3. 成功時トースト「User unblocked」を表示
- API: ActivityPub API経由でブロック解除リクエスト

### 3-ユーザー再ブロック

- トリガー: 解除済みユーザーの「Block」ボタンクリック
- 処理:
  1. 楽観的UIで即座にブロック状態を反映
  2. `blockMutation.mutate(account)` を実行
  3. 成功時トースト「User blocked」を表示
- API: ActivityPub API経由でブロックリクエスト

### 4-ドメインブロック解除

- トリガー: ブロック済みドメインの「Blocked」ボタンをホバー→「Unblock」表示→クリック
- 処理:
  1. 楽観的UIで即座にブロック解除状態を反映
  2. `unblockDomainMutation.mutate({url: domain.url})` を実行
  3. 成功時トースト「Domain unblocked」を表示

### 5-ドメイン再ブロック

- トリガー: 解除済みドメインの「Block」ボタンクリック
- 処理:
  1. 楽観的UIで即座にブロック状態を反映
  2. `blockDomainMutation.mutate({url: domain.url})` を実行
  3. 成功時トースト「Domain blocked」を表示

### 6-プロフィールクリック

- トリガー: ユーザー行をクリック
- 処理: `handleProfileClick` でユーザープロフィール画面へ遷移

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| ユーザーブロック解除 | (ActivityPub API) | DELETE | ブロックリストからユーザー削除 |
| ユーザー再ブロック | (ActivityPub API) | INSERT | ブロックリストにユーザー追加 |
| ドメインブロック解除 | (ActivityPub API) | DELETE | ブロックリストからドメイン削除 |
| ドメイン再ブロック | (ActivityPub API) | INSERT | ブロックリストにドメイン追加 |

※ 実際のデータはActivityPub APIサーバー側で管理

## メッセージ仕様

| 種別 | メッセージ | 表示タイミング |
|------|-----------|---------------|
| 成功 | User unblocked | ユーザーブロック解除成功時 |
| 成功 | User blocked | ユーザー再ブロック成功時 |
| 成功 | Domain unblocked | ドメインブロック解除成功時 |
| 成功 | Domain blocked | ドメイン再ブロック成功時 |
| 情報 | When you block someone, they won't be able to follow you or interact with your content on the social web. | ブロック済みユーザーが0件の場合 |
| 情報 | When you block a domain, all users from that domain won't be able to follow you or interact with your content. | ブロック済みドメインが0件の場合 |

## 例外処理

| 例外状況 | 対応内容 |
|---------|---------|
| データ読み込み中 | スケルトンローダー表示（5件分のプレースホルダー） |
| ブロック済みアカウントが0件 | 空状態メッセージとアイコンを表示 |
| ブロック済みドメインが0件 | 空状態メッセージとアイコンを表示 |

## 備考

- 楽観的UI更新を採用しており、ユーザー操作に対して即座にUIが反映される
- ホバー時にボタンテキストが「Blocked」から「Unblock」に変化するインタラクティブなUXを実装
- ProfilePreviewHoverCardコンポーネントにより、ユーザー情報のプレビューがホバーで表示可能

---

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

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

### 推奨読解順序

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

まず、プログラム間で受け渡されるデータ構造を理解することが重要です。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | activitypub.ts | `apps/activitypub/src/api/activitypub.ts` | Account型、ブロックAPIのレスポンス型を確認 |

**読解のコツ**: TypeScriptの型定義を追跡することで、APIから返されるデータ構造が把握できます。

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

処理の起点となるファイル・関数を特定します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | moderation.tsx | `apps/activitypub/src/views/preferences/components/moderation.tsx` | メインコンポーネントの構造 |
| 2-2 | routes.tsx | `apps/activitypub/src/routes.tsx` | ルーティング定義（L99-103） |

**主要処理フロー**:
1. **L14-15**: `useBlockedAccountsForUser`/`useBlockedDomainsForUser` でデータ取得
2. **L24-30**: ブロック/アンブロック用のmutation hookを初期化
3. **L35-81**: ブロック解除・再ブロックのハンドラー関数
4. **L83-208**: JSXでUI構築（タブ、リスト、ボタン）

#### Step 3: カスタムフックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | use-activity-pub-queries.ts | `apps/activitypub/src/hooks/use-activity-pub-queries.ts` | ブロック関連のAPI呼び出しロジック |

**主要処理フロー**:
- `useBlockedAccountsForUser`: ブロック済みアカウント一覧を取得するinfinite query
- `useBlockMutationForUser`: アカウントをブロックするmutation
- `useUnblockMutationForUser`: アカウントのブロックを解除するmutation
- `useBlockedDomainsForUser`: ブロック済みドメイン一覧を取得
- `useBlockDomainMutationForUser`: ドメインをブロック
- `useUnblockDomainMutationForUser`: ドメインのブロックを解除

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

```
moderation.tsx (Moderation Component)
    |
    +-- useBlockedAccountsForUser('index')
    |       +-- ActivityPubAPI.getBlockedAccounts()
    |
    +-- useBlockedDomainsForUser('index')
    |       +-- ActivityPubAPI.getBlockedDomains()
    |
    +-- handleUnblock(account)
    |       +-- unblockMutation.mutate(account)
    |               +-- ActivityPubAPI.unblock(account)
    |
    +-- handleBlock(account)
    |       +-- blockMutation.mutate(account)
    |               +-- ActivityPubAPI.block(account)
    |
    +-- handleDomainUnblock(domain)
    |       +-- unblockDomainMutation.mutate({url})
    |               +-- ActivityPubAPI.unblockDomain(url)
    |
    +-- handleDomainBlock(domain)
            +-- blockDomainMutation.mutate({url})
                    +-- ActivityPubAPI.blockDomain(url)
```

### データフロー図

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

ユーザー操作
  |
  +-- タブ切替 ---------> Tabs state管理 -----------> TabsContent表示
  |
  +-- Unblockクリック --> handleUnblock() ---------> 楽観的UI更新
  |                       unblockMutation ---------> APIリクエスト
  |                                                   toast表示
  |
  +-- Blockクリック ----> handleBlock() -----------> 楽観的UI更新
                          blockMutation -----------> APIリクエスト
                                                      toast表示
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| moderation.tsx | `apps/activitypub/src/views/preferences/components/moderation.tsx` | ソース | メインコンポーネント |
| routes.tsx | `apps/activitypub/src/routes.tsx` | ソース | ルーティング定義 |
| use-activity-pub-queries.ts | `apps/activitypub/src/hooks/use-activity-pub-queries.ts` | ソース | API呼び出しフック |
| activitypub.ts | `apps/activitypub/src/api/activitypub.ts` | ソース | APIクライアント |
| ap-avatar.tsx | `apps/activitypub/src/components/global/ap-avatar.tsx` | ソース | アバターコンポーネント |
| activity-item.tsx | `apps/activitypub/src/components/activities/activity-item.tsx` | ソース | リストアイテムコンポーネント |
| profile-preview-hover-card.tsx | `apps/activitypub/src/components/global/profile-preview-hover-card.tsx` | ソース | プロフィールプレビュー |
