2020年10月28日

MacのKarabiner-Elementsで「PC−Style Home/End」を有効にしたときにChrome Remote Desktopを除外する方法

数年前からRealforceのキーボードをMacBook Proにつないで使っています。とても打ちやすくて快適なのですが、長い間解決できずに困っている問題がありました。


それは、HomeキーとEndキーで行頭・行末にカーソルを移動するために Karabiner-Elements の「PC-Sylte Home/End」という設定を有効にすると、Google Chrome Remote Desktop を使ってWindowsマシンを操作しているときに、Homeが「Windows+左矢印キー」、Endが「Windows+右矢印キー」として伝わってしまい、アプリケーションのウィンドウがいきなり画面の左半分にリサイズされたり右半分にリサイズされたりしてしまう、というものです。

Karabiner-Elementsの設定で「PC-Style Home/End」を追加




Microsoft Remote Desktop などではこのような問題は起こりません。「Chrome Remote Desktop」 を使ったときにだけ起きる問題です。

これは Karabiner-Elements の「PC-Sylte Home/End」設定の「除外対象(frontmost_application_unless)」に、Chrome Remote Desktop が含められていないのが原因のようです。


どうやって Chrome Remote Desktop を除外対象に含めれば良いのか分からなかったのですが、今日は長い間避けて通ってきたこの問題にようやく時間をとって真剣に向き合うことにしました。


調査 & 試行錯誤すること1時間、ついに解決策を発見したのでメモしておきます。




設定のJSONファイルをエディタで開く


Chrome Remote Desktopを除外する記述を追加


"type": "frontmost_application_unless",  
の下の
 "bundle_identifiers": [ ... ]  
の配列に
    "^com\\.google\\.Chrome\\.app\\.*",
の行を追加すればOKです! 

(HomeキーとEndキーでそれぞれに設定があるので2箇所とも追加します。)


検索して見つけた一部のサイトでは、

  "^com\\.google\\.Chrome\\.app",

と書かれていたのですが、これだと上手く行かず、末尾に「\\.*」を追加することで正しく除外されるようになりました。


ちなみに末尾が「\\.*」だとChrome Remote Desktop だけではなく「全てのChromeアプリ」が除外対象になると思います。


もし不都合がある場合はそれぞれのChromeアプリ固有のIDを調べて個々に指定する必要があります。



アプリ固有のBundle Identifierの値は、Karabiner-Elementsに付属の「EventViewer」を起動すれば簡単に調べることが出来ます。


EventViewer で Bundle Identifierを調べる



最近は Microsoft のRemote Desktop よりも Chrome Remote Desktop を使う機会の方が多くなってきているので、これでさらに快適にリモート接続出来そうです!





🍺 

2020年10月1日

ReactによるWebアプリ開発を体系的に学べるコース「Epic React」を購入!

購入したコース




内容
  • React Fundamentals
  • React Hooks
  • Advanced React Hooks
  • Advanced React Patterns
  • React Performance
  • Testing React Apps
  • React Suspense
  • Build an Epic React App
  • Epic React Expert Interviews



購入の動機


以前からEgghead.ioの動画ブログでKent C. DoddsさんのReactやJavascriptに関する解説を読んだり観たりしていて、とても参考になっていたため。

今回のコースはそのKentさんが何年にもわたって数々の有料セミナーなどで教えてきた経験を元に作成された、渾身の作ということなので、まず期待はずれになることはないだろうと確信が持てました。


インストラクター



元PayPalのフロントエンド開発者で、Reactのテスト用ライブラリとして人気の「React Testing Library」の作者でもあります。

ブログを読むと、初めてソフトウェア開発者として職を得たきっかけ、Facebookからのオファーを断ってPayPalに入った経緯、PayPalでの仕事内容、AngularからReactに軸足を移した経緯や、人に教えることへの情熱などがよくわかります。



Egghead.ioでおすすめのコース


Kentさんのコースに興味を持たれた方は、まずはEgghead.ioで下の動画レッスンなどを視聴してみてはいかがでしょうか。英語のリスニング練習にも最適です! 😀

React Tutorial for Beginners

Collection - Testing JavaScript with Jest





 

2020年7月30日

Parcelでasync/awaitを使うと「regeneratorRuntime is not defined」エラーが出る場合の対処法

Parcel を使ってJavaScriptアプリケーションを書いているときに、async/awaitを使おうとすると下のエラーが出ました。

Uncaught ReferenceError: regeneratorRuntime is not defined

調べたところ、次のサイトに解決策が載っていました。





対処法1


ブラウザを比較的新しいものだけに限定して構わないのであれば、次の方法が簡単です。

package.jsonに以下を追記します。

  "browserslist": [
    "since 2017-06"
  ]
これでasync/awaitを使ってもエラーが出なくなります。



対処法2


もう一つの対処法としては、「regenerator-runtime」パッケージをインストールするというものがあります。

> npm install regenerator-runtime

でインストールしておき、index.jsなどから

import 'regenerator-runtime/runtime';

として読み込みます。

バンドルサイズが25KBほど増えてしまうようですが、こちらの方法でもエラーが出なくなります。古いブラウザもサポートする必要がある場合はこちらを使うことになると思います。






 .


2020年6月16日

データベース不要! ReactとGoogle Drive APIで任意のデータを保存する

前回はReactアプリにGoogle認証を組み込む方法を試しました。

今回はそのコードを使いながら、さらにGoogle Drive APIを使って任意のデータをログインしているユーザーのGoogle Driveに保存することに挑戦してみました。


完全にブラウザのみで動作するReactアプリケーションなので、バックエンドサーバは不要です。Netlifyなど静的サイトのホスティングサービスを使ってデプロイすることが可能です。


Screen shot on a PC

Screen shot on a phone


  • Google Drive内の所定のフォルダからファイル一覧を表示。
  • ファイル名がクリックされるとその内容(テキストデータ)を表示。
  • テキストを入力・編集して保存ボタンを押し、ファイル名を入力するとGoogle Driveにテキストデータを保存。

ということを行っています。



2つのContextプロバイダー


認証状態とアプリケーション固有の状態を分けて2つのContextプロバイダーで別々に共有するようにします。

import React from 'react';
import { FileList } from './FileList';
import { FileContent } from './FileContent';
import { AuthProvider } from './auth-state';
import { AppStateProvider } from './app-state';
import { Header } from './Header';
import './App.css';

function App() {
return (
<AuthProvider>
<AppStateProvider>
<div className="App">
<Header />
<FileList />
<FileContent />
</div>
</AppStateProvider>
</AuthProvider>
);
}
export default App;



Google Drive APIの有効化とAPI Keyの作成


下のドキュメントにしたがってGoogle Drive APIを有効化し、API Keyを作成しておいてください。

https://developers.google.com/drive/api/v3/quickstart/js



Google DriveへのCRUD処理


次にGoogle Driveにアクセスする処理を「google-api.js」に記述します。

google-api.js
---
---

前回作成した認証関係の関数に加えて、

 getFiles ファイル一覧取得  
 getFileContent     ファイル内容取得
 uploadFile ファイル保存
 deleteFile ファイル削除

などの関数をエクスポートしています。



アップロード処理がなかなか動かず苦労しましたが、最終的には下のサイトの情報にしたがってMultipart形式でPOST(更新のときはPATCH)リクエストを送ることで解決しました。


またファイルの内容を取得する方法も最初は分からず試行錯誤しました。こちらは下のURLにGETリクエストを送ることでダウンロード出来ました。

https://www.googleapis.com/drive/v3/files/${fileId}?alt=media&source=downloadUrl

もちろんリクエストヘッダーにはGoogle認証で得られたアクセストークンを付加する必要があります。


ファイルの保存先フォルダは現状では APP_FOLDER という定数で定義していますが、これはユーザーが任意のフォルダ名を指定出来るようにした方が便利ですね。


以上、今回のサンプルを作成してみて、バックエンドサーバ無しでもブラウザ上のJavaScriptだけでかなり柔軟にGoogle Driveへのデータ保存が出来るということが分かりました。


単純なテキストではなく例えばJSONを保存すれば、ある意味簡易的なデータベースとしても使えるかも知れませんね。何よりアプリの開発者側で保存先のデータベースやストレージを用意する必要が無いのが魅力的です。



今回作成したアプリケーションのソースコードはこちらにあります。






 

2020年6月14日

ReactのSPAアプリケーションでContextとHooksを使ってGoogle認証を実装する

ReactのSPAアプリケーションにおいて、ContextとHooksを使うことでGoogle認証の処理をラップして利用するというサンプルを作成しました。


Google Cloud Consoleでアプリケーションを登録する




OAuth consent screenを設定する  







OAuth client IDを作成する








Create React AppでReactアプリケーションを作成する


npx create-react-app project-name
cd project-name


public/index.html を編集する


WebアプリケーションでGoogle認証を利用するための最もシンプルなサンプルコードがこちらにあります。

Google Sign-In for Websites  |  Google Developers 

<html lang="en">
  <head>
    <meta name="google-signin-scope" content="profile email">
    <meta name="google-signin-client_id" content="YOUR_CLIENT_ID.apps.googleusercontent.com">
    <script src="https://apis.google.com/js/platform.js" async defer></script>
  </head>
  <body>
    <div class="g-signin2" data-onsuccess="onSignIn" data-theme="dark"></div>
    <script>
      function onSignIn(googleUser) {
        // Useful data for your client-side scripts:
        var profile = googleUser.getBasicProfile();
        console.log("ID: " + profile.getId()); // Don't send this directly to your server!
        console.log('Full Name: ' + profile.getName());
        console.log('Given Name: ' + profile.getGivenName());
        console.log('Family Name: ' + profile.getFamilyName());
        console.log("Image URL: " + profile.getImageUrl());
        console.log("Email: " + profile.getEmail());
        // The ID token you need to pass to your backend:
        var id_token = googleUser.getAuthResponse().id_token;
        console.log("ID Token: " + id_token);
      }
    </script>
  </body>
</html>

これを参考に public/index.htmlを編集します。

上のサンプルコードの中でReactアプリで必要になるのはGoogleのスクリプトを読み込んでいる行だけなので、その部分をコピーしてheadタグ内に挿入します。

    <script src="https://apis.google.com/js/platform.js"></script>

Googleのサンプルコードでは「async defer」という属性が付いていますが、Create React Appの場合これがあるとスクリプトが実行されるタイミングの関係で上手く動かないので削除しています。



.env.localファイルを作成する


次にプロジェクトのルートフォルダに「.env.local」 ファイルを作成して値を設定しておきましょう。

REACT_APP_CLIENTID=41702000-xxxxxxxxxxxxx.apps.googleusercontent.com

この環境変数の名前は、Create React Appの規約上「REACT_APP_」から始まる必要があるので注意してください。

ClientIdを環境変数に持たせておくことによって、本番用、ステージング用など、ビルド環境に応じて柔軟に変更することが出来るようになります。


src/google-auth.jsファイルを作成する


Google APIを実際に呼び出す際に使う関数をこのファイルにまとめておきます。

init(), signIn(), signOut()の3つの関数をエクスポートしています。
------


src/auth-state.jsファイルを作成する


上のgoogle-auth.jsに定義した関数をReactアプリ内で使うためのContextプロバイダーとフックを提供するファイルになります。

内部的にはuseReducerを使って認証状態を管理していますが、それはファイル内に隠蔽して外部へはAuthProviderコンポーネントとuseAuthStateフックをエクスポートしています。

useAuthStateフックからの戻り値は、認証状態を表す state オブジェクトとサインイン、サインアウト処理を呼び出すための関数を提供する actions オブジェクトの2要素配列となっています。

---
---


ここまで準備が出来れば、あとは

1. AuthProviderコンポーネントでアプリをラップする。
2. 任意のコンポーネント内でuseAuthStateフックを使って認証機能にアクセスする。

という形になります。


App.js
function App() {
  return (
    <AuthProvider>
      <div className="App">
        <header className="App-header">
          <h1>Google Auth React Examle</h1>
          <SignInOutButton />
        </header>
        <div className="App-main">
          <UserInfo />
        </div>
      </div>
    </AuthProvider>
  );
}


SignInOutButton.js
 
import React from 'react'; import { useAuthState } from './auth-state'; 
export function SignInOutButton() {
  const [state, actions] = useAuthState();
  const { isSignedIn } = state;
  const { signIn, signOut } = actions;
  if (isSignedIn === undefined) {
    return null;
  }
  return (
    <div>
      {isSignedIn ? (
        <button onClick={signOut}>Sign Out</button>
      ) : (
        <button onClick={signIn}>Sign In</button>
      )}
    </div>
  );
}




今回作成したアプリケーションのソースコードはこちらにあります。





以上、ReactのSPAアプリケーションにおいて、ContextとHooksを使ってGoogle認証の処理をラップするサンプルでした。



次回は、認証した後さらにGoogle Drive APIを使ってアプリケーションのデータを(バックエンドサーバー無しで)ブラウザから直接Google Driveに保存するということに挑戦したいと思います。





 

2020年5月23日

Macの音声読み上げ機能を言語別にキー一発で呼び出す方法

読み上げ機能は目に優しい


最近、ブラウザでニュースを読む時にMacの音声読み上げ機能を使うと、「目を閉じていてもニュースが頭に入ってくる」ことに気付きました。


これは慣れるとかなり便利。なんと言っても目の疲れが大幅に軽減できることが最大の利点です。







もちろんMacの標準状態でも、ブラウザで文章を選択して「Edit」メニュー → 「Start Speaking」を選べば読み上げ機能を使うことは可能です。

アクセシビリティの設定で任意のショートカットキーを設定することも出来ます。



もし一つの言語しか読み上げさせる必要がないのであれば、これで大丈夫です。


ただ、複数の言語を使い分けたい場合は、これだと読み上げに使用する言語(音声)をその都度選ぶことが出来ません。

日本語の音声を設定している状態で英語の文章を読み上げさせても、カナカナ読みの変な発音でしか読まれません。逆に英語の音声が選択されている状態で日本語を読み上げさせようとしても、上手く発音してくれません。

つまり、選択されている文章の言語によって読み上げに使う音声を切り替える必要があるわけです。


これが出来るように今回設定した方法を、以下にメモしておきます。


1. 言語別にAutomatorでサービスを作成


Automatorを起動して、新しい Quick Action を作成します。




Actionsから System → Speak Text を選んで、ドラッグアンドドロップで追加します。




読み上げに使う言語に対応した音声を選んで、後から分かる名前を付けて保存します。


これを、使いたい言語の数だけ繰り返します。



2. キーボードショートカットを設定


システム設定から「Keyboard」を選ぶと、「Services」のカテゴリ内に先ほどAutomatorで作ったQuick Actionが表示されます。




それぞれの言語に対応したQuick ActionのチェックをONにして、好きなショートカットキーを設定すれば完了です。

私の場合は、

Ctrl + Cmd + w  → 日本語読み上げ
Ctrl + Cmd + e  → 英語読み上げ

としています。

なるべく片手ですぐ押さえられる組み合わせが良いと思います。


ブラウザを開いて何らかの文章を選択状態にすると、Servicesメニューに上で作成したQuick Actionが追加されているのが分かります。






この設定で、Google Chrome、Safari, Firefox のいずれのブラウザでも文章を選択してキー一発で言語別に読み上げ機能を使うことが出来るようになりました!