🎉

Google Meetに新登場したリアクション機能を便利にするChrome拡張

2023/02/19に公開

Google Meetに気軽に絵文字のリアクションを送信することができる、リアクション機能が登場しました。

https://workspaceupdates.googleblog.com/2023/01/in-meeting-reactions-for-google-meet.html

弊社では登場からすぐこれが大活用されており、絵文字が飛び交うWeb会議が開催されまくっていますが、今回はこのリアクション機能を便利にするChrome拡張機能を作成したので紹介させてください。

つくったもの

https://chrome.google.com/webstore/detail/meet-reaction-keydown/agliiaikkgebokbnbcfopkbcgnkoifjf

機能

まずは、デフォルトでリアクションバーを有効にする機能です。

Google Meetのリアクション機能を使うためには画面下部のコントロールバーの笑顔アイコンを選択してリアクションバーを表示させてあげる必要があります。

Google Meetのコントロールバー

これ自体はボタンを押せば良いだけなのですが、間違えて隣の挙手ボタンを押してしまい、会議中に何もないのに突然手を挙げてしまう人をが多発してしまっていました。

今回、拡張機能をつくろうと思ったのもこの問題を解決しようとしたのがきっかけで、解決策としては単純にリアクションバーが表示されていないときに笑顔アイコンを押してあげるようにしています。

const main = (e) => {
  const callback = () => {
    const reactionButton =
      document.querySelector('[aria-label="リアクションを送信"]') ??
      document.querySelector('[aria-label="Send a reaction"]') ??
      null

    const reactionBanner = 
      document.querySelector('[aria-label="リアクション"]') ??
      document.querySelector('[aria-label="Reactions"]') ??
      null

    if (reactionButton != null) {
      clearInterval(buttonIsExsist)
      if (reactionBanner == null) reactionButton.click()

      ...
    }
  }
  const buttonIsExsist = setInterval(callback, 1000)
}

後述しますが、ボタン周りのDOMの取得にはaria-labelを使っています。

また、Google Meetでは待機画面とミーティング画面は同一ページなので、setIntervalを用いてreactionButtonが取得できるまで(ミーティング画面になるまで)繰り返し実行しています。

これだけだと物足りなかったので、もう1つの機能として数字キーでリアクションを送信することができる機能を付けました。

Google Meetのリアクションバーに数字のラベルが付いている

実装としては各リアクションのボタンに対してcreateElementでラベルを付与し、addEventListenerでkeydownイベントを監視、対応したボタンをクリックするようにしています。

const getReactionDOM = () => {
  const reactionBanner = getReactionBanner()

  return {
    heart: reactionBanner.querySelector('img[src*="1f496"]'),
    thumbsup: reactionBanner.querySelector('img[src*="1f44d"]'),
    ...
  }
}

const startListenKeydown = () => {
  const reactionBanner = getReactionBanner()
  if (reactionBanner != null) {
    if (bunnerIsExsist != null) clearInterval(bunnerIsExsist)

    const reactions = getReactionDOM()
    Object.keys(reactions).forEach((key, index) => {
      const badge = document.createElement('div')
      badge.textContent = index + 1
      badge.classList.add('badge')
      reactions[key].parentNode.insertBefore(
        badge,
        reactions[key].nextElementSibling
      )
    })
    document.addEventListener('keydown', reactionKeydown)
  }
}

const reactionKeydown = (event) => {
  const inputElements = [...document.querySelectorAll('textarea, input')]

  const reactions = getReactionDOM()

  let typing = false
  inputElements.map((inputElement) => {
    if (inputElement === document.activeElement) {
      typing = true
    }
  })

  if (!typing) {
    switch (event.key) {
      case '1':
        reactions.heart.click()
        break
      case '2':
        reactions.thumbsup.click()
        break
      ...
    }
  }
}

リアクションボタンの取得にはimgのsrcの一部を利用しています。ボタンでaria-labelを用いている理由も同様ですが、DOMの取得のために利用する要素はアップデートなどによる変更が入りにくそうなものを選択しています。

また、常にkeydownでのリアクションが有効になっているとコメント入力時にもリアクションをしてしまうので、textareaとinputにfocusしているときはリアクションがされないようにすることで対応しています。

困ったこと

ボタンの要素の取得にaria-labelを使っているため、全ユーザーが利用できるようにするためには、多言語対応が必要になります。

これは、chrome.i18n.getMessageを利用して対応しようと思ったのですが、Google Meetの言語はChromeの言語設定でなく、Googleアカウントの言語設定に紐付いているらしく利用できませんでした。

そのため、現状では一時的に日本語/英語のaria-labelをコード内に直接書き込んでいます。

また、各リアクションボタンには画面サイズによって変化するdata-iml属性(何に使うものだろう?)が設定されているため、画面サイズが変更されるとラベルを付与するためのDOMの再取得が必要になってしまっています。

これらの問題があるので、どうやって各要素を取得するのかは再考しながらアップデートしたいと思います。

まとめ

今回は簡単なものだったのでpure jsで開発をしましたが、Chrome拡張を作成するためのReactフレームワークであるplasmoを使った開発も次回はしてみたいと思っています。

https://docs.plasmo.com/

Discussion