> ## Documentation Index
> Fetch the complete documentation index at: https://www.ayrshare.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# リンク完了イベント

> ユーザーがアカウントを連携した瞬間を、ポーリングではなく通知で知ることができます。

export const PlansAvailable = ({plans = [], maxPackRequired}) => {
  let displayPlans = plans;
  if (plans && plans.length === 1) {
    const lowerCasePlan = plans[0].toLowerCase();
    if (lowerCasePlan === "business") {
      displayPlans = ["Launch", "Business", "Enterprise"];
    } else if (lowerCasePlan === "premium") {
      displayPlans = ["Premium", "Launch", "Business", "Enterprise"];
    }
  }
  return <Note>
Available on {displayPlans.length === 1 ? "the " : ""}
{displayPlans.join(", ").replace(/\b\w/g, l => l.toUpperCase())}{" "}
{displayPlans.length > 1 ? "plans" : "plan"}.

{maxPackRequired && <span onClick={() => window.open('https://www.ayrshare.com/docs/additional/maxpack', '_self')} className="flex items-center mt-2 cursor-pointer">
 <span className="px-1.5 py-0.5 rounded text-sm" style={{
    backgroundColor: '#C264B6',
    color: 'white',
    fontSize: '12px'
  }}>
   Max Pack required
 </span>
</span>}
</Note>;
};

<PlansAvailable plans={["business"]} maxPackRequired={false} />

リンクページをポップアップで開くと、その中で起きたことを貴社自身のページでリッスンできます:
ユーザーが Reddit を接続した、途中でやめた、接続が失敗した、などです。結末ごとにイベントが
届くため、UI はタイマーではなく、起きた瞬間に更新されます。

リンクの作成時に [`origin`](/docs/apis/profiles/create-link-session) を設定し、返された `url` を
ポップアップで開いて、リッスンしてください。それ以外に必要なものはなく、`origin` なしで作成された
リンクには何の変更もありません — これまでとまったく同じように動作します。

<Note>
  **[埋め込みウィジェット](/docs/multiple-users/connect-widget)をお使いですか? これはすでに配線済み
  です。** スクリプトがこれらのイベントを受け取り、`connect:` プレフィックスを取り除いて
  `connect.on("success", …)` に渡します — `message` リスナーも、オリジンチェックも、書くべき
  `popup.closed` のポーリングもありません。このページはその土台となるワイヤープロトコルであり、
  **貴社が**ウィンドウを所有する場合 — ダイレクトモードや、自分で開くポップアップ — に対して
  構築するものです。
</Note>

<Note>
  イベントは、リンクに設定した正確な `origin` に対してのみ、そしてポップアップを開いたウィンドウに
  対してのみ送信されます。それでもリスナーでは必ず `event.origin` をチェックしてください: どの
  ページでも貴社のウィンドウにメッセージを post でき、偽装できないのはメッセージのうちオリジン
  だけです。
</Note>

## イベント一覧

貴社のページに post されるすべてのメッセージは `{ source: "ayrshare", version: 1, event, ... }` の
形のオブジェクトで、イベントがネットワークに関するものであれば `network` を伴います。伴わない
イベントは 2 つあります: ホスト型リンクページからの `connect:closed`(接続が 1 つ終わったのでは
なく、ユーザーが Done を押したことを意味します)と、ウィジェットの `connect:ready`(下記参照)です。
貴社のコードが合成する結末 — ブロックされたポップアップや、ユーザーが閉じたウィンドウ — は当社
からのものではなく、貴社が与えたものだけを保持します。

<Note>
  [埋め込みウィジェット](/docs/multiple-users/connect-widget)は 1 つのイベントで異なります。その
  `ready` はネットワークに関することではなくスロットが立ち上がったことを告げるため、`network` を
  伴いません — 一方、ポップアップの `ready` は、それが開かれた対象のネットワークを示します。
  1 つのリスナーで両方のサーフェスを扱う場合は、`ready` の `network` を防御的に読み取ってください。

  ウィジェットは、このサーフェスが決して送らない `cancelled` の reason も 1 つ追加します:
  `superseded` — 2 回目の `popup()` 呼び出しが進行中の試行を置き換えた場合です。ここでは
  ウィンドウは 1 つ、結末も 1 つなので、置き換えられるものがありません。
</Note>

| イベント                | 追加フィールド                     | 送信されるタイミング                                                                                                         |
| ------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `connect:ready`     |                             | ページが読み込まれ、リンクの検査が終わった。                                                                                             |
| `connect:started`   |                             | ユーザーがソーシャルネットワークに送られた。                                                                                             |
| `connect:selection` | `step`                      | ピッカーまたはフォームが画面に表示されている — ユーザーに操作すべきことがある。                                                                          |
| `connect:success`   | `refId`、`displayName`       | アカウントが接続され、**保存された**。`refId` は接続先の User Profile です。`displayName` はアカウント名で、まだ取得できていない場合は省略されます。                     |
| `connect:error`     | `message`、および存在する場合は `code` | 接続が失敗した。`code` は[エラーリファレンス](/docs/errors/overview)と一致します。失敗にカタログ化されたコードがない場合や、ブロックされたポップアップなど貴社自身のスニペットが合成する結末では省略されます。 |
| `connect:cancelled` | `reason`                    | ユーザーが途中でやめた。`reason` は `popupClosed`、`scopesDenied`、`userCancelled` のいずれかです。                                       |
| `connect:closed`    |                             | ポップアップが自ら閉じようとしている。ホスト型リンクページから届く場合は `network` を伴わず、接続が 1 つ終わったのではなくユーザーが Done を押したことを意味します。                       |

接続ごとに、`connect:success`、`connect:error`、`connect:cancelled` のうちちょうど 1 つが届き
ます。`connect:closed` はそのうちの 1 つではありません — その後に続いて、ポップアップが意図的に
閉じたことを伝えます。

`connect:success` はアカウントが保存された後にのみ送信されるため、その直後の
[`GET /user`](/docs/apis/user/profile-details) には接続されたアカウントがすでに反映されています。

<Note>
  `connect:error` の後、ポップアップはユーザーが何が問題だったかを読めるように開いたままになり
  ます。`connect:success` と `connect:cancelled` の後は自ら閉じます。デバッグ中にすべてのケースで
  開いたままにするには、URL に `&autoClose=false` を追加してください。
</Note>

## リッスンする

このスニペットが行っていることのうち、抜けやすいものが 2 つあります。`event.origin` をチェックする
ことと、ユーザーが手動で閉じたポップアップを監視することです — 閉じたウィンドウは何も送信できない
ため、気づく方法はポーリングだけです。

```javascript theme={"system"}
function connectAccount(url) {
  // Derive the origin from the URL you were given rather than hard-coding one:
  // if your account uses its own linking domain, the popup runs on that.
  const popupOrigin = new URL(url).origin;

  const popup = window.open(url, "ayrshare-connect", "width=600,height=800");
  if (!popup) {
    handleOutcome({ event: "connect:error", message: "The popup was blocked." });
    return;
  }

  // Declared before anything can call `cleanup`: a message arriving early would
  // otherwise hit `poll` in its temporal dead zone and throw.
  let poll;

  const cleanup = () => {
    clearInterval(poll);
    window.removeEventListener("message", onMessage);
  };

  const onMessage = event => {
    // Both checks, not just the origin: another tab or frame on the same origin
    // could otherwise post a message your handler would believe.
    if (event.origin !== popupOrigin || event.source !== popup) return;
    const message = event.data;
    if (!message || message.source !== "ayrshare") return;

    if (["connect:success", "connect:error", "connect:cancelled"].includes(message.event)) {
      // `finally`, so your own handler throwing cannot leave the poll running —
      // it would later see the closed popup and report `cancelled` on top of the
      // outcome you already had. Cleanup also matters after `connect:error`,
      // where the popup stays open so your user can read it.
      try {
        handleOutcome(message);
      } finally {
        cleanup();
      }
    }
  };
  window.addEventListener("message", onMessage);

  // A hand-closed popup sends nothing, so watch for it. Wait a moment before
  // deciding: the popup closes itself right after sending, and the message can
  // still be in flight when you first see the window go.
  let closedAt = null;
  poll = setInterval(() => {
    if (!popup.closed) return;
    if (closedAt === null) {
      closedAt = Date.now();
      return;
    }
    if (Date.now() - closedAt < 750) return;

    cleanup();
    handleOutcome({ event: "connect:cancelled", reason: "popupClosed" });
  }, 500);
}
```

## エラー

`connect:error` は API の他の部分と同じコードを伴うため、ここで見るコードは他の場所と同じ意味を
持ちます。このサーフェスに固有のものは 1 つです:

| Code  | 意味                                                                                                                         |
| ----- | -------------------------------------------------------------------------------------------------------------------------- |
| `513` | リンクが単一ネットワークの connect ウィンドウとして開かれたが、そのために作成されたものではなかった。この URL を自分で組み立てた場合にのみ到達します — このエンドポイントが返す `url` は、常に作成されたリンクと一致します。 |

それ以外はソーシャルネットワーク自身の失敗であり、その失敗がすでに持っているコードで報告されます —
たとえば Instagram の認可の問題は `322` で、これには Professional ではなくまだ Personal のままの
アカウントも含まれます。

### 無効なリンクは別の形で届く

**期限切れ**、**失効済み**、または**不明**なリンクは、ページがイベントの送信先を知る前に拒否される
ため、イベントを送信できません。ユーザーには理由とコードが画面に表示され、貴社のページはユーザーが
ウィンドウを閉じたときに `connect:cancelled` を受け取ります。

これらを区別するには、[Link Session の取得](/docs/apis/profiles/get-link-session)をポーリングして
ください: `expired` と `revoked` を確定的に報告し、存在しないリンクには `502` を返します。

## ポップアップを管理したくない場合

選択肢は 2 つあり、イベントを諦めるのは 2 つ目だけです。

**ウィジェットにウィンドウを所有させる。** [埋め込みウィジェット](/docs/multiple-users/connect-widget)が
代わりにウィンドウを開いて監視し、上記のすべてのイベントを貴社のハンドラーに届けます。アカウントが
保存された瞬間の `success` は変わらず受け取れます。配管を書かなくてよくなるだけです。さらに
ウィジェットのフレームなら、ほとんどのネットワークでネットワーク自身のログインまでポップアップが
まったく開きません。

**代わりにポーリングする。** ポップアップが本当に使えない場合 — サーバーレンダリングされるアプリや、
システムブラウザでリンクを開くモバイルアプリ — は、
[Link Session の取得](/docs/apis/profiles/get-link-session)をポーリングしてください。アカウントが保存
されると同時に `completedAt` と `completedNetworks` を報告します。これは `connect:success` が送信
されたはずの瞬間と同じです。Telegram は帯域外で完了しブラウザコールバックがまったくないため、常に
この方法で完了します。
