GitHubで動く💩Emojiを使えるChrome拡張を作った

Custom Emojiは好きですか？SlackやDiscordにある[こういうEmoji](https://slackmojis.com/)のことです。

![custom_emoji.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44173/24236c39-5eb7-f270-50df-3a85f4ddcd42.gif)

僕はSlackでもDiscordでもMattermostでもCustom Emojiが大好きです。GitHubやGitlabでSlackと同じEmojiが使えたら・・・と考えたことありませんか？

それ**「Chrome拡張なら実現できるんじゃね？」**と思って開発を始めました。

## 想定したユースケース

GitHub/Gitlabでもでも画像をアップロードすることで、Emojiっぽいことができます。が、文章にインライン表示されないし何より面倒です。あくまでSlackのように`:EMOJI:`として入力したいと思いました。

`こんにちは :thumbsupparrot:`と入力したら、以下のように文章に表示されるようにしたいです。
![ezgif.com-gif-maker.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44173/0158d793-c6a1-df47-bc04-37daba6ecfa7.gif)

## できたもの

GitHubのissueページでうんちのアニメーションEmojiを使えるようになりました。GitHub以外にも基本的にどのページでも使えます。作ってQiita書いたら力尽きてしまったのでまだChrome Web Storeにはアプリ提出できていません。気になる方は[Installation](#Installation)を参照してみてください。

![demo3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44173/bb765a09-ede0-3299-f8b8-02015b98e229.gif)

### 機能①: Emojiアップロード

ローカルのファイルシステムからEmojiファイルを複数まとめてアップロードできます。
一度アップロードしたらブラウザのローカルストレージに保存されるので、以降ブラウザを再起動してもそのまま使えます。

![demo-upload.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44173/7c41141b-5fa2-e438-3fc9-b5a36dafb200.gif)


### 機能②: 動かすWebサイトを限定する

ページ全体をスキャンしてEmojiに変換する処理は割と重たいので指定されたURLパターンにマッチするページのみ動かせるようにしました。

![demo-url.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44173/ef9e42ad-ccf9-b209-1e55-f8fc48ce215d.gif)


### 機能③: タグの種類によって非表示orサイズ変更

![demo-size.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44173/9f14c8fa-1e85-6e9e-4362-782498010b06.gif)

## 技術的な詳細

動き的にはかなりシンプルです。単純に表示しているページ全体をスキャンして`:EMOJI:`の文字列を`img`要素に書き換えるだけです。が、単純に実装するとブラウザのレンダリングをブロックして使いづらいものになるので以下のチューニングを入れてユーザーエクスペリエンスを損なわないようにしています。

## チューニング

* 機能②により、限定したサイトのみで動かす
* Emojiのデータをブラウザのメモリにキャッシュ
* Chrome拡張の[document_idle](https://developer.chrome.com/docs/extensions/mv2/content_scripts/)という指定をして、ブラウザがページをロードし終わった後にコードを実行する
* [requestIdCallback](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback) APIを使ってブラウザがIdleになるべく実行する
    * ページをスキャンし終わってEmojiの文字列を`img`に置き換えていく処理を1タスクとしてキューに追加していく
    * `requestIdCallback`に登録したハンドラから一つづつタスクをとって処理する

### 使用したライブラリ／サービス／データ

* [React](https://reactjs.org/): Chrome拡張のOptionページの実装に使用
* [jQuery XPath](https://github.com/ilinsky/jquery-xpath):　Content scriptでXPathを使ってEmojiを検索するのに使用
* [Cult of the Party Parrot](https://cultofthepartyparrot.com/): 様々なParty parrotをダウンロードできる
* [pile-of-poo](https://emojipedia.org/pile-of-poo/): https://emojipedia.org/pile-of-poo/
    * :poop:のemojiデータをお借りました
* [PARTI-FY](https://nathanielw.github.io/party-ify/): 任意の画像をParty Parrot風にするWebサービス。Party Poopを作るために使用



## Installation

ParrotifyはまだプロトタイプでChrome Web Storeに出していないので、ソースからビルドする必要があります。

<a href="https://github.com/yukinarit/parrotify"><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44173/3bc3e4bf-a034-ab1a-6693-5492dff90d20.png" width=450></a>
※クリックするとGitHubへ遷移します。

* GitHubからソースをcloneする

```
$ git clone git@github.com:yukinarit/parrotify.git
```

* ビルド

```
$ cd option-app
$ npm i
$ npm run build
```

* Parrotifyをブラウザにロード
    * Chromeを開く
    * アドレスバーに`chrome://extensions`と入力する
    * `Load unpacked`をクリックする
    * Parrotifyのディレクトリを指定する

## おわりに

作者は普段はバックエンド開発者で、Chrome拡張やフロントエンドに関しては素人です。このアプリを面白いと思っていただいた方は、

* LGTM
* 機能追加やBugfix
* フィードバック
* タイポ修正やドキュメント

等いただけたら大変嬉しいです！