【Electron入門】GithubのIssueを一瞬で確認できるアプリを作って業務効率化してみた #1 Alfredっぽいwindow作成編

## 完成品
option(alt) + Spaceで表示/非表示が切り替えられます。

![scc.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/488042/e31df68d-1879-ca25-6ad3-950a4f87d880.gif)


### ソースコード
https://github.com/ikkei12/git-app.pub

## はじめに
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/488042/c4f4f457-8aaf-84be-ff7b-122b4d6cf3b6.png)

[Alfred](https://www.alfredapp.com/)最高ですよね。
彼のおかげでマウスを使う頻度が激減して助かっています。

Alfredと同じ手軽さで、自分がアサインされているissueを確認できるデスクトップアプリが作りたいなと思ったので、[Electron](https://www.electronjs.org/)で作ってみようと思いました。

Electronには

- JavaScriptで作れる
- Chromiumベースなのでwebエンジニアにも優しい

といった利点があり、HTML/CSS/JavaScriptを学んだ初心者の方でも扱いやすいかと思います！


## Electronのはじめかた

基本的に[公式ドキュメント](https://www.electronjs.org/docs/tutorial/quick-start#prerequisites)を参考にElectronのセットアップをするだけですが、一部変更を加えています。
  

### 1. 以下のコマンドを実行

``` shell
mkdir git-app && cd git-app
npm init -y
npm i --save-dev electron
npm i --save-dev electron-builder
```
### 2. html/jsファイルを作成
標準的なNode.jsアプリと同様に以下のような構成で始めます。

```shell
git-app/
├── package.json
├── main.js
└── index.html
```

```javascript:main.js
const { app, BrowserWindow } = require('electron')

function createWindow () {
  const win = new BrowserWindow({
    webPreferences: {
      nodeIntegration: false,
      contextIsolation: true,
    },
    frame: false,
    alwaysOnTop: true,
    useContentSize: true,
    transparent: true,
  })

  win.loadFile('index.html')

  win.on("blur", (e) => {
    // window外をクリックしたタイミング(blur)で閉じる
    app.hide();
  });
}

app.whenReady().then(createWindow)

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', () => {
  if (BrowserWindow.getAllWindows().length === 0) {
    createWindow()
  }
})
```

```html:index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello World!</title>
    <meta
      http-equiv="Content-Security-Policy"
      content="script-src 'self' 'unsafe-inline';"
    />
  </head>
  <body style="-webkit-app-region: drag">
    <div style="background: #fff; height: 300px">
      <h1>Hello World!</h1>
    </div>
  </body>
</html>
```

### 3. package.jsonを修正

package.jsonは以下のように書き換えます。

```json:package.json
{
  "name": "git-app",
  "version": "1.0.0",
  "description": "",
  "main": "main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "NODE_ENV=development electron .",
    "build": "NODE_ENV=production electron-builder"
  },
  "build": {
    "appId": "git-app",
    "mac": {
      "category": "git-app"
    }
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "electron": "^10.1.5"
  }
}
```

### 4. 起動

`npm start`
でこのようなwindowが表示されると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/488042/1cc922e1-a115-de16-6eb6-de36e1f11a5b.png)




## どこからでもショートカットでアプリを呼び出せるようにする

このままではwindowの表示/非表示を切り替えられないし、PCの起動時に毎回アプリを立ち上げる必要があります。

Alfredのような挙動を実現するには
　1. electronアプリを常駐で起動させておいて、
　2. 表示/非表示を切り替えるショートカットを登録する
必要があります。

### 1. electronアプリを常駐で起動させる

```javascript:main.js
app.dock.hide();

if (process.env.NODE_ENV !== "development") {
  app.setLoginItemSettings({
    openAtLogin: true,
    path: app.getPath("exe"),
  });
}
```
#### 解説
・`app.dock.hide()`
常駐のアプリをDockに表示したくないので、Dockから非表示にします。

・`app.setLoginItemSettings()`
でログイン時に自動で起動してくれるようになります。
ローカルでは起動したくないので、`NODE_ENV`で判定しています。



・macではログイン時に起動されるアプリは
`ユーザーとグループ > ログイン項目`に追加されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/488042/29233320-d7bb-4130-7ec1-2602700f5a60.png)

### 2. 表示/非表示を切り替えるショートカットを登録する

```javascript:main.js
app.on("ready", function () {
  globalShortcut.register("alt+space", function () {
    // 現在focusしているwindowを取得
    const window = BrowserWindow.getFocusedWindow();
    // windowが存在すればhide、なければshow
    window ? hideWindow(window) : showWindow();
  });
});

app.on("will-quit", function () {
  // 終了するタイミングで全てのglobalShortcutを解除
  globalShortcut.unregisterAll();
});

function showWindow() {
  // focusさせる事でBrowserWindowのblurイベントを検知させる
  app.focus({ steal: true });
  app.show();
}

function hideWindow(window) {
  // center()する事でshowする時に中央で表示される
  window.center();
  app.hide();
}
```

#### 解説
・アプリが`ready`状態になった段階で、ショートカットキーを登録しています。自分は`alt+space`で起動したいので`register`の第一引数に渡しています。

・第二引数ではショートカットキーが押されたタイミングで実行する関数を登録しています。現在focusされているwindowを取得し、存在する場合はhideメソッド、しなければshowメソッドを実行する事で表示を切り替えています。

・`globalShortcut`と言うのがポイントで、普通のショートカット([localShortcut](https://www.electronjs.org/docs/tutorial/keyboard-shortcuts#local-shortcuts))ではElectronアプリがフォアグラウンドになっている場合にしか反応しません。


<br>
#### このように動くと思います！

![test.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/488042/563a9615-0192-68f9-2d24-2ed8a4468d05.gif)

## Tips

#### Windowをドラッグできるようにする

``` html
<body style="-webkit-app-region: drag">
```

参考: https://www.electronjs.org/docs/api/frameless-window#draggable-region

## まとめ

ご覧いただきありがとうございました！
次回はGithub APIからIssueを取得して表示する部分を作成していきます！

[#2 GithubAPI編](https://qiita.com/ikkei12/items/264cdb241711c8e741ce)

Twitterのフォローもお願い致します！
https://twitter.com/1keiuu