Open6

LINEのLIFF試す

しなぷすしなぷす

Create LIFF Appの実行時にはLIFF IDが必要となります。まず「チャネルを作成する」と「LIFFアプリをチャネルに追加する」を読み、LIFF IDを取得してください。

とのことなので、チャンネルを作成し、LIFFアプリをチャンネルに追加するということをやってみる

https://developers.line.biz/ja/docs/liff/getting-started/#step-two-create-channel

LIFFアプリを作成する場合や、次のステップでLIFFスターターアプリを試してみる場合、Create LIFF AppでLIFFアプリの開発環境を構築する場合は、こちらのチャネルを作成してください。

って書いてあったのでチャンネルのタイプはLINEログインを選択する

LIFFアプリを開発する場合、チャネルのアプリタイプは[ウェブアプリ]を選択してください。

って書いてあるのでチャンネルのアプリタイプは「ウェブアプリを選択」

で、その他は適当に埋めて作成

しなぷすしなぷす

Create LIFF Appを使って開発環境を作る

npx @line/create-liff-app --template nextjs --ts --npm

これ実行したらLIFF IDというものを求められたので、管理画面側でLIFFを作る?っぽい

ここから「追加」をクリック

サイズは「Tall」にしてみる
ちなみにサイズの参考はこれ
https://developers.line.biz/ja/docs/liff/registering-liff-apps/#using-share-target-picker

エンドポイントURLは「https://example.com/」
これでいいらしい。

LIFF URLを利用してLIFFアプリを起動した際に、このURLが利用されます。

だそうだ。つまりどういうことだってばよ。

Scopeは「openid」だけにする。

Scopeの参考はこれ
https://developers.line.biz/ja/docs/liff/registering-liff-apps/#using-share-target-picker

友達追加オプションは「On (normal)」にする。

友達追加オプションの参考はこれ
https://developers.line.biz/ja/docs/liff/registering-liff-apps/#using-share-target-picker

設定はこれで「追加」をクリック。

すると、LIFF IDが表示されてるので、これを使ってコマンドラインのやつに応答する。

(base) shinaps@calcifer dev % npx @line/create-liff-app --template nextjs --ts --npm
Need to install the following packages:
@line/create-liff-app@1.1.0
Ok to proceed? (y) y
Welcome to the Create LIFF App
? Enter your project name:  liff-sample
? Please enter your LIFF ID: 
 Don't you have LIFF ID? Check out https://developers.line.biz/ja/docs/liff/getting-started/ {LIFF IDをここにペースト}

Generating liff app using `create-app`, this might take a while.

Need to install the following packages:
create-next-app@14.2.1
Ok to proceed? (y) y
✔ Would you like to use ESLint? … No / Yes
✔ Would you like to use Tailwind CSS? … No / Yes
✔ Would you like to use `src/` directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to customize the default import alias (@/*)? … No / Yes
Creating a new Next.js app in /Users/shinaps/dev/liff-sample.
...
..
.

で完了したっぽい。

しなぷすしなぷす
npm run dev

で起動したら、404になってしまった。

npm run dev

> liff-sample@0.1.0 dev
> next dev

  ▲ Next.js 14.2.1
  - Local:        http://localhost:3000
  - Environments: .env.local

 ✓ Starting...
Attention: Next.js now collects completely anonymous telemetry regarding usage.
This information is used to shape Next.js' roadmap and prioritize features.
You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL:
https://nextjs.org/telemetry

 ✓ Ready in 1992ms
 ○ Compiling /_not-found ...
 ✓ Compiled /_not-found in 1461ms (461 modules)
 GET / 404 in 1561ms
 ✓ Compiled in 79ms (233 modules)
 ○ Compiling /_error ...
 ✓ Compiled /_error in 611ms (715 modules)
 GET /favicon.ico 500 in 676ms
 GET / 404 in 33ms
 GET /favicon.ico 500 in 4ms

どうやらApp Routerのディレクトリ構成なのに何故かpagesディレクトリが一番上の階層にあってバグってるらしいので削除したらNext.jsのいつもの画面が表示された。

でもこれではLIFFの画面ではないので、多分違うので、pagesディレクトリを復元した。

pages/_app.tsxは以下のようになっていた。

import "../styles/globals.css";
import type { AppProps } from "next/app";
import type { Liff } from "@line/liff";
import { useState, useEffect } from "react";

function MyApp({ Component, pageProps }: AppProps) {
  const [liffObject, setLiffObject] = useState<Liff | null>(null);
  const [liffError, setLiffError] = useState<string | null>(null);

  // Execute liff.init() when the app is initialized
  useEffect(() => {
    // to avoid `window is not defined` error
    import("@line/liff")
      .then((liff) => liff.default)
      .then((liff) => {
        console.log("LIFF init...");
        liff
          .init({ liffId: process.env.NEXT_PUBLIC_LIFF_ID! })
          .then(() => {
            console.log("LIFF init succeeded.");
            setLiffObject(liff);
          })
          .catch((error: Error) => {
            console.log("LIFF init failed.");
            setLiffError(error.toString());
          });
      });
  }, []);

  // Provide `liff` object and `liffError` object
  // to page component as property
  pageProps.liff = liffObject;
  pageProps.liffError = liffError;
  return <Component {...pageProps} />;
}

export default MyApp;

自分、App RouterからNext.jsに入門したので_app.tsxわからん。し、眠いし調べるのめんどくさい。

まあでも多分layout.tsxに書き写せばいいだろということでやってみる。

しなぷすしなぷす

そんなこんなでChatGPTに書かせていい感じにしたのがこれ

"use client";

import React, { createContext, useContext, useEffect, useState } from "react";
import { liff, Liff } from "@line/liff";

// コンテキストの型を定義
type LiffContextType = {
  liffObject: Liff | null;
  liffError: string | null;
};

// コンテキストの作成
const LiffContext = createContext<LiffContextType | undefined>(undefined);

// コンテキストプロバイダーコンポーネント
export const LiffProvider = ({ children }: any) => {
  const [liffObject, setLiffObject] = useState<Liff | null>(null);
  const [liffError, setLiffError] = useState<string | null>(null);

  useEffect(() => {
    const initializeLiff = async () => {
      try {
        await liff.init({ liffId: process.env.NEXT_PUBLIC_LIFF_ID! });
        console.log("LIFF init succeeded.");
        setLiffObject(liff);
      } catch (error: any) {
        console.log("LIFF init failed.");
        setLiffError(error.toString());
      }
    };

    initializeLiff();
  }, []);
  const value = { liffObject, liffError };

  return <LiffContext.Provider value={value}>{children}</LiffContext.Provider>;
};

// コンテキストを使用するためのフック
export const useLiff = () => {
  const context = useContext(LiffContext);
  if (context === undefined) {
    throw new Error("useLiff must be used within a LiffProvider");
  }
  return context;
};
しなぷすしなぷす

process.env.NEXT_PUBLIC_LIFF_ID
ってビルド時にインライン化されてしまうので練習用なら直で書いてしまっても別にいいんだけどもね