19
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[エンジニア向け]LINEミニアプリの代表的な機能を実装/体験してみる

Last updated at Posted at 2022-06-30

貼り付けた画像_2022_06_30_8_12.png

前回記事からの続きです。

本記事では前回動作確認を行った公式サンプルコードを編集しながらLINEミニアプリの機能を体験すると共に、便利なデバッグツールの使い方についても解説します。

本記事の対象者

  • 基本的なプログラミングを理解している方
  • LINEミニアプリやLIFFを利用した開発が初めて(久しぶり)の方
  • 前回記事で解説したアプリケーションが手元にある方

準備

まず、前回のプロジェクトを少し編集します。

起動コマンドの変更

前回は公式に習ってNetlifyへアプリケーションをデプロイし、そのURLをミニアプリのエンドポイントとして利用しましたが、今回は利便性のためGitpod上のプレビューURLを利用します。

プレビューにはwebpack-dev-serverを利用しておりますので、起動時のコマンドを以下のように変更します。

package.json
  "scripts": {
-    "dev": "cross-env NODE_ENV=development webpack-dev-server --progress",
+    "dev": "LIFF_ID='前回取得したLIFF ID' cross-env NODE_ENV=development webpack-dev-server --progress",
    "build": "cross-env NODE_ENV=production webpack"
  },

babelの設定変更

続いて、babelの設定変更を行います。これをしておかないと後ほどエラーでビルドに失敗します。

$ gitpod /workspace/line-liff-v2-starter/src/vanilla (master) $ yarn add --dev @babel/core @babel/preset-env
.babelrc
{
    "presets": [
-      "env"
+      ["@babel/preset-env"]
    ]
}

エンドポイントの変更

ではこちらで一度プレビューしてみましょう。

$ gitpod /workspace/line-liff-v2-starter/src/vanilla (master) $ yarn dev

<w> [webpack-dev-server] "hot: true" automatically applies HMR plugin, you don't have to add it manually to your webpack configuration.
<i> [webpack-dev-server] Project is running at:
<i> [webpack-dev-server] Loopback: http://localhost:3000/
<i> [webpack-dev-server] On Your Network (IPv4): http://10.0.5.2:3000/
<i> [webpack-dev-server] Content not from webpack is served from '/workspace/line-liff-v2-starter/src/vanilla/public' directory

エラーなく起動できたらウィンドウ右下のPorts:3000、左サイドメニューのブラウザアイコンとクリックし、問題なく起動することを確認します。

image.png

貼り付けた画像_2022_06_29_15_06.png

確認できたら左サイドメニューの鍵アイコンをクリックし、プレビューを公開します。これにより誰にでも見ることができるようになります。

URLをコピーし、LINE DevelopersのLINEミニアプリチャネル > LIFFタブ内のエンドポイント(開発用)に貼り付け、「更新」をクリックします。これでLINEミニアプリはNetlifyにデプロイされたものではなく、Gitpod上のプレビュー状態のアプリケーションにアクセスするようになります。

貼り付けた画像_2022_06_29_15_12.png

LINEミニアプリの設定変更

最後にこの後解説する機能を利用出来るようにするため、ミニアプリの設定を変更します。2つのラジオボタンをオンにしてください。

貼り付けた画像_2022_06_29_15_15.png

プロジェクトの編集

画像の追加

src/vanilla以下にpublicという名前でフォルダを作成し、以下の画像を追加します。

header.png

public/header.png

pizza_icon.png

public/pizza_icon.png

image.png

ソースコードの編集

準備が出来たので、ソースコードを編集していきましょう。HTMLファイルにボタンを追加し、Javascriptでクリックした時に関数を呼び出すようにした単純な変更です。

長いので変更後の全文を掲載します。

index.html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta http-equiv="X-UA-Compatible" content="ie=edge" />
  <link rel="stylesheet" href="index.css" />
  <title>LIFF Starter</title>
</head>

<body>
  <div class="home">
    <h1 class="home__title">
      Welcome to <br /><a class="home__title__link" href="https://developers.line.biz/en/docs/liff/overview/">LIFF
        Starter!</a>
    </h1>
    <div class="home__badges">
      <span class="home__badges__badge badge--primary"> LIFF Starter </span>
      <span class="home__badges__badge badge--secondary"> vanilla </span>
      <span class="home__badges__badge badge--tertiary"> 0.1.0 </span>
      <a href="https://github.com/line/line-liff-v2-starter" target="_blank" rel="noreferrer"
        class="home__badges__badge badge--secondary">
        GitHub
      </a>
    </div>
    <div class="home__buttons">
      <a href="https://developers.line.biz/en/docs/liff/developing-liff-apps/" target="_blank" rel="noreferrer"
        class="home__buttons__button button--primary">
        LIFF Documentation
      </a>
      <a href="https://liff-playground.netlify.app/" target="_blank" rel="noreferrer"
        class="home__buttons__button button--tertiary">
        LIFF Playground
      </a>
      <a href="https://developers.line.biz/console/" target="_blank" rel="noreferrer"
        class="home__buttons__button button--secondary">
        LINE Developers Console
      </a>
      <hr width="100%" />
      <div>
        <img id="profile_img" width="100px" />
        <h2 id="profile_string" />
      </div>
      <hr width="100%" />
      <a id="login" class="home__buttons__button button--primary">
        Login with LINE
      </a>
      <a id="logout" class="home__buttons__button button--tertiary">
        Logout
      </a>
      <a id="stp" class="home__buttons__button button--secondary">
        Share Target Picker
      </a>
      <a id="scan" class="home__buttons__button button--secondary">
        Scan Code
      </a>
      <a id="service_message" class="home__buttons__button button--secondary">
        Service Message
      </a>
      <a id="custom_action" class="home__buttons__button button--secondary">
        Custom Action
      </a>
    </div>
  </div>
</body>

</html>
index.js
import './index.css';
import liff from '@line/liff'

document.addEventListener("DOMContentLoaded", function () {
    liff
        .init({ liffId: process.env.LIFF_ID })
        .then(() => {
            console.log("Success! you can do something with LIFF API here.")

            document.getElementById('login').addEventListener("click", liff_login);
            document.getElementById('logout').addEventListener("click", liff_logout);
            document.getElementById('stp').addEventListener("click", liff_shareTargetPicler);
            document.getElementById('scan').addEventListener("click", liff_scan);

            document.getElementById('service_message').addEventListener("click", mini_serviceMessage);
            document.getElementById('custom_action').addEventListener("click", mini_customAction);

            if (liff.isLoggedIn()) {
                const idToken = liff.getDecodedIDToken();
                document.getElementById('profile_img').src = idToken.picture
                document.getElementById('profile_string').innerHTML = "こんにちは!" + idToken.name + "さん!"
            } else {
                document.getElementById('profile_string').innerHTML = "ログインされていません。"
            }
        })
        .catch((error) => {
            console.log(error)
        })
});


function liff_login() {
    liff.login()
}

function liff_logout() {
    liff.logout()
    location.reload()
}

function liff_shareTargetPicler() {
    if (liff.isLoggedIn()) {
        liff
            .shareTargetPicker(
                [
                    {
                        type: "text",
                        text: "Hello, World!",
                    },
                ]
            )
    } else {
        alert("shareTargetPickerの利用にはログインが必要です。")
    }
}

function liff_scan() {
    if (liff.isLoggedIn()) {
        liff
            .scanCodeV2()
            .then((result) => {
                alert(result.value)
            })
            .catch((error) => {
                console.log("error", error);
            });
    } else {
        alert("scanCodeV2の利用にはログインが必要です。")
    }
}

function mini_serviceMessage() {
    alert("現在Service Messageの利用には審査承認が必要です。")
}

function mini_customAction() {
    if (liff.isLoggedIn()) {
        liff
            .shareTargetPicker(
                [
                    customActionButtonMessage
                ]
            )
    } else {
        alert("Custom Actionの利用にはログインが必要です。")
    }
}

const customActionButtonMessage = {
    "type": "flex",
    "altText": "代替テキスト",
    "contents":
    {
        "type": "bubble",
        "hero": {
            "type": "image",
            "url": location.href.replace(/\?.*$/,"") + "header.png",
            "size": "full",
            "aspectRatio": "20:13",
            "aspectMode": "cover"
        },
        "body": {
            "type": "box",
            "layout": "vertical",
            "contents": [
                {
                    "type": "box",
                    "layout": "vertical",
                    "contents": [
                        {
                            "type": "text",
                            "text": "Thanks!",
                            "size": "lg",
                            "color": "#000000",
                            "weight": "bold",
                            "wrap": true
                        }
                    ],
                    "spacing": "none"
                },
                {
                    "type": "box",
                    "layout": "vertical",
                    "contents": [
                        {
                            "type": "text",
                            "text": "ご注文を承りました。",
                            "size": "sm",
                            "color": "#999999",
                            "wrap": true
                        }
                    ],
                    "spacing": "none"
                },
                {
                    "type": "box",
                    "layout": "vertical",
                    "contents": [
                        {
                            "type": "box",
                            "layout": "horizontal",
                            "contents": [
                                {
                                    "type": "text",
                                    "text": "ピザ",
                                    "size": "sm",
                                    "color": "#555555",
                                    "wrap": false,
                                    "flex": 3
                                },
                                {
                                    "type": "text",
                                    "text": "× 1",
                                    "size": "sm",
                                    "color": "#111111",
                                    "wrap": false,
                                    "align": "end",
                                    "flex": 1
                                }
                            ],
                            "flex": 1,
                            "spacing": "sm"
                        },
                        {
                            "type": "box",
                            "layout": "horizontal",
                            "contents": [
                                {
                                    "type": "text",
                                    "text": "スパゲッティ",
                                    "size": "sm",
                                    "color": "#555555",
                                    "wrap": false,
                                    "flex": 3
                                },
                                {
                                    "type": "text",
                                    "text": "× 1",
                                    "size": "sm",
                                    "color": "#111111",
                                    "wrap": false,
                                    "align": "end",
                                    "flex": 1
                                }
                            ],
                            "flex": 1,
                            "spacing": "sm"
                        }
                    ],
                    "spacing": "sm",
                    "margin": "lg",
                    "flex": 1
                },
                {
                    "type": "box",
                    "layout": "vertical",
                    "contents": [
                        {
                            "type": "button",
                            "action": {
                                "type": "uri",
                                "label": "ご注文詳細",
                                "uri": "https://liff.line.me/" + process.env.LIFF_ID + "/"
                            },
                            "style": "primary",
                            "height": "md",
                            "color": "#17c950"
                        },
                        {
                            "type": "button",
                            "action": {
                                "type": "uri",
                                "label": "Share",
                                "uri": "https://liff.line.me/" + process.env.LIFF_ID + "/"
                            },
                            "style": "link",
                            "height": "md",
                            "color": "#469fd6"
                        }
                    ],
                    "spacing": "xs",
                    "margin": "lg"
                }
            ],
            "spacing": "md"
        },
        "footer": {
            "type": "box",
            "layout": "vertical",
            "contents": [
                {
                    "type": "separator",
                    "color": "#f0f0f0"
                },
                {
                    "type": "box",
                    "layout": "horizontal",
                    "contents": [
                        {
                            "type": "image",
                            "url": location.href.replace(/\?.*$/,"") + "pizza_icon.png",
                            "flex": 1,
                            "gravity": "center"
                        },
                        {
                            "type": "text",
                            "text": "ほげほげピザ",
                            "flex": 19,
                            "size": "xs",
                            "color": "#999999",
                            "weight": "bold",
                            "gravity": "center",
                            "wrap": false
                        },
                        {
                            "type": "image",
                            "url": "https://vos.line-scdn.net/service-notifier/footer_go_btn.png",
                            "flex": 1,
                            "gravity": "center",
                            "size": "xxs",
                            "action": {
                                "type": "uri",
                                "label": "action",
                                "uri": "https://liff.line.me/" + process.env.LIFF_ID + "/"
                            }
                        }
                    ],
                    "flex": 1,
                    "spacing": "md",
                    "margin": "md"
                }
            ]
        }
    }
}

編集できたら、PCのブラウザでURLにアクセスし変更点が反映されているかを確認して下さい。

機能の体験

image.png

では機能を体験してみましょう。

ログイン/ログアウト

「Login with LINE」ボタンをクリックすると、LINEのメールアドレス/パスワードを利用してサービスにログイン出来ます。

image.png

2回目以降は前回のデータが記憶されていますので、より便利です。

貼り付けた画像_2022_06_30_8_10.png

ログインが完了するとliff.getDecodedIDToken()でユーザーのプロフィールデータが取得出来るようになります。

貼り付けた画像_2022_06_30_8_12.png

なお設定により、メールアドレスも取得可能です。ドキュメントはこちら

「Logout」ボタンをクリックするとログアウトできます。

Share Target Picker

Share Target Pickerは、LINEの友だちやグループにミニアプリ内で生成したメッセージを送信することが出来る機能です。送り主は自分、届くのは友だちとの1:1トークやグループになります。

image.png

メッセージはシンプルなテキストから画像、スタンプ、FlexMessageを利用したリッチなものまで、様々なフォーマットが利用出来ます。

Share Target Pickerのドキュメントはこちら

Flex Messageのドキュメントはこちら

Scan Code

Scan Codeは二次元バーコードを読み取ることが出来る機能です。スマホだけでなく、PCでもカメラが搭載されていれば利用可能です。

貼り付けた画像_2022_06_30_8_53.png

ドキュメントはこちら

Service Message

サービスメッセージはミニアプリから任意のタイミングでユーザーにメッセージを送ることが出来る機能です。Share Target Picker、LINE公式アカウントのPush Messageと比較すると以下のようになります。

サービスメッセージ Share Target Picker Push Message
送り主 ミニアプリ アプリを開いているユーザー 公式アカウント
届く場所 「Service Message」専用の受信トークルーム ユーザーが選択したトーク/グループ ユーザー
タイミング ユーザーの操作(アクション)に対する確認や応答であれば任意 即時 任意
費用 無料 無料 有料
内容の制限 ユーザーの操作(アクション)に対する確認や応答に限る 常識的な範囲で任意(規約による) 常識的な範囲で任意(規約による)
利用前の審査 あり なし なし

上記は変更されることもあります。利用の際には公式を参照するように下さい。

なお、Service Messageの利用は現在審査に通過したサービスに限られますので、今回も実装はしてありません。

長くなりますので後日詳細な解説記事をアップする予定です。

Custom Action Button

カスタムアクションボタンは、ミニアプリを利用しているユーザーが友だちにミニアプリをシェアすることが出来る機能です。

公式のテンプレートやガイドライン、アイコン画像が用意されており、それらを利用して実装する必要があります。ドキュメントはこちら

custom_pizza.png

liff.shareTargetPicker()を実行する際にindex.js下部のcustomActionButtonMessageのようなオブジェクトを指定し利用します。

ミニアプリには他の機能もたくさんあります。以下のサイトで挙動が確認できますので、興味のある方は是非ご確認下さい。

LIFF Playground

LINEミニアプリとして起動

これまではGitpodのプレビューURLで挙動を確認してきました。つまり、これまで見ていたのはLINEミニアプリではなく、LIFF SDKを導入したWebアプリケーションと言えます。

LINEミニアプリとしての挙動を確認するにはLINE Developers記載の開発用LIFF URLにアクセスします。

貼り付けた画像_2022_06_30_12_11.png

PCからは見れません。

貼り付けた画像_2022_06_30_12_12.png

スマホから見てみます。

先程までと違い、ログインボタンをクリックせずとも開いたタイミングで自動的にログインされることが確認出来ます。

IMG_9859.png

LIFF Inspectorの紹介

image.png

最後に、LINEミニアプリのデバッグに便利なLIFF Inspectorをご紹介します。

LIFF Inspectorを利用すると、スマホ上でしか利用できないLINEミニアプリをPC上のChromeの開発者ツールに接続し、コンソール等の便利な機能を使いながら開発を進めることが可能になります。

まずはインストール済みのLIFF SDKのバージョンが2.19.0以上であること確認します。古い場合はアップデートします。

$ gitpod /workspace/line-liff-v2-starter/src/vanilla (master) $ npm list @line/liff
└── @line/liff@2.16.0

$ gitpod /workspace/line-liff-v2-starter/src/vanilla (master) $ yarn add @line/liff@2.19.0

続いて新しいターミナルを立ち上げ、

貼り付けた画像_2022_06_30_12_37.png

以下のコマンドを実行します。

$ cd src/vanilla/
$ gitpod /workspace/line-liff-v2-starter/src/vanilla (master) $ npx @line/liff-inspector

Need to install the following packages:
  @line/liff-inspector
Ok to proceed? (y) y ← yキー押下
Debugger listening on ws://10.0.5.2:9222

You need to serve this server over SSL/TLS
For help, see: https://github.com/line/liff-inspector#important-liff-inspector-server-need-to-be-served-over-ssltls

LIFF Inspectorサーバーが9222番で起動しました。3000番と同様に9222番もpublicに設定してください。

ターミナル右側の起動中のターミナル一覧から先程まで使っていたターミナルに戻り、パッケージをインストールします。

$ gitpod /workspace/line-liff-v2-starter/src/vanilla (master) $ yarn add @line/liff-inspector

index.jsを編集します。

index.js
import './index.css';
import liff from '@line/liff'
+ import LIFFInspectorPlugin from "@line/liff-inspector"

document.addEventListener("DOMContentLoaded", function () {
+   liff.use(new LIFFInspectorPlugin())
    liff
        .init({ liffId: process.env.LIFF_ID })

起動します。

$ gitpod /workspace/line-liff-v2-starter/src/vanilla (master) $ yarn dev

準備が出来ました。次に、エンドポイントを変更しましょう。これまでのURLの末尾に?li.origin=wss://9222-[URL]を付けたものを設定します。

貼り付けた画像_2022_06_30_13_03.png

この状態でミニアプリにアクセスすると、LIFF Inspectorを起動したターミナルに開発者ツールのURLが表示されます。

貼り付けた画像_2022_06_30_13_06.png

アクセスすると、コンソールにリアルタイムにエラーメッセージ等が出力されているのが確認出来ます。

貼り付けた画像_2022_06_30_13_07.png

これでスマホ上でしか実行出来ないミニアプリをPC上でリアルタイムにデバッグ出来るようになりました。是非開発にお役立て下さい。

まとめ

本記事ではLINEミニアプリの代表的な機能とその実装サンプルをご紹介しました。

是非既存のWebアプリケーションのLINEミニアプリとしての実装、また新しいLINEミニアプリの創出をお待ちしています。

19
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?