Live Share コア機能

Teams 会議でアジャイル ポーカー ゲームをプレイしているユーザーの例を示すスクリーンショット。ライブ共有機能が示されています。

Live Share SDK は、最小限の労力で会議拡張機能の sidePanelmeetingStage コンテキストに追加できます。 また、構成可能なタブ、静的タブ、コラボレーションステージビューなどのチャットやチャネル content コンテキストで SDK を使用することもできます。

注:

チャットとチャネルの Live Share content コンテキストは、Teams デスクトップクライアントと Web クライアントでのみサポートされます。

この記事では、Live Share SDK をアプリに統合する方法と、SDK の主な機能を中心に説明します。

前提条件

JavaScript SDK をインストールする

Live Share SDK は npm で発行された JavaScript パッケージであり、npm または yarn を使用してダウンロードできます。 また、 fluid-framework@fluidframework/azure-clientを含む Live Share ピアの依存関係もインストールする必要があります。 タブ アプリケーションで Live Share を使用している場合は、バージョン 2.23.0 以降@microsoft/teams-jsインストールする必要もあります。 ローカル ブラウザー開発に TestLiveShareHost クラスを使用する場合は、devDependencies@fluidframework/test-client-utilsパッケージとstart-server-and-test パッケージをインストールする必要があります。

NPM

npm install @microsoft/live-share fluid-framework @fluidframework/azure-client --save
npm install @microsoft/teams-js --save
npm install @fluidframework/test-client-utils start-server-and-test --save-dev

Yarn

yarn add @microsoft/live-share fluid-framework @fluidframework/azure-client
yarn add @microsoft/teams-js
yarn add @fluidframework/test-client-utils -dev

RSC アクセス許可を登録する

タブ拡張機能の Live Share SDK を有効にするには、まず、アプリ マニフェストに次の RSC アクセス許可を追加する必要があります。

{
  // ...rest of your manifest here
  "configurableTabs": [
    {
        "configurationUrl": "<<YOUR_CONFIGURATION_URL>>",
        "canUpdateConfiguration": true,
        "scopes": [
            "groupchat",
            "team"
        ],
        "context": [
            // meeting contexts
            "meetingSidePanel",
            "meetingStage",
            // content contexts
            "privateChatTab",
            "channelTab",
            "meetingChatTab"
        ]
    }
  ],
  "validDomains": [
    "<<BASE_URI_ORIGIN>>"
  ],
  "authorization": {​
    "permissions": {​
      "resourceSpecific": [
        // ...other permissions here​
        {​
          "name": "LiveShareSession.ReadWrite.Chat",​
          "type": "Delegated"
        },
        {​
          "name": "LiveShareSession.ReadWrite.Group",​
          "type": "Delegated"
        },
        {​
          "name": "MeetingStage.Write.Chat",​
          "type": "Delegated"
        },
        {​
          "name": "ChannelMeetingStage.Write.Group",​
          "type": "Delegated"
        }
      ]​
    }​
  }​
}

セッションに参加する

次の手順に従って、ユーザーの会議、チャット、またはチャネルに関連付けられているセッションに参加します。

  1. LiveShareClientを初期化します。
  2. 同期する必要のあるデータ構造を定義します。 たとえば、LiveState および SharedMap が禁止となります。
  3. コンテナーに参加します。

例:

import { LiveShareClient, LiveState } from "@microsoft/live-share";
import { LiveShareHost } from "@microsoft/teams-js";
import { SharedMap } from "fluid-framework";

// Join the Fluid container
const host = LiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: {
    liveState: LiveState,
    sharedMap: SharedMap,
  },
};
const { container } = await liveShare.joinContainer(schema);

// ... ready to start app sync logic

これで、コンテナーを設定し、会議、チャット、またはチャネルにマップされたセッションに参加するのに必要でした。 ここで、Live Share SDK で使用できる分散データ構造の種類を確認しましょう。

ヒント

LiveShareHost.create()を呼び出す前に、Teams クライアント SDK が初期化されていることを確認します。

Live Share データ構造

Live Share SDK には、Fluid の DataObject クラスを拡張する新しい分散データ構造のセットが含まれており、新しい種類のステートフル オブジェクトとステートレス オブジェクトが提供されます。 Fluid データ構造とは異なり、Live Share の LiveDataObject クラスでは Fluid コンテナーに変更が書き込まれず、より高速な同期が可能になります。 さらに、これらのクラスは、Teams 会議、チャット、チャネルでの一般的なシナリオに対してゼロから設計されています。 一般的なシナリオとしては、発表者が表示しているコンテンツの同期、セッション内の各ユーザーのメタデータの表示、カウントダウン タイマーの表示などがあります。

Live オブジェクト 説明
LivePresence どのユーザーがオンラインであるかを確認し、各ユーザーにカスタム プロパティを設定し、そのプレゼンスへの変更をブロードキャストします。
LiveState JSON シリアル化可能な state 値を同期します。
LiveTimer 特定の間隔のカウントダウン タイマーを同期します。
LiveEvent ペイロード内の任意のカスタム データ属性を持つ個々のイベントをブロードキャストします。
LiveFollowMode 特定のユーザーに従い、セッションのすべてのユーザーに提示し、中断を開始または終了します。

LivePresence の例

スクリーンショットは、Live Share プレゼンスを使用して sessionTeams で使用可能なユーザーを示す例を示しています。

LivePresence クラスを使用すると、セッションに参加しているユーザーをこれまで以上に簡単に追跡できます。 .initialize() または .updatePresence() メソッドを呼び出すときに、プロファイル画像、表示しているコンテンツの識別子など、そのユーザーのカスタム メタデータを割り当てることができます。 presenceChangedイベントをリッスンすることで、各クライアントは最新のLivePresenceUser オブジェクトを受け取り、一意のuserIdごとにすべてのプレゼンス更新を 1 つのレコードに折りたたみます。

アプリケーションで LivePresence を使用できる例をいくつか次に示します。

  • セッション内の各ユーザーのMicrosoft Teams userIddisplayName、および roles を取得します。
  • プロファイル画像 URL など、セッションに接続されている各ユーザーに関するカスタム情報を表示する。
  • 各ユーザーのアバターが配置されている 3D シーンで座標を同期する。
  • テキスト ドキュメント内の各ユーザーのカーソル位置を報告します。
  • グループ アクティビティ中にアイス ブレーカーの質問に対する各ユーザーの回答を投稿する。
import {
  LiveShareClient,
  LivePresence,
  PresenceState,
} from "@microsoft/live-share";
import { LiveShareHost } from "@microsoft/teams-js";

// Join the Fluid container
const host = LiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: {
    presence: LivePresence,
  },
};
const { container } = await liveShare.joinContainer(schema);
const presence = container.initialObjects.presence;

// Register listener for changes to each user's presence.
// This should be done before calling `.initialize()`.
presence.on("presenceChanged", (user, local) => {
  console.log("A user presence changed:");
  console.log("- display name:", user.displayName);
  console.log("- state:", user.state);
  console.log("- custom data:", user.data);
  console.log("- change from local client", local);
  console.log("- change impacts local user", user.isLocalUser);
});

// Define the initial custom data for the local user (optional).
const customUserData = {
  picture: "DEFAULT_PROFILE_PICTURE_URL",
  readyToStart: false,
};
// Start receiving incoming presence updates from the session.
// This will also broadcast the user's `customUserData` to others in the session.
await presence.initialize(customUserData);

// Send a presence update, in this case once a user is ready to start an activity.
// If using role verification, this will throw an error if the user doesn't have the required role.
await presence.update({
  ...customUserData,
  readyToStart: true,
});

1 つのデバイスからセッションに参加するユーザーは、すべてのデバイスと共有される 1 つの LivePresenceUser レコードを持っています。 アクティブな各接続の最新のdatastateにアクセスするには、LivePresenceUser クラスの getConnections() API を使用します。 これにより、 LivePresenceConnection オブジェクトの一覧が返されます。 isLocalConnection プロパティを使用して、特定のLivePresenceConnection インスタンスがローカル デバイスから存在するかどうかを確認できます。

LivePresenceUserおよびLivePresenceConnection インスタンスには、onlineoffline、またはawayのいずれかのstate プロパティがあります。 presenceChanged イベントは、ユーザーの状態が変化したときに生成されます。 たとえば、ユーザーがセッションから切断したり、アプリケーションを閉じると、その状態が offlineに変わります。

注:

ユーザーがセッションから切断した後、 LivePresenceUserstateoffline に更新されるまでに最大 20 秒かかることがあります。

LiveState の例

ライブ共有状態の例を示すスクリーンショット。太陽系内の惑星が会議に積極的に提示されているかを同期します。

LiveState クラスを使用すると、接続された参加者の単純なアプリケーション状態を同期できます。 LiveState は 1 つの state 値を同期します。これにより、 stringnumberobjectなどの JSON シリアル化可能な値を同期できます。

アプリケーションで LiveState を使用できる例をいくつか次に示します。

  • 現在の発表者のユーザー識別子を設定して 、テイク コントロール 機能を構築します。
  • すべてのユーザーが同じページに表示されるように、アプリケーションの現在のルート パスを同期する。 たとえば、「 /whiteboard/:whiteboardId 」のように入力します。
  • 現在の発表者が表示しているコンテンツ識別子を維持する。 たとえば、タスク ボード上の taskId
  • 複数ラウンド グループ アクティビティでの現在のステップの同期。 たとえば、アジャイル ポーカー ゲーム中の推測フェーズなどです。
  • フォローミー機能のスクロール位置を同期したままにします。

注:

SharedMapとは異なり、LiveStatestate値は、すべてのユーザーがセッションから切断した後にリセットされます。

例:

import { LiveShareClient, LiveState } from "@microsoft/live-share";
import { LiveShareHost } from "@microsoft/teams-js";

// Join the Fluid container
const host = LiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: { appState: LiveState },
};
const { container } = await liveShare.joinContainer(schema);
const { appState } = container.initialObjects;

// Register listener for changes to the state.
// This should be done before calling `.initialize()`.
appState.on("stateChanged", (planetName, local, clientId) => {
  // Update app with newly selected planet.
  // See which user made the change (optional)
  const clientInfo = await appState.getClientInfo(clientId);
});

// Set a default value and start listening for changes.
// This default value will not override existing for others in the session.
const defaultState = "Mercury";
await appState.initialize(defaultState);

// `.set()` will change the state for everyone in the session.
// If using role verification, this will throw an error if the user doesn't have the required role.
await appState.set("Earth");

LiveEvent の例

イベントに変更がある場合に通知を表示する Teams クライアントの例を示すスクリーンショット。

LiveEvent は、配信時にのみ必要な他の接続済みクライアントに単純なイベントを送信する優れた方法です。 これは、セッション通知の送信やカスタムリアクションの実装などのシナリオに役立ちます。

import { LiveEvent, LiveShareClient } from "@microsoft/live-share";
import { LiveShareHost } from "@microsoft/teams-js";

// Join the Fluid container
const host = LiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: { customReactionEvent: LiveEvent },
};
const { container } = await liveShare.joinContainer(schema);
const { customReactionEvent } = container.initialObjects;

// Register listener to receive events sent through this object.
// This should be done before calling `.initialize()`.
customReactionEvent.on("received", async (kudosReaction, local, clientId) => {
  console.log("Received reaction:", kudosReaction, "from clientId", clientId);
  // See which user made the change (optional)
  const clientInfo = await customReactionEvent.getClientInfo(clientId);
  // Display notification in your UI
});

// Start listening for incoming events
await customReactionEvent.initialize();

// `.send()` will send your event value to everyone in the session.
// If using role verification, this will throw an error if the user doesn't have the required role.
const kudosReaction = {
  emoji: "❤️",
  forUserId: "SOME_OTHER_USER_ID",
};
await customReactionEvent.send(kudosReaction);

LiveTimer の例

残り 9 秒のカウントダウン タイマーの例を示すスクリーンショット。

LiveTimer は、接続されているすべての参加者に同期される単純なカウントダウン タイマーを提供します。 これは、グループ瞑想タイマーやゲームのラウンド タイマーなど、制限時間があるシナリオに役立ちます。 また、アラーム プロンプトの表示など、セッションのすべてのユーザーのタスクをスケジュールすることもできます。

import { LiveShareClient, LiveTimer } from "@microsoft/live-share";
import { LiveShareHost } from "@microsoft/teams-js";

// Join the Fluid container
const host = LiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: { timer: LiveTimer },
};
const { container } = await liveShare.joinContainer(schema);
const { timer } = container.initialObjects;

// Register listeners for timer changes
// This should be done before calling `.initialize()`.

// Register listener for when the timer starts its countdown
timer.on("started", (config, local) => {
  // Update UI to show timer has started
});

// Register listener for when a paused timer has resumed
timer.on("played", (config, local) => {
  // Update UI to show timer has resumed
});

// Register listener for when a playing timer has paused
timer.on("paused", (config, local) => {
  // Update UI to show timer has paused
});

// Register listener for when a playing timer has finished
timer.on("finished", (config) => {
  // Update UI to show timer is finished
});

// Register listener for the timer progressed by 20 milliseconds
timer.on("onTick", (milliRemaining) => {
  // Update UI to show remaining time
});

// Start synchronizing timer events for users in session
await timer.initialize();

// Start a 60 second timer for users in the session.
// If using role verification, this will throw an error if the user doesn't have the required role.
const durationInMilliseconds = 1000 * 60;
await timer.start(durationInMilliseconds);

// Pause the timer for users in session
// If using role verification, this will throw an error if the user doesn't have the required role.
await timer.pause();

// Resume the timer for users in session
// If using role verification, this will throw an error if the user doesn't have the required role.
await timer.play();

LiveFollowMode の例

画像は、発表者、発表者に従うユーザー、発表者に戻るオプションを持つ独自のプライベート ビューを持つユーザーの 3 つの異なるビューを持つ 3 つのクライアントを示しています。

LiveFollowMode クラスは、LivePresenceLiveStateを 1 つのクラスにまとめ、フォロワー モードと発表者モードをアプリケーションに簡単に実装できます。 これにより、PowerPoint Live、Excel Live、Whiteboard などの一般的なコラボレーション アプリの使い慣れたパターンを実装できます。 画面共有とは異なり、 LiveFollowMode を使用すると、高品質でアクセシビリティが向上し、パフォーマンスが向上したコンテンツをレンダリングできます。 ユーザーはプライベート ビューを簡単に切り替えて、他のユーザーに従うことができます。

startPresenting()関数を使用して、セッション内の他のすべてのユーザーのアプリケーションを制御できます。 または、 followUser() 関数を使用して、ユーザーがフォローする特定のユーザーを個別に選択できるようにすることもできます。 どちらのシナリオでも、ユーザーは beginSuspension() 関数を使用してプライベート ビューを一時的に入力するか、 endSuspension() 関数を使用して発表者に同期できます。 一方、 update() 関数を使用すると、ローカル ユーザーはセッション内の他のクライアントに自分の個人 stateValueを通知できます。 LivePresenceと同様に、presenceChanged イベント リスナーを介して各ユーザーのstateValueの変更をリッスンできます。

LiveFollowMode また、ローカル ユーザーがフォローしているユーザーに応じて動的に更新される、 state オブジェクトも公開されます。 たとえば、ローカル ユーザーが誰もフォローしていない場合、state.value プロパティは、update()経由でブロードキャストされたローカル ユーザーの最新のstateValueと一致します。 ただし、ローカル ユーザーが発表者をフォローしている場合、 state.value プロパティは発表者の最新の stateValueと一致します。 LiveStateと同様に、stateChanged イベント リスナーを使用して、state値の変更をリッスンできます。 state オブジェクトの詳細については、「IFollowModeState インターフェイス リファレンス」を参照してください

アプリケーションで LiveFollowMode を使用できる例をいくつか次に示します。

  • デザイン レビュー中に 3D シーン内のカメラの位置をコブロウズに同期します。
  • slideIdを更新してカルーセルで開き、生産性の高いプレゼンテーションやディスカッションを行います。
  • アプリケーションのルーターで開く path をブロードキャストします。

例:

import {
  LiveShareClient,
  LiveFollowMode,
  FollowModeType,
} from "@microsoft/live-share";
import { LiveShareHost } from "@microsoft/teams-js";

// Join the Fluid container
const host = LiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: {
    followMode: LiveFollowMode,
  },
};
const { container } = await liveShare.joinContainer(schema);
const followMode = container.initialObjects.followMode;

// As an example, we will assume there is a button in the application document
const button = document.getElementById("action-button");
// As an example, we will assume there is a div with text showing the follow state
const infoText = document.getElementById("info-text");

// Register listener for changes to the `state` value to use in your app.
// This should be done before calling `.initialize()`.
followMode.on("stateChanged", (state, local, clientId) => {
  console.log("The state changed:");
  console.log("- state value:", state.value);
  console.log("- follow mode type:", state.type);
  console.log("- following user id:", state.followingUserId);
  console.log(
    "- count of other users also following user",
    state.otherUsersCount
  );
  console.log(
    "- state.value references local user's stateValue",
    state.isLocalValue
  );
  // Can optionally get the relevant user's presence object
  const followingUser = followMode.getUserForClient(clientId);
  switch (state.type) {
    case FollowModeType.local: {
      // Update app to reflect that the user isn't following anyone and there is no presenter.
      infoText.innerHTML = "";
      // Show a "Start presenting" button in your app.
      button.innerHTML = "Start presenting";
      button.onclick = followMode.startPresenting;
      // Note: state.isLocalValue will be true.
      break;
    }
    case FollowModeType.activeFollowers: {
      // Update app to reflect that the local user is being followed by other users.
      infoText.innerHTML = `${state.otherUsersCount} users are following you`;
      // Does not mean that the local user is presenting to everyone, so you can still show the "Start presenting" button.
      button.innerHTML = "Present to all";
      button.onclick = followMode.startPresenting;
      // Note: state.isLocalValue will be true.
      break;
    }
    case FollowModeType.activePresenter: {
      // Update app to reflect that the local user is actively presenting to everyone.
      infoText.innerHTML = `You are actively presenting to everyone`;
      // Show a "Stop presenting" button in your app.
      button.innerHTML = "Stop presenting";
      button.onclick = followMode.stopPresenting;
      // Note: state.isLocalValue will be true.
      break;
    }
    case FollowModeType.followPresenter: {
      // The local user is following a remote presenter.
      infoText.innerHTML = `${followingUser?.displayName} is presenting to everyone`;
      // Show a "Take control" button in your app.
      button.innerHTML = "Take control";
      button.onclick = followMode.startPresenting;
      // Note: state.isLocalValue will be false.
      break;
    }
    case FollowModeType.suspendFollowPresenter: {
      // The local user is following a remote presenter but has an active suspension.
      infoText.innerHTML = `${followingUser?.displayName} is presenting to everyone`;
      // Show a "Sync to presenter" button in your app.
      button.innerHTML = "Sync to presenter";
      button.onclick = followMode.endSuspension;
      // Note: state.isLocalValue will be true.
      break;
    }
    case FollowModeType.followUser: {
      // The local user is following a specific remote user.
      infoText.innerHTML = `You are following ${followingUser?.displayName}`;
      // Show a "Stop following" button in your app.
      button.innerHTML = "Stop following";
      button.onclick = followMode.stopFollowing;
      // Note: state.isLocalValue will be false.
      break;
    }
    case FollowModeType.suspendFollowUser: {
      // The local user is following a specific remote user but has an active suspension.
      infoText.innerHTML = `You were following ${followingUser?.displayName}`;
      // Show a "Resume following" button in your app.
      button.innerHTML = "Resume following";
      button.onclick = followMode.endSuspension;
      // Note: state.isLocalValue will be true.
      break;
    }
    default: {
      break;
    }
  }
  const newCameraPosition = state.value;
  // TODO: apply new camera position
});

// Register listener for changes to each user's personal state updates.
// This should be done before calling `.initialize()`.
followMode.on("presenceChanged", (user, local) => {
  console.log("A user presence changed:");
  console.log("- display name:", user.displayName);
  console.log("- state value:", user.data?.stateValue);
  console.log("- user id user is following:", user.data?.followingUserId);
  console.log("- change from local client", local);
  console.log("- change impacts local user", user.isLocalUser);
  // As an example, we will assume there is a button for each user in the session.
  document.getElementById(`follow-user-${user.userId}-button`).onclick = () => {
    followMode.followUser(user.userId);
  };
  // Update 3D scene to reflect this user's camera position (e.g., orb + display name)
  const userCameraPosition = user.data?.stateValue;
});

// Define the initial stateValue for the local user (optional).
const startingCameraPosition = {
  x: 0,
  y: 0,
  z: 0,
};
// Start receiving incoming presence updates from the session.
// This will also broadcast the user's `startingCameraPosition` to others in the session.
await followMode.initialize(startingCameraPosition);

// Example of an event listener for a camera position changed event.
// For something like a camera change event, you should use a debounce function to prevent sending updates too frequently.
// Note: it helps to distinguish changes initiated by the local user (e.g., drag mouse) separately from other change events.
function onCameraPositionChanged(position, isUserAction) {
  // Broadcast change to other users so that they have their latest camera position
  followMode.update(position);
  // If the local user changed the position while following another user, we want to suspend.
  // Note: helps to distinguish changes initiated by the local user (e.g., drag mouse) separately from other change events.
  if (!isUserAction) return;
  switch (state.type) {
    case FollowModeType.followPresenter:
    case FollowModeType.followUser: {
      // This will trigger a "stateChanged" event update for the local user only.
      followMode.beginSuspension();
      break;
    }
    default: {
      // No need to suspend for other types
      break;
    }
  }
}

meetingStageコンテキストでは、ユーザーは共同作業を行い、より生産的なディスカッションを容易にするために同期的に発表しています。 ユーザーが会議ステージにコンテンツを提示する場合は、最初の発表者の startPresenting() API を呼び出す必要があります。 コラボレーション ステージビューのような content コンテキストでは、コンテンツは最も一般的に非同期的に使用されます。 この場合は、[フォロー] ボタンなど、ユーザーがリアルタイムコラボレーションをオプトインできるようにすることが最善です。 Teams JavaScript SDK の teamsJs.app.getContext() API を使用すると、それに応じて簡単に機能を調整できます。

例:

import {
  LiveShareClient,
  LiveFollowMode,
  FollowModeType,
} from "@microsoft/live-share";
import {
  app,
  meeting,
  FrameContexts,
  LiveShareHost,
} from "@microsoft/teams-js";

// Join the Fluid container
const host = LiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: {
    followMode: LiveFollowMode,
  },
};
const { container } = await liveShare.joinContainer(schema);
const followMode = container.initialObjects.followMode;

// Get teamsJs context
const context = await app.getContext();
// Take control if in meetingStage context and local user is initial presenter
if (context.page?.frameContext === FrameContexts.meetingStage) {
  // Take control if in meetingStage context and local user is initial presenter
  meeting.getAppContentStageSharingState((error, state) => {
    const isShareInitiator = state?.isShareInitiator;
    if (!isShareInitiator) return;
    // The user is the initial presenter, so we "take control"
    await followMode.startPresenting();
  });
}
// TODO: rest of app logic

ライブ データ構造のロール検証

Teams の会議には、通話、オールハンズ会議、オンライン 教室が含まれます。 会議の参加者は、組織全体にまたがったり、異なる特権を持ったり、異なる目標を持つ場合があります。 そのため、会議中にさまざまなユーザー ロールの特権を尊重することが重要です。 ライブ オブジェクトは、ロールの検証をサポートするように設計されており、個々のライブ オブジェクトごとにメッセージを送信できるロールを定義できます。 たとえば、会議の発表者と開催者のみがビデオ再生を制御できるオプションを選択しました。 ただし、ゲストと出席者は引き続き次のビデオの視聴を要求できます。

注:

contentチャットまたはチャネル コンテキストから Live Share にアクセスすると、すべてのユーザーにOrganizerロールとPresenterロールが付与されます。

発表者と開催者のみが制御できる次の例では、 LiveState を使用して、アクティブな発表者であるユーザーを同期します。

import {
  LiveShareClient,
  LiveState,
  UserMeetingRole,
} from "@microsoft/live-share";
import { LiveShareHost } from "@microsoft/teams-js";

// Join the Fluid container
const host = LiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: { appState: LiveState },
};
const { container } = await liveShare.joinContainer(schema);
const { appState } = container.initialObjects;

// Register listener for changes to state
appState.on("stateChanged", (state, local) => {
  // Update local app state
});

// Set roles who can change state and start listening for changes
const initialState = {
  documentId: "INITIAL_DOCUMENT_ID",
};
const allowedRoles = [UserMeetingRole.organizer, UserMeetingRole.presenter];
await appState.initialize(initialState, allowedRoles);

async function onSelectEditMode(documentId) {
  try {
    await appState.set({
      documentId,
    });
  } catch (error) {
    console.error(error);
  }
}

async function onSelectPresentMode(documentId) {
  try {
    await appState.set({
      documentId,
      presentingUserId: "LOCAL_USER_ID",
    });
  } catch (error) {
    console.error(error);
  }
}

アプリに役割の検証を実装する前に、特に [開催者] の役割のシナリオを理解するために顧客の声を耳を傾けます。 会議の開催者が会議に出席する保証はありません。 一般的な経験則として、組織内で共同作業を行う場合、すべてのユーザーは オーガナイザー または 発表者 です。 ユーザーが [出席者]の場合、通常は会議の開催者に代わって意図的に決定されます。

場合によっては、ユーザーに複数のロールが含まれる場合があります。 たとえば、 開催者発表者でもあります。 さらに、会議をホストしているテナントの外部にある会議参加者は ゲスト ロールを持ちますが、 発表者 特権を持つ場合もあります。 これにより、アプリケーションでロール検証を使用する方法の柔軟性が高くなります。

注:

Live Share SDK は、チャネル会議の ゲスト ユーザーではサポートされていません。

流動分散データ構造

Live Share SDK は、流動フレームワークに含まれる分散データ構造をサポートします。 これらの機能は、タスク リストのリアルタイム更新や HTML <textarea>内の共同編集テキストなど、堅牢なコラボレーション シナリオを構築するために使用できるプリミティブのセットとして機能します。

この記事で説明する LiveDataObject クラスとは異なり、Fluid データ構造は、アプリケーションが閉じられた後にリセットされません。 これは、ユーザーが頻繁にアプリを閉じて再び開く会議 sidePanelcontent コンテキストなどのシナリオに最適です。

Fluid Framework では、次の種類の分散データ構造が正式にサポートされています。

共有オブジェクト 説明
SharedMap 分散キー値ストア。 任意の JSON シリアル化可能なオブジェクトを指定されたキーに設定し、セッション内のすべてのユーザーにそのオブジェクトを同期させます。
SharedSegmentSequence 設定された位置にある一連の項目 (セグメントと呼ばれる) の集合体を格納するリスト状のデータ構造。
SharedString ドキュメントまたはテキスト領域のテキストを編集するために最適化された分散文字列シーケンス。

SharedMap のしくみを見てみましょう。 この例では、SharedMap を使用してプレイリスト機能を構築しています。

import { LiveShareClient } from "@microsoft/live-share";
import { LiveShareHost } from "@microsoft/teams-js";
import { SharedMap } from "fluid-framework";

// Join the Fluid container
const host = LiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: { playlistMap: SharedMap },
};
const { container } = await liveShare.joinContainer(schema);
const playlistMap = container.initialObjects.playlistMap;

// Register listener for changes to values in the map
playlistMap.on("valueChanged", (changed, local) => {
  const video = playlistMap.get(changed.key);
  // Update UI with added video
});

function onClickAddToPlaylist(video) {
  // Add video to map
  playlistMap.set(video.id, video);
}

注:

コア流動フレームワーク DDS オブジェクトでは、会議の役割の検証はサポートされません。 会議のすべてのユーザーは、これらのオブジェクトを介して格納されているデータを変更できます。

ローカル ブラウザーのテスト

アプリを Teams にインストールせずに、 TestLiveShareHost クラスを使用して、ブラウザーで Live Share SDK をローカルでテストできます。 これは、使い慣れた localhost 環境内でアプリケーションのコア コラボレーション機能をテストする場合に役立ちます。

例:

import {
  LiveShareClient,
  TestLiveShareHost,
  LiveState,
} from "@microsoft/live-share";
import { LiveShareHost } from "@microsoft/teams-js";
import { SharedMap } from "fluid-framework";

/**
 * Detect whether you are in Teams or local environment using your preferred method.
 * Options for this include: environment variables, URL params, Teams FX, etc.
 */
const inTeams = process.env.IN_TEAMS;
// Join the Fluid container
const host = inTeams ? LiveShareHost.create() : TestLiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: {
    liveState: LiveState,
    sharedMap: SharedMap,
  },
};
const { container } = await liveShare.joinContainer(schema);

// ... ready to start app sync logic

TestLiveShareHost クラスは、運用環境の Azure Fluid Relay サービスではなく、Fluid Framework からtinyliciousテスト サーバーを利用します。 これを行うには、テスト サーバーを起動するために、いくつかのスクリプトを package.json に追加する必要があります。 また、@fluidframework/test-client-utilsパッケージとstart-server-and-test パッケージをpackage.jsondevDependenciesに追加する必要もあります。

{
  "scripts": {
    "start": "start-server-and-test start:server 7070 start:client",
    "start:client": "{YOUR START CLIENT COMMAND HERE}",
    "start:server": "npx tinylicious@latest"
  },
  "devDependencies": {
    "@fluidframework/test-client-utils": "^1.3.6",
    "start-server-and-test": "^2.0.0"
  }
}

この方法でアプリケーションを起動すると、 LiveShareClient は URL に #{containerId} を追加します (存在しない場合)。 その後、URL をコピーして新しいブラウザー ウィンドウに貼り付けて、同じ Fluid コンテナーに接続できます。

注:

既定では、 TestLiveShareHost 経由で接続されているすべてのクライアントには、 presenter ロールと organizer ロールが含まれます。

コード サンプル

サンプルの名前 説明 JavaScript TypeScript
Dice Roller 接続されているすべてのクライアントがサイコロをふり、結果を表示できるようにします。 表示 表示
アジャイル ポーカー 接続されているすべてのクライアントが Agile Poker をプレイできるようにします。 表示 該当なし
3D モデル 接続されているすべてのクライアントが 3D モデルを一緒に表示できるようにします。 該当なし 表示
Timer 接続されているすべてのクライアントがカウントダウン タイマーを表示できるようにします。 該当なし 表示
プレゼンス アバター 接続されているすべてのクライアントのプレゼンス アバターを表示します。 該当なし 表示

次の手順

関連項目