kintone hack の予選で披露したアプリ「メッセージヨマセール」の作り方

# みなさァ～ん、kintone hack ・・・知ってますか👀？？


<iframe width="480" height="360" src="https://www.youtube.com/embed/Ll_G-mMHUEk" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>


サムネでやりすぎと言われていますがｗ
こんな作品で[kintone hack 予選会](https://page.cybozu.co.jp/-/kintonehack2021)に参加してきました。

今回はそんな私が作った、「メッセージヨマセール」の作り方を紹介したいと思います。

# アプリの準備

| フィールド種類 | フィールドコード | 備考 |
|:-:|:-:|:-:|
| ラジオボタン  | 確認  |未,済　（デフォルトは「未」）   |
|   ドロップダウン|緊急度   | 緊急, 普通  |
|  文字列(複数行) |メッセージ   |必須   |
| ユーザー選択  | 宛先  | 必須  |
|  リンク | URL  | Webサイトアドレス   |
|  ユーザー選択 | 送り主  | デフォルトはログインユーザー  |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536148/b3b8d4a5-f9d4-bed8-5ed2-29c4d1f01be3.png)

# JavaScript

こちらの記事をこっそりパクいや、参考にさせていただきました🙏
@youtoy さん、ありがとうございます！！！

https://qiita.com/youtoy/items/051dc658025a3b21c7f0

## JavaScript / CSSでカスタマイズ
こちらを追加しておきます。

[GSAP](https://greensock.com/get-started/)
`https://cdnjs.cloudflare.com/ajax/libs/gsap/3.5.1/gsap.min.js`

[kintone REST API Client](https://github.com/kintone/js-sdk/tree/master/packages/rest-api-client)
`https://unpkg.com/@kintone/rest-api-client@latest/umd/KintoneRestAPIClient.min.js`

## コード

突貫工事気味に作りましたので、動かないところがあるかも？
これはメッセージヨマセールアプリ自体のコードなので、
ポータルやスペースで使用したいときはコピペして調整して使ってね。

```js
(() => {
  "use strict";
  // 読み上げ
  const readJpn = async (msg) => {
    const u = new SpeechSynthesisUtterance();
    u.text = msg;
    u.lang = "jp-ja";
    u.rate = 1.0;
    speechSynthesis.speak(u);
  };

  // 表示
  kintone.events.on(["app.record.index.show"], (event) => {
    // ポータルに設置するときはアプリIDベタ書きになる
    const appId = kintone.app.getId();
    let count = 0;
    const sp = kintone.app.getHeaderMenuSpaceElement();
    const btn = new Kuc.Button({
      text: "レコード作成",
      type: "submit",
    });

    const i = setInterval(async () => {
      const client = new KintoneRestAPIClient();
      const resGet = await client.record.getRecords({
        app: appId,
        query: `確認 in ("未") and 宛先 in ("${kintone.getLoginUser().code}")`,
      });
      if (resGet.records.length > 0) {
        const randRange = Math.floor(Math.random() * resGet.records.length);
        createText(resGet.records[randRange]);
      } else {
        clearInterval(i);
        console.log("流れるメッセージ終了");
      }
    }, 1000);

    const createText = async (record) => {
      const div_text = document.createElement("div");
      div_text.id = "text" + count;
      count++;
      div_text.style.position = "fixed";
      div_text.style.whiteSpace = "nowrap";
      div_text.style.left = document.documentElement.clientWidth + "px";
      const random = Math.round(
        Math.random() * document.documentElement.clientHeight
      );
      div_text.style.top = random + "px";

      div_text.addEventListener("click", async () => {
        const client = new KintoneRestAPIClient();
        const rec = {
          確認: {
            value: "済",
          },
        };
        const resPut = await client.record.updateRecord({
          app: appId,
          id: record.$id.value,
          record: rec,
        });
      });
      const a = document.createElement("a");
      a.href = record.URL.value;
      a.target = "_blank";
      const str = document.createTextNode(record.メッセージ.value);
      if (record.緊急度.value === "緊急") {
        a.style = "color:red; font-weight:bold;font-size:x-large;";
        btn.addEventListener("click", {
          handleEvent: readEvent,
          arg1: record.メッセージ.value,
        });
        // this の扱い方がよくわからないので function
        function readEvent(event) {
          readJpn(this.arg1);
          event.currentTarget.removeEventListener("click", this);
        }
        btn.click();
      } else {
        a.style = "color:black; font-size:x-large;";
      }
      a.appendChild(str);

      div_text.appendChild(a);
      document.body.appendChild(div_text);

      await gsap.to("#" + div_text.id, {
        duration: 20,
        x: -1 * (document.documentElement.clientWidth + div_text.clientWidth),
      });

      div_text.parentNode.removeChild(div_text);
    };

    return event;
  });
})();
```

# まとめというか感想
我ながらしょうもないhackでしたが、予選会で笑ってもらって嬉しかったです。
こんなショウモナイ内容でも怒られないのがいいですね😂サイボウズさん心広い！

kintone hack 毎年やっているみたいなので、どなたでも、是非、お気軽に挑戦してみてください😁