# 操作ガイド

このセクションでは、ライブラリのコンポーネントを使用して一般的なタスクを完了したり、特定のワークフローを実装したりするための、実践的なステップバイステップガイドを提供します。各ガイドは、特定の結果を効率的に達成できるよう、自己完結型の説明書として設計されています。

<x-cards data-columns="2">
  <x-card data-title="ヘッダーにカスタム要素を追加する" data-icon="lucide:layout-template" data-href="#how-to-add-custom-elements-to-the-header">
    `addons`レンダープロップを使用して、`Header`コンポーネントをカスタムボタン、ナビゲーション、またはその他のインタラクティブな要素で拡張する方法を学びます。
  </x-card>
  <x-card data-title="オプションのコンポーネント依存関係を処理する" data-icon="lucide:puzzle" data-href="#how-to-handle-optional-component-dependencies">
    管理者に必要なコンポーネントをオンデマンドでインストールするように促すワークフローを実装し、ブロックレットの機能がスムーズに実行されるようにします。
  </x-card>
  <x-card data-title="リアルタイムのユーザー通知を実装する" data-icon="lucide:bell-ring" data-href="#how-to-implement-real-time-user-notifications">
    WebSocketを使用してリアルタイム通知システムを設定し、アプリケーション内の重要なイベントをユーザーに通知し続けます。
  </x-card>
  <x-card data-title="BlockletStudioでリソースを公開する" data-icon="lucide:rocket" data-href="#how-to-publish-resources-with-blockletstudio">
    `BlockletStudio`コンポーネントを統合して、リソースと依存コンポーネントを公開および管理するためのユーザーインターフェイスを提供します。
  </x-card>
</x-cards>

---

## ヘッダーにカスタム要素を追加する方法

`Header`コンポーネントは拡張可能に設計されています。`addons`プロップを利用することで、カスタムボタン、検索バー、またはその他のReactコンポーネントをヘッダーに直接追加できます。

### 目標

メインアプリケーションのヘッダーに、カスタムの「チャット」ボタン、検索入力、および追加のアクションアイコンを追加します。

### 前提条件

- `@arcblock/ux`ライブラリがインストールされた、機能するReactアプリケーション。
- 既存の`<Header>`コンポーネントインスタンス。詳細については、[Headerコンポーネントのドキュメント](./components-layout-header.md)を参照してください。

### 手順

`addons`プロップはレンダー関数を受け入れます。この関数は、最初の引数としてデフォルトのアドオン（セッションマネージャーなど）を受け取り、カスタム要素をデフォルトの要素に対してどこに配置するかを決定できます。

1.  **`addons`レンダープロップを定義する**

    `Header`コンポーネントで、`addons`プロップに関数を渡します。この関数は、レンダリングしたいJSXを返す必要があります。

    ```jsx
    <Header
      meta={meta}
      addons={(defaultAddons, { navigation }) => {
        // ここにカスタムコンポーネントを配置します
        return (
          <>
            {/* カスタムコンポーネントをレンダリングします */}
            {defaultAddons}
          </>
        );
      }}
    />
    ```

2.  **カスタムコンポーネントを追加する**

    レンダー関数内で、必要なコンポーネントを追加できます。この例では、標準のMaterial-UI `Button`、アイコン用のいくつかの`AddonButton`コンポーネント、および`Divider`を追加します。

3.  **デフォルトのアドオンと組み合わせる**

    関数に渡される`defaultAddons`をレンダリングするのが標準的な方法です。これにより、ロケールスイッチャーやセッションマネージャーなどの重要な要素が引き続き表示されるようになります。カスタムコンポーネントは`defaultAddons`の前または後に配置できます。

### 完全な例

これは、ヘッダーにいくつかのカスタム要素を追加する方法を示す完全な例です。これには、ナビゲーション、ボタン、アイコンが含まれ、デフォルトのセッションマネージャーと一緒にレンダリングされます。

```javascript ヘッダーにカスタム要素を追加する方法 icon=logos:react
import { Box, Divider, Button } from '@mui/material';
import { SessionContext } from '@arcblock/did-connect-react/lib/Session';
import { AddonButton } from '@arcblock/ux/lib/Header/addon-button';
import NavMenu from '@arcblock/ux/lib/NavMenu';
import SessionManager from '@arcblock/ux/lib/SessionManager';
import Header from '@arcblock/ux/lib/Header';
import { Icon } from '@iconify/react';

// デモンストレーション用のモックデータ
const mockBlockletMeta = {
  title: 'My App',
  description: 'A great application',
  logoUrl: 'https://www.arcblock.io/image-bin/uploads/eb1cf5d60cd85c42362920c49e3768cb.svg'
};

const mockSessionContextValue = {
  session: {
    user: {
      fullName: 'Demo User',
      did: 'z1ex...',
      role: 'admin',
    },
    // ... その他のセッションプロパティ
  },
};

export default function CustomHeaderGuide() {
  const meta = {
    ...mockBlockletMeta,
    enableConnect: true,
    enableLocale: true,
  };

  return (
    <SessionContext.Provider value={mockSessionContextValue}>
      <Header
        meta={meta}
        homeLink="https://www.arcblock.io"
        addons={(defaultAddons, { navigation }) => (
          <>
            {/* 1. カスタムナビゲーションを追加 */}
            {navigation.navItems?.length > 0 && (
              <NavMenu
                activeId={navigation.activeId}
                items={navigation.navItems}
                className="header-nav"
              />
            )}
            {/* 2. カスタムボタンを追加 */}
            <Button variant="contained" color="primary" size="small">
              Button
            </Button>
            {/* 3. カスタムアイコンボタンを追加 */}
            <AddonButton icon={<Icon icon="tabler:message-circle" />}>Chat</AddonButton>
            <AddonButton icon={<Icon icon="tabler:bell" />} />
            <Divider orientation="vertical" flexItem sx={{ height: 12, alignSelf: 'center' }} />
            {/* 4. デフォルトのアドオンをレンダリング（セッションマネージャーを含む） */}
            {defaultAddons}
          </>
        )}
      />
    </SessionContext.Provider>
  );
}
```

---

## オプションのコンポーネント依存関係を処理する方法

多くのブロックレットは、特定の機能を提供するために他のコンポーネントに依存しています。`ComponentInstaller`を使用すると、オプションのコンポーネントに依存する機能を構築でき、それらがインストールされていない場合、管理者がそれらをインストールするためのユーザーフレンドリーな方法を提供します。

### 目標

別のブロックレットコンポーネントのインストールを必要とする機能を保護します。コンポーネントが見つからない場合、管理者にはそれをインストールするためのインターフェイスが表示されますが、他のユーザーには何も表示されません。

### 前提条件

- アプリケーション内の、別のコンポーネントに依存する機能（例：「メディアキット」ブロックレットを必要とする「メディアマネージャー」）。
- コンポーネント依存関係のDID。
- ユーザーロール（`owner`、`admin`）に関する知識。

### 手順

1.  **`ComponentInstaller`をインポートする**

    まず、ライブラリからコンポーネントをインポートします。

    ```javascript
    import ComponentInstaller from '@arcblock/ux/lib/ComponentInstaller';
    ```

2.  **機能をラップする**

    依存関係を持つコンポーネントまたは機能を`ComponentInstaller`でラップします。

3.  **コンポーネントのDIDを提供する**

    必要なコンポーネントのDIDを`did`プロップに渡します。複数の依存関係がある場合は、単一の文字列または文字列の配列にすることができます。

4.  **非管理者向けの動作を設定する**

    `noPermissionMute`プロップを使用します。`true`に設定すると、コンポーネントをインストールする権限を持たないユーザー（つまり非管理者）にはインストールプロンプトが表示されません。代わりに表示する`fallback`コンポーネントを提供できます。

### 完全な例

このシナリオでは、DID `z8ia...`を持つコンポーネントがインストールされている場合にのみレンダリングされるべき`MyFeatureButton`があります。

```javascript オプションのコンポーネント依存関係を処理する方法 icon=logos:react
import ComponentInstaller from '@arcblock/ux/lib/ComponentInstaller';
import { Button } from '@mui/material';

const REQUIRED_COMPONENT_DID = 'z8ia1mAXo8ZE7ytGF36L5uBf9kD2kenhqFGp9'; // DIDの例

// これが依存関係を必要とするコンポーネントです
function MyFeatureButton() {
  return <Button variant="contained">Use Awesome Feature</Button>;
}

export default function OptionalComponentGuide() {
  return (
    <ComponentInstaller
      // チェックするコンポーネントのDID
      did={REQUIRED_COMPONENT_DID}
      // インストーラーUIの表示を許可されるロールのリスト
      roles={['owner', 'admin']}
      // trueの場合、非管理者はインストーラーUIを表示しません
      noPermissionMute
      // オプション：コンポーネントがインストールされていない場合に非管理者に表示するもの
      fallback={<div>This feature is not available.</div>}
      // インストール成功後に実行されるコールバック
      onInstalled={() => console.log('Component was installed successfully!')}
    >
      {/* この子はコンポーネントがすでにインストールされている場合にのみレンダリングされます */}
      <MyFeatureButton />
    </ComponentInstaller>
  );
}
```

管理者がこのページにアクセスし、コンポーネントがインストールされていない場合、それをインストールするためのポップアップが表示されます。一般ユーザーにはフォールバックメッセージが表示されます。インストールされると、すべてのユーザーに`MyFeatureButton`が表示されます。

---

## リアルタイムのユーザー通知を実装する方法

Blocklet ServerからのWebSocketイベントをリッスンすることで、ユーザーにリアルタイムのフィードバックと通知を提供できます。`NotificationAddon`コンポーネントは、ヘッダーに未読通知数を表示するためのすぐに使えるソリューションです。

### 目標

アプリケーションのヘッダーに通知ベルアイコンを追加し、未読通知数をバッジで表示してリアルタイムで更新します。

### 前提条件

- ブロックレットは、通知サービスをサポートするBlocklet Serverバージョン（`1.16.42`以降）で実行されている必要があります。
- 通知アイコンが配置される`<Header>`コンポーネント。
- ユーザー情報を提供するセッションコンテキスト。

### 主要な概念

- **WebSocketイベント**: Blocklet Serverは、通知が作成または読み取られたときにイベントをブロードキャストします。
- **`useListenWsClient`**: 特定のチャネル（例：「user」）のWebSocketクライアントインスタンスを取得するためのフック。
- **イベントの命名**: イベントはユーザーとブロックレットにスコープされます。新しい通知の形式は`${blocklet.did}/${user.did}/notification:blocklet:create`です。

### 手順

`NotificationAddon`コンポーネントは、WebSocketイベントをリッスンし、未読数を表示するために必要なすべてのロジックをカプセル化します。

1.  **`NotificationAddon`をインポートする**

    ```javascript
    import NotificationAddon from '@arcblock/ux/lib/common/notification-addon';
    ```

2.  **ヘッダーの`addons`に追加する**

    これを使用する最も簡単な方法は、`Header`の`addons`レンダープロップ内に追加することです。

3.  **セッションオブジェクトを渡す**

    `NotificationAddon`コンポーネントは、現在のユーザーを識別し、未読数の状態を管理するために`session`オブジェクトを必要とします。

### 完全な例

この例は、`NotificationAddon`を`Header`に統合する方法を示しています。これは自動的にWebSocketに接続し、イベントをリッスンし、バッジ数を更新します。

```javascript リアルタイムのユーザー通知を実装する方法 icon=logos:react
import { SessionContext } from '@arcblock/did-connect-react/lib/Session';
import Header from '@arcblock/ux/lib/Header';
import NotificationAddon from '@arcblock/ux/lib/common/notification-addon';

// デモンストレーション用のモックデータ
const mockBlockletMeta = {
  title: 'My App',
  description: 'A great application',
  logoUrl: 'https://www.arcblock.io/image-bin/uploads/eb1cf5d60cd85c42362920c49e3768cb.svg'
};

const mockSessionContextValue = {
  session: {
    user: {
      fullName: 'Demo User',
      did: 'z1ex...',
      role: 'admin',
    },
    unReadCount: 3, // 初期の未読数
    setUnReadCount: () => {}, // 状態セッター関数
    // ... その他のセッションプロパティ
  },
};

export default function NotificationGuide() {
  const meta = { ...mockBlockletMeta, enableConnect: true };

  return (
    <SessionContext.Provider value={mockSessionContextValue}>
      <Header
        meta={meta}
        addons={(defaultAddons) => (
          <>
            {/* NotificationAddonがリアルタイムの更新を処理します */}
            <NotificationAddon session={mockSessionContextValue.session} />
            {defaultAddons}
          </>
        )}
      />
    </SessionContext.Provider>
  );
}
```

ログインしているユーザーの新しい通知が作成されると、ベルアイコンのバッジ数が自動的に増加します。アイコンをクリックすると、ユーザーは通知ページに移動します。

---

## BlockletStudioでリソースを公開する方法

`BlockletStudio`コンポーネントは、リソースとコンポーネントを公開するための、完全で埋め込み可能なUIを提供します。ユーザー接続、リソース選択、およびリリースプロセスを処理し、複雑なワークフローを単一のコンポーネントに簡素化します。

### 目標

ダイアログを開くボタンを追加し、ユーザーがブロックレットからファイルと依存コンポーネントを選択して公開できるようにします。

### 前提条件

- 利用可能なリソースのリストを返す、ブロックレット内のAPIエンドポイント。
- 公開UIを提供するコンポーネント（「スタジオ」コンポーネント）のDID。

### 手順

1.  **`BlockletStudio`をインポートして状態を管理する**

    コンポーネントの状態を使用して、スタジオダイアログの可視性を管理する必要があります。

    ```javascript
    import { useState } from 'react';
    import { BlockletStudio } from '@arcblock/ux/lib/BlockletStudio';
    import { Button, CircularProgress } from '@mui/material';
    ```

2.  **コンポーネントをレンダリングする**

    `<BlockletStudio />`コンポーネントをアプリに配置し、`open`と`setOpen`プロップでその可視性を制御します。

3.  **必須のプロップを設定する**

    - `componentDid`: 公開サービスを提供するスタジオブロックレットのDID。
    - `title`、`description`: 公開されるアイテムのメタデータ。
    - `resourcesParams`: リソース取得APIにクエリパラメータとして渡されるオブジェクト。
    - `components`: 事前に選択または必須とされるコンポーネントの配列。
    - `resources`: 事前に選択するリソースを指定するオブジェクト。

4.  **ライフサイクルイベントを処理する**

    `onOpened`、`onUploaded`、および`onReleased`コールバックを使用して、ローディングスピナーを非表示にしたり、成功メッセージを表示したりするなど、公開ライフサイクル内のイベントに応答します。

### 完全な例

この例は、クリックすると`BlockletStudio`ダイアログを開くボタンを示しています。また、より良いユーザーエクスペリエンスのためにローディング状態を管理する方法も示しています。

```javascript BlockletStudioでリソースを公開する方法 icon=logos:react
import { useState } from 'react';
import { Button, CircularProgress } from '@mui/material';
import { BlockletStudio } from '@arcblock/ux/lib/BlockletStudio';

// スタジオ/公開UIを提供するブロックレットのDID
const AI_STUDIO_COMPONENT_DID = 'z8ia1mAXo8ZE7ytGF36L5uBf9kD2kenhqFGp9'; // DIDの例

export default function PublisherGuide() {
  const [isStudioOpen, setStudioOpen] = useState(false);
  const [isOpening, setOpening] = useState(false);

  const handleShowDialog = () => {
    setOpening(true);
    setStudioOpen(true);
  };

  return (
    <>
      <Button
        variant="contained"
        onClick={handleShowDialog}
        disabled={isOpening}
        startIcon={isOpening ? <CircularProgress size={16} /> : null}>
        Publish to Studio
      </Button>

      <BlockletStudio
        // ダイアログの可視性を制御します
        open={isStudioOpen}
        setOpen={setStudioOpen}
        // 公開されるアイテムの基本情報
        title="My Demo Project"
        description="This is a project published from my blocklet."
        // スタジオサービスコンポーネントのDID
        componentDid={AI_STUDIO_COMPONENT_DID}
        // ブロックレットのリソースAPIに送信されるパラメータ
        resourcesParams={{ projectId: 'test-project-123' }}
        // 事前に選択されたコンポーネント
        components={[
          { did: 'z8ia3xzq2tMq8CRHfaXj1BTYJyYnEcHbqP8cJ', included: true, required: true },
          { did: 'z2qZyjnsRffFtn2PDnDwDHTRbAu53RpKqDtFZ', included: true, required: false },
        ]}
        // 事前に選択されたリソース
        resources={{
          // キーはリソースプロバイダーブロックレットのDIDです
          z8iZpog7mcgcgBZzTiXJCWESvmnRrQmnd3XBB: [
            'template-448698592710885376',
            'example-448698592710885376',
          ],
        }}
        // イベントハンドラ
        onOpened={() => setOpening(false)}
        onUploaded={() => alert('Upload complete!')}
        onReleased={() => {
          alert('Successfully released!');
          setStudioOpen(false);
        }}
      />
    </>
  );
}
```