前回記事からの続きです。
本記事では前回動作確認を行った公式サンプルコードを編集しながらLINEミニアプリの機能を体験すると共に、便利なデバッグツールの使い方についても解説します。
本記事の対象者
- 基本的なプログラミングを理解している方
- LINEミニアプリやLIFFを利用した開発が初めて(久しぶり)の方
- 前回記事で解説したアプリケーションが手元にある方
準備
まず、前回のプロジェクトを少し編集します。
起動コマンドの変更
前回は公式に習ってNetlifyへアプリケーションをデプロイし、そのURLをミニアプリのエンドポイントとして利用しましたが、今回は利便性のためGitpod上のプレビューURLを利用します。
プレビューにはwebpack-dev-server
を利用しておりますので、起動時のコマンドを以下のように変更します。
"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
{
"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
、左サイドメニューのブラウザアイコンとクリックし、問題なく起動することを確認します。
確認できたら左サイドメニューの鍵アイコンをクリックし、プレビューを公開します。これにより誰にでも見ることができるようになります。
URLをコピーし、LINE DevelopersのLINEミニアプリチャネル > LIFFタブ内のエンドポイント(開発用)に貼り付け、「更新」をクリックします。これでLINEミニアプリはNetlifyにデプロイされたものではなく、Gitpod上のプレビュー状態のアプリケーションにアクセスするようになります。
LINEミニアプリの設定変更
最後にこの後解説する機能を利用出来るようにするため、ミニアプリの設定を変更します。2つのラジオボタンをオンにしてください。
プロジェクトの編集
画像の追加
src/vanilla
以下にpublic
という名前でフォルダを作成し、以下の画像を追加します。
public/header.png
public/pizza_icon.png
ソースコードの編集
準備が出来たので、ソースコードを編集していきましょう。HTMLファイルにボタンを追加し、Javascriptでクリックした時に関数を呼び出すようにした単純な変更です。
長いので変更後の全文を掲載します。
<!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>
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にアクセスし変更点が反映されているかを確認して下さい。
機能の体験
では機能を体験してみましょう。
ログイン/ログアウト
「Login with LINE」ボタンをクリックすると、LINEのメールアドレス/パスワードを利用してサービスにログイン出来ます。
2回目以降は前回のデータが記憶されていますので、より便利です。
ログインが完了するとliff.getDecodedIDToken()
でユーザーのプロフィールデータが取得出来るようになります。
なお設定により、メールアドレスも取得可能です。ドキュメントはこちら。
「Logout」ボタンをクリックするとログアウトできます。
Share Target Picker
Share Target Pickerは、LINEの友だちやグループにミニアプリ内で生成したメッセージを送信することが出来る機能です。送り主は自分、届くのは友だちとの1:1トークやグループになります。
メッセージはシンプルなテキストから画像、スタンプ、FlexMessageを利用したリッチなものまで、様々なフォーマットが利用出来ます。
Share Target Pickerのドキュメントはこちら。
Flex Messageのドキュメントはこちら。
Scan Code
Scan Codeは二次元バーコードを読み取ることが出来る機能です。スマホだけでなく、PCでもカメラが搭載されていれば利用可能です。
ドキュメントはこちら。
Service Message
サービスメッセージはミニアプリから任意のタイミングでユーザーにメッセージを送ることが出来る機能です。Share Target Picker、LINE公式アカウントのPush Messageと比較すると以下のようになります。
サービスメッセージ | Share Target Picker | Push Message | |
---|---|---|---|
送り主 | ミニアプリ | アプリを開いているユーザー | 公式アカウント |
届く場所 | 「Service Message」専用の受信トークルーム | ユーザーが選択したトーク/グループ | ユーザー |
タイミング | ユーザーの操作(アクション)に対する確認や応答であれば任意 | 即時 | 任意 |
費用 | 無料 | 無料 | 有料 |
内容の制限 | ユーザーの操作(アクション)に対する確認や応答に限る | 常識的な範囲で任意(規約による) | 常識的な範囲で任意(規約による) |
利用前の審査 | あり | なし | なし |
上記は変更されることもあります。利用の際には公式を参照するように下さい。
なお、Service Messageの利用は現在審査に通過したサービスに限られますので、今回も実装はしてありません。
長くなりますので後日詳細な解説記事をアップする予定です。
Custom Action Button
カスタムアクションボタンは、ミニアプリを利用しているユーザーが友だちにミニアプリをシェアすることが出来る機能です。
公式のテンプレートやガイドライン、アイコン画像が用意されており、それらを利用して実装する必要があります。ドキュメントはこちら
liff.shareTargetPicker()
を実行する際にindex.js
下部のcustomActionButtonMessage
のようなオブジェクトを指定し利用します。
他
ミニアプリには他の機能もたくさんあります。以下のサイトで挙動が確認できますので、興味のある方は是非ご確認下さい。
LINEミニアプリとして起動
これまではGitpodのプレビューURLで挙動を確認してきました。つまり、これまで見ていたのはLINEミニアプリではなく、LIFF SDKを導入したWebアプリケーションと言えます。
LINEミニアプリとしての挙動を確認するにはLINE Developers記載の開発用LIFF URLにアクセスします。
PCからは見れません。
スマホから見てみます。
先程までと違い、ログインボタンをクリックせずとも開いたタイミングで自動的にログインされることが確認出来ます。
LIFF Inspectorの紹介
最後に、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
続いて新しいターミナルを立ち上げ、
以下のコマンドを実行します。
$ 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
を編集します。
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]
を付けたものを設定します。
この状態でミニアプリにアクセスすると、LIFF Inspectorを起動したターミナルに開発者ツールのURLが表示されます。
アクセスすると、コンソールにリアルタイムにエラーメッセージ等が出力されているのが確認出来ます。
これでスマホ上でしか実行出来ないミニアプリをPC上でリアルタイムにデバッグ出来るようになりました。是非開発にお役立て下さい。
まとめ
本記事ではLINEミニアプリの代表的な機能とその実装サンプルをご紹介しました。
是非既存のWebアプリケーションのLINEミニアプリとしての実装、また新しいLINEミニアプリの創出をお待ちしています。