できるLIFFアプリ開発者になるための基本のシェアターゲットピッカー

## はじめに
LIFFアプリ開発について、基本的な実装方法は[前回の記事](https://qiita.com/course_k/items/c7c2dc4eab679e249e06)で紹介しました。

今回は、LIFFの特徴的な機能の一つであるシェアターゲットピッカーについて紹介します。

## シェアターゲットピッカーとは
シェアターゲットピッカーは、LIFFアプリからLINEの友だちやグループにメッセージを送信するための機能です。

### 事前準備
基本的な設定はすべて完了している想定ですすめます。
（詳細は[こちらの記事](https://qiita.com/course_k/items/c7c2dc4eab679e249e06)を参照してください。）
そのうえで、シェアターゲットピッカーを利用するには、LINE Developers側で有効化する必要があります。
LINE Developersのプロバイダー→チャネル→LIFFタブから、以下の設定が確認できます。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/6224b011-ab47-0f6a-2979-5c5413a87ed3.png'>
現在シェアターゲットピッカーはOFFになっているので、トグルボタンをクリックします。

同意して有効化します。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/acddce37-00fa-457f-205e-4ae476a223a5.png'>

以下のようにONの状態になれば準備完了です
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/86beb96f-3bc9-ba27-c65f-c20b76838dad.png'>


### 基本的な実装
まずはシェアターゲットピッカーの機能を簡単に確認していきます。
```react
import liff, { Liff } from "@line/liff";
import { useEffect, useState } from "react";

const App = () => {
  const [message, setMessage] = useState("");
  const [liffObject, setLiffObject] = useState<Liff>();

  useEffect(() => {
    initializeLiff();
  }, []);

  // liffの初期化
  const initializeLiff = async () => {
    try {
      await liff.init({
        liffId: import.meta.env.VITE_LIFF_ID,
        withLoginOnExternalBrowser: true,
      });
      setLiffObject(liff);
    } catch (error) {
      console.log(error);
    }
  };

  // シェアターゲットピッカーによるメッセージシェア
  const shareMessage = async () => {
    if (!liffObject) {
      console.log("LIFF is not initialized");
      return;
    }

    try {
      await liffObject.shareTargetPicker([
        {
          type: "text",
          text: message,
        },
      ]);
    } catch (error) {
      console.log(error);
    }
  };

  if (!liffObject) {
    return <div>Loading...</div>;
  }

  return (
    <div>
      <h1>シェアターゲットピッカー デモ</h1>
      <input
        type="text"
        value={message}
        onChange={(e) => setMessage(e.target.value)}
        placeholder="メッセージを入力"
      />
      <button onClick={shareMessage}>メッセージをシェアする</button>
    </div>
  );
};

export default App;
```
このコードでは、以下のような処理を行っています
* LIFFの初期化の関数を定義
* シェアターゲットピッカー用の関数を定義
* シェア用メッセージ入力欄と、シェア用ボタンの用意

こちらを実装すると以下のようになります

<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/f90d7be0-5044-10df-1041-a0894f2c36f1.png'>

適当なメッセージを入力して、「メッセージをシェアする」ボタンを押します。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/b1d4f705-aa7f-d12c-c7f9-092c900d353d.png'>

メッセージをシェアする先を選択する画面が表示されます。
グループ、友達、トークの中から自由に送信先を選択できます。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/31595a7f-ba37-a036-9470-6c33e51b2465.png'>

今回はLINE公式アカウントを送信対象に選択しました。

:::note alert
送信ボタンを押すとそのままメッセージが送信されるので、送信対象は慎重に選んでください。
:::

<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/c34fd494-2874-bdf4-62cd-cf6aad0ffe1e.png'>

ボタンを押下すると、以下のようにメッセージが送信されていることがわかります。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/b569338c-af7e-a1ca-98f9-f000d8aeef3a.png'>

### シェアターゲットピッカーのオプション
オプションは現状以下の1つのみのようです。

#### isMultiple
送信先を複数選択か、単一選択かに絞ることができます。
デフォルトはtrueとなっています。
falseに設定することで、送信先に一つしか選べなくなります。

### メッセージの種類
テキストメッセージ以外にも、画像、動画、位置情報など、様々な種類のメッセージを送信できます。以下は画像メッセージの例です：
```react
await liffObject.shareTargetPicker([
  {
    type: 'image',
    originalContentUrl: 'https://example.com/image.jpg',
    previewImageUrl: 'https://example.com/image_preview.jpg',
  },
]);
```
また、送信するメッセージは最大5つまで設定することができます。

### 実践的な実装例
それでは、より実践的な実装例を見ていきましょう。この例では、テキストメッセージと画像メッセージを選択して送信できるようにします。


```react
import { useState, useEffect } from "react";
import liff, { Liff } from "@line/liff";
import { SendMessagesParams } from "@liff/send-messages";

type MessageType = "text" | "image";

interface Message {
  type: MessageType;
  content: string;
}

function App() {
  const [messages, setMessages] = useState<Message[]>([]);
  const [currentMessage, setCurrentMessage] = useState("");
  const [currentType, setCurrentType] = useState<MessageType>("text");
  const [liffObject, setLiffObject] = useState<Liff>();
  const [liffError, setLiffError] = useState("");

  useEffect(() => {
    initializeLiff();
  }, []);

  // liffの初期化
  const initializeLiff = async () => {
    try {
      await liff.init({
        liffId: import.meta.env.VITE_LIFF_ID,
        withLoginOnExternalBrowser: true,
      });
      setLiffObject(liff);
    } catch (error) {
      setLiffError(error instanceof Error ? error.message : String(error));
    }
  };

  // メッセージ追加処理
  const addMessage = () => {
    if (currentMessage) {
      setMessages([
        ...messages,
        { type: currentType, content: currentMessage },
      ]);
      setCurrentMessage("");
    }
  };

  // シェアターゲットピッカーによるメッセージシェア
  const shareMessages = async () => {
    if (!liffObject) {
      alert("LIFF is not initialized");
      return;
    }

    try {
      const liffMessages: SendMessagesParams = messages.map((msg) => {
        if (msg.type === "text") {
          return { type: "text", text: msg.content };
        } else {
          return {
            type: "image",
            originalContentUrl: msg.content,
            previewImageUrl: msg.content,
          };
        }
      });

      await liffObject.shareTargetPicker(liffMessages, {
        isMultiple: true,
      });
    } catch (error) {
      alert("Failed to share: " + error);
    }
  };

  // エラー表示
  if (liffError) {
    return <div>Error: {liffError}</div>;
  }

  // 初期化待ち表示
  if (!liffObject) {
    return <div>Loading...</div>;
  }

  return (
    <div>
      <h1>シェアターゲットピッカー デモ</h1>
      <div>
        <select
          value={currentType}
          onChange={(e) => setCurrentType(e.target.value as MessageType)}
        >
          <option value="text">テキスト</option>
          <option value="image">画像</option>
        </select>
        <input
          type="text"
          value={currentMessage}
          onChange={(e) => setCurrentMessage(e.target.value)}
          placeholder={
            currentType === "text" ? "メッセージを入力" : "画像URLを入力"
          }
        />
        <button onClick={addMessage}>メッセージを追加</button>
      </div>
      <ul>
        {messages.map((msg, index) => (
          <li key={index}>
            {msg.type}: {msg.content}
          </li>
        ))}
      </ul>
      <button onClick={shareMessages}>メッセージをシェア</button>
    </div>
  );
}

export default App;
```

この実装例では、以下の機能を追加しています：

* テキストメッセージと画像メッセージの選択
* 追加したメッセージのリスト表示
* メッセージの複数送信
「メッセージをシェア」ボタンをクリックすると、追加したすべてのメッセージを共有できます。

<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/58109d18-5b29-1362-1e37-6978b777be87.png'>

今回は画像送信をしていませんが、設定したメッセージを複数送信することができました。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/b2ea398f-1536-28a5-ba5c-eb2a37af2b25.png'>

## まとめ
LIFFのシェアターゲットピッカーを使うことで、ユーザーが簡単にLINEの友だちやグループにメッセージを送信できるようになりました。

この機能を使いこなすことで、アプリの拡散や情報共有がより効果的に行えるようになります。

LIFFアプリには他にもいろいろな機能があるので、また別の機会に紹介したいと思います。