# 画面設計書 165-カスタム絵文字

## 概要

本ドキュメントはGitLabのグループカスタム絵文字管理画面の設計仕様を記載したものである。

### 本画面の処理概要

グループ固有のカスタム絵文字を管理する画面である。グループ管理者がカスタム絵文字を作成・一覧表示し、グループ配下のすべてのプロジェクトで利用可能にすることができる。カスタム絵文字はイシュー、マージリクエスト、コメントなどで使用可能。

**業務上の目的・背景**：チームのコミュニケーションを活性化し、独自の文化を表現するために、標準の絵文字に加えてカスタム絵文字を使用したいというニーズがある。本画面により、グループ単位でカスタム絵文字を管理し、チーム独自のリアクションや表現を可能にする。

**画面へのアクセス方法**：グループメニュー > Settings > Custom emoji からアクセスする。または直接URL `/{group_path}/-/custom_emoji` にアクセスする。

**主要な操作・処理内容**：
1. カスタム絵文字の一覧表示
2. 新規カスタム絵文字の作成（名前、画像URL指定）
3. カスタム絵文字の削除
4. 絵文字のプレビュー表示

**画面遷移**：
- 遷移元：グループ設定画面
- 遷移先：カスタム絵文字新規作成画面（同一ページ内モーダル）

**権限による表示制御**：
- グループメンバー：カスタム絵文字一覧の閲覧が可能
- グループ管理者：カスタム絵文字の作成・削除が可能

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 144 | アバター管理 | 主機能 | カスタム絵文字の管理 |

## 画面種別

一覧

## URL/ルーティング

- パス: `/{group_path}/-/custom_emoji`
- コントローラー: `Groups::CustomEmojiController#index`
- HTTPメソッド: GET

## 入出力項目

| 項目名 | 項目ID | 入出力 | データ型 | 必須 | 説明 |
|--------|--------|--------|----------|------|------|
| グループパス | group_path | 入力 | String | 必須 | 対象グループのパス |
| 絵文字名 | name | 入力 | String | 必須 | 絵文字の識別名（:name:形式で使用） |
| 画像URL | file | 入力 | URL | 必須 | 外部画像のURL |

## 表示項目

| 項目名 | データソース | 説明 |
|--------|-------------|------|
| 絵文字プレビュー | custom_emoji.file | 絵文字画像のプレビュー |
| 絵文字名 | custom_emoji.name | 絵文字の名前（:name:形式） |
| 作成日 | custom_emoji.created_at | 作成日時 |
| 作成者 | custom_emoji.creator | 作成したユーザー |

## イベント仕様

### 1-新規作成ボタン押下

カスタム絵文字作成フォームを表示する。

- トリガー: 「Add new custom emoji」ボタンクリック
- 処理: Vue Routerで新規作成画面へ遷移
- 遷移先: `/{group_path}/-/custom_emoji/new`（SPA内遷移）

### 2-絵文字作成フォーム送信

新規カスタム絵文字を作成する。

- トリガー: フォームの「Save」ボタンクリック
- 処理: GraphQL mutation `createCustomEmoji`を実行
- 成功時: 一覧に追加、成功メッセージ表示
- 失敗時: エラーメッセージ表示

### 3-削除ボタン押下

カスタム絵文字を削除する。

- トリガー: 絵文字行の削除ボタンクリック
- 処理: 確認ダイアログ表示後、GraphQL mutation `destroyCustomEmoji`を実行
- 成功時: 一覧から削除、成功メッセージ表示

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 一覧表示 | custom_emoji | SELECT | カスタム絵文字一覧の取得 |
| 絵文字作成 | custom_emoji | INSERT | 新規カスタム絵文字の作成 |
| 絵文字削除 | custom_emoji | DELETE | カスタム絵文字の削除 |

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

#### custom_emoji

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | 全項目 | namespace_id = グループID | グループのカスタム絵文字 |
| INSERT | name | ユーザー入力値 | 絵文字名（英小文字、数字、アンダースコア、ハイフン） |
| INSERT | file | ユーザー入力値 | 外部画像URL |
| INSERT | namespace_id | グループID | 所属グループ |
| INSERT | creator_id | current_user.id | 作成者 |
| INSERT | external | true | 外部画像フラグ（現在は常にtrue） |
| DELETE | id | 削除対象ID | 絵文字削除 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|---------|
| MSG001 | 情報 | Custom emoji will be available to use in every project in the group. | 画面説明 |
| MSG002 | 成功 | Custom emoji was created | 作成成功時 |
| MSG003 | 成功 | Custom emoji was deleted | 削除成功時 |
| MSG004 | エラー | Name is already being used for another emoji | 名前重複時 |
| MSG005 | エラー | Name has already been taken | グループ内で名前重複時 |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| 権限不足 | 作成・削除ボタンを非表示 |
| グループ未存在 | 404ページを表示 |
| 名前重複 | エラーメッセージを表示 |
| 無効な画像URL | エラーメッセージを表示 |
| 標準絵文字名との重複 | エラーメッセージを表示 |

## 備考

- 本画面はVue.jsコンポーネントとVue Routerによるspa構成
- データ取得・更新はGraphQL APIを使用
- 現在は外部画像URL（external=true）のみサポート
- 絵文字名は `/[a-z0-9_-]+/` パターンに従う必要がある
- 名前の最大長は36文字
- 標準のTanukiEmoji（GitLab絵文字）との名前重複は不可

---

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

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

### 推奨読解順序

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

カスタム絵文字のデータモデルとバリデーションを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | custom_emoji.rb | `app/models/custom_emoji.rb` | モデル定義、バリデーション、スコープ |

**読解のコツ**:
- `NAME_REGEXP`（行4）で名前パターンを確認
- `validates :name`（行20-25）でバリデーションルールを確認
- `valid_emoji_name`（行62-66）で標準絵文字との重複チェック確認

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

HAMLテンプレートとコントローラーの処理を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.html.haml | `app/views/groups/custom_emoji/index.html.haml` | Vueマウントポイント、data属性設定 |
| 2-2 | custom_emoji_controller.rb | `app/controllers/groups/custom_emoji_controller.rb` | コントローラー定義 |

**主要処理フロー**:
1. **行3**: `#js-custom-emojis-root`要素にbase_path、group_pathを設定
2. コントローラーは最小限で、Vueアプリに処理を委譲

#### Step 3: フロントエンド初期化を理解する

Vue.jsアプリケーションの初期化とルーティングを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.js | `app/assets/javascripts/pages/groups/custom_emoji/index.js` | エントリーポイント |
| 3-2 | custom_emoji_bundle.js | `app/assets/javascripts/custom_emoji/custom_emoji_bundle.js` | Vueアプリ初期化 |
| 3-3 | routes.js | `app/assets/javascripts/custom_emoji/routes.js` | Vue Routerルート定義 |

**主要処理フロー**:
- `initCustomEmojis`関数でVue Apollo、Vue Router設定
- `requestIdleCallback`で非同期初期化

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

```
Groups::CustomEmojiController#index
    │
    └─ View: index.html.haml
           └─ JavaScript: initCustomEmojis()
                  │
                  ├─ VueApollo (GraphQLクライアント)
                  ├─ VueRouter (ルーティング)
                  │
                  └─ Vue: App
                         │
                         ├─ GraphQL: getCustomEmojis
                         │
                         └─ GraphQL mutations
                                ├─ createCustomEmoji
                                └─ destroyCustomEmoji
```

### データフロー図

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

URLリクエスト ───▶ CustomEmojiController ───▶ HAMLテンプレート
                         │
                         ▼
                  Vue初期化 ───▶ GraphQL API ───▶ 絵文字一覧表示

フォーム入力 ───▶ createCustomEmoji mutation
                         │
                         ▼
                  custom_emoji INSERT ───▶ 一覧更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.html.haml | `app/views/groups/custom_emoji/index.html.haml` | テンプレート | Vueマウントポイント定義 |
| custom_emoji_controller.rb | `app/controllers/groups/custom_emoji_controller.rb` | コントローラー | リクエスト処理 |
| custom_emoji.rb | `app/models/custom_emoji.rb` | モデル | カスタム絵文字データ定義 |
| custom_emoji_bundle.js | `app/assets/javascripts/custom_emoji/custom_emoji_bundle.js` | JavaScript | Vue初期化 |
| routes.js | `app/assets/javascripts/custom_emoji/routes.js` | JavaScript | Vue Routerルート定義 |
| graphql_client.js | `app/assets/javascripts/custom_emoji/graphql_client.js` | JavaScript | GraphQLクライアント |
