Dice Roller 代码教程

在“骰子滚筒”示例应用中,向用户显示一个骰子,其中包含一个用于滚动它的按钮。 滚动骰子时,Live Share SDK 使用 Fluid Framework 跨客户端同步数据,因此每个人都会看到相同的结果。 若要同步数据,请在 app.js 文件中执行以下步骤:

  1. 设置应用程序
  2. 加入 Fluid 容器
  3. 编写会议阶段视图
  4. 将会议 Stageview 连接到 Live Share
  5. 编写侧面板视图
  6. 编写设置视图

DiceRoller 示例

设置应用程序

首先,可以导入所需的模块。 此示例使用 Live Share SDK 中的 LiveState DDSLiveShareClient 。 此示例支持 Teams 会议扩展性,因此必须包含 Microsoft Teams JavaScript 客户端库 (TeamsJS) 。 最后,该示例设计为在本地和 Teams 会议中运行,因此需要包含更多 Fluid Framework 部分以 在本地测试示例

应用程序使用一个架构创建 Fluid 容器,该架构定义一组可用于容器 的初始对象 。 该示例使用 LiveState 存储已滚动的当前骰子值。

Teams 会议应用需要多个视图,例如内容、配置和阶段。 可以创建一个 start() 函数来帮助识别视图。 这有助于呈现和执行所需的任何初始化。 该应用支持在 Web 浏览器中本地运行,也支持从 Teams 会议中运行。 函数 start() 查找 inTeams=true 查询参数以确定它是否在 Teams 中运行。

注意

在 Teams 中运行时,应用程序需要在调用 app.initialize() 任何其他 teams-js 方法之前调用。

除了 inTeams=true 查询参数,还可以使用 view=content|config|stage 查询参数来确定需要呈现的视图。

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

const searchParams = new URL(window.location).searchParams;
const root = document.getElementById("content");

// Define container schema

const containerSchema = {
  initialObjects: { diceState: LiveState },
};

// STARTUP LOGIC

async function start() {
  // Check for page to display
  let view = searchParams.get("view") || "stage";

  // Check if we are running on stage.
  if (!!searchParams.get("inTeams")) {
    // Initialize teams app
    await app.initialize();
  }

  // Load the requested view
  switch (view) {
    case "content":
      renderSidePanel(root);
      break;
    case "config":
      renderSettings(root);
      break;
    case "stage":
    default:
      const { container } = await joinContainer();
      renderStage(container.initialObjects.diceState, root);
      break;
  }
}

start().catch((error) => console.error(error));

加入 Fluid 容器

并非所有应用视图都需要协作。 stage视图始终需要协作功能,content视图可能需要协作功能,config视图不应需要协作功能。 对于需要协作功能的视图,需要加入与当前会议关联的 Fluid 容器。

加入会议的容器非常简单,只需使用 LiveShareHost Teams 客户端 SDK 中的实例初始化 LiveShareClient ,然后调用其 joinContainer() 方法即可。

在本地运行时,可以改为使用 TestLiveShareHost 实例进行初始化LiveShareClient

async function joinContainer() {
  // Are we running in teams? If so, use LiveShareHost, otherwise use TestLiveShareHost
  const host = !!searchParams.get("inTeams")
    ? LiveShareHost.create()
    : TestLiveShareHost.create();
  // Create client
  const client = new LiveShareClient(host);
  // Join container
  return await client.joinContainer(containerSchema, onContainerFirstCreated);
}

在本地测试时, TestLiveShareHost 更新浏览器 URL 以包含创建的测试容器的 ID。 将该链接复制到其他浏览器选项卡会导致 LiveShareClient 加入创建的测试容器。 如果应用程序 URL 的修改干扰了应用程序的操作,则可以使用传递给 LiveShareClientsetLocalTestContainerIdgetLocalTestContainerId 选项自定义用于存储测试容器 ID 的策略。

编写阶段视图

许多 Teams 会议扩展性应用程序旨在将 React 用于其视图框架,但这不是必需的。 例如,此示例使用标准 HTML/DOM 方法来呈现视图。

从静态视图开始

使用不带任何 Fluid 功能的本地数据轻松创建视图,然后通过更改应用的某些关键部分来添加 Fluid。

函数 renderStagestageTemplate 追加到传递的 HTML 元素,并在每次选择“ 滚动 ”按钮时使用随机骰子值创建一个工作骰子滚筒。 在接下来的几个步骤中将使用 diceState

const stageTemplate = document.createElement("template");

stageTemplate["innerHTML"] = `
  <div class="wrapper">
    <div class="dice"></div>
    <button class="roll"> Roll </button>
  </div>
`;
function renderStage(diceState, elem) {
  elem.appendChild(stageTemplate.content.cloneNode(true));
  const rollButton = elem.querySelector(".roll");
  const dice = elem.querySelector(".dice");

  const updateDice = () => {
    // Get a random value between 1 and 6
    const diceValue = Math.floor(Math.random() * 6) + 1;
    // Unicode 0x2680-0x2685 are the sides of a die (⚀⚁⚂⚃⚄⚅).
    dice.textContent = String.fromCodePoint(0x267f + value);
  };
  rollButton.onclick = () => updateDice();
  updateDice(1);
}

将会议 Stageview 连接到 Live Share

修改 LiveState

若要开始在应用程序中使用 Live Share,首先要更改的是当用户选择 rollButton时发生的情况。 按钮更新中存储为 statediceState的数字,而不是直接更新本地状态。 每当使用新的 state调用 .set() 时,该值都会分发到所有客户端。 对 diceState 的任何更改都可能导致 stateChanged 发出事件,并且事件处理程序可以触发视图的更新。

此模式在 Fluid 和 Live Share 分布式数据结构中很常见,因为它使视图能够对本地和远程更改的行为方式相同。

rollButton.onclick = () =>
  diceState.set(Math.floor(Math.random() * 6) + 1);

依赖 Fluid 数据

需要进行的下一个更改是更改 函数,updateDice以便从每次调用时updateDice检索LiveState最新的骰子值。

const updateDice = () => {
  const diceValue = diceState.state;
  dice.textContent = String.fromCodePoint(0x267f + diceValue);
};

处理远程更改

diceState 返回的值只是时间快照。 若要在数据更改时保持最新状态,必须向 diceState 注册事件处理程序,以便每次发送事件时stateChanged调用 updateDice

diceState.on("stateChanged", updateDice);

初始化 LiveState

在开始接收应用程序中的 Live Share 更改之前,必须先对LiveState具有初始值的对象进行调用initialize()。 此初始值不会覆盖其他用户发送的任何现有状态。

初始化 LiveState后,每当进行更改时, stateChanged 前面注册的事件就会开始触发。 但是,若要更新初始值内的 UI,请调用 updateDice()

await diceState.initialize(1);
updateDice();

编写侧面板视图

当用户在会议中打开应用时,会在侧面板中向用户显示侧面板视图,该视图通过选项卡 contentUrl 加载 sidePanel 帧上下文。 侧面板视图的目标是让用户在将应用共享到会议阶段之前选择应用的内容。 对于 Live Share SDK 应用,侧面板视图也可用作应用的配套体验。 从侧面板视图调用 joinContainer() 将连接到 Stageview 连接到的同一 Fluid 容器。 然后,可以使用此容器与 Stageview 通信。 确保与每个人的舞台视图和侧面板视图通信。

示例的侧面板视图提示用户选择要暂存的共享按钮。

const sidePanelTemplate = document.createElement("template");

sidePanelTemplate["innerHTML"] = `
  <style>
    .wrapper { text-align: center }
    .title { font-size: large; font-weight: bolder; }
    .text { font-size: medium; }
  </style>
  <div class="wrapper">
    <p class="title">Lets get started</p>
    <p class="text">Press the share to stage button to share Dice Roller to the meeting stage.</p>
  </div>
`;

function renderSidePanel(elem) {
  elem.appendChild(sidePanelTemplate.content.cloneNode(true));
}

编写设置视图

应用清单中加载configurationUrl的设置视图在用户首次将你的应用添加到 Teams 会议时会显示给用户。 此视图允许开发人员根据用户输入配置固定到会议的选项卡的 contentUrl。 即使不需要用户输入来设置 contentUrl,当前也需要此页。

注意

选项卡settings上下文中不支持 Live Share joinContainer() 的 。

示例的设置视图提示用户选择保存按钮。

const settingsTemplate = document.createElement("template");

settingsTemplate["innerHTML"] = `
  <style>
    .wrapper { text-align: center }
    .title { font-size: large; font-weight: bolder; }
    .text { font-size: medium; }
  </style>
  <div class="wrapper">
    <p class="title">Welcome to Dice Roller!</p>
    <p class="text">Press the save button to continue.</p>
  </div>
`;

function renderSettings(elem) {
  elem.appendChild(settingsTemplate.content.cloneNode(true));

  // Save the configurable tab
  pages.config.registerOnSaveHandler((saveEvent) => {
    pages.config.setConfig({
      websiteUrl: window.location.origin,
      contentUrl: window.location.origin + "?inTeams=1&view=content",
      entityId: "DiceRollerFluidLiveShare",
      suggestedDisplayName: "DiceRollerFluidLiveShare",
    });
    saveEvent.notifySuccess();
  });

  // Enable the Save button in config dialog
  pages.config.setValidityState(true);
}

在本地测试

可以使用 npm run start 在本地测试应用。 有关详细信息,请参阅 快速入门指南

在 Teams 中测试

使用 npm run start 开始在本地运行应用后,可以在 Teams 上测试应用。 若要在不部署的情况下测试应用,请下载并使用 ngrok 隧道服务。

创建 ngrok 隧道以允许 Teams 访问你的应用

  1. 下载 ngrok

  2. 使用 ngrok 创建具有端口 8080 的隧道。 运行以下命令:

     ngrok http 8080 --host-header=localhost
    

    将打开一个新的 ngrok 终端,其中包含一个新 URL,例如 https:...ngrok.io。 新 URL 是指向应用的隧道,需要在应用 manifest.json 中更新。

创建要上传到 Teams 的应用包

  1. 转到计算机上 live-share-sdk\samples\javascript\01.dice-roller 的 Dice Roller 示例文件夹。 还可以从 GitHub 上的 Dice Roller 示例中检查manifest.json

  2. 打开 manifest.json 并更新配置 URL。

    https://<<BASE_URI_DOMAIN>> 替换为 ngrok 中的 http 终结点。

  3. 可以更新以下字段:

    • developer.name 设置为你的姓名。
    • 使用网站更新 developer.websiteUrl
    • 使用隐私策略更新 developer.privacyUrl
    • 使用使用条款更新 developer.termsOfUseUrl
  4. 压缩清单文件夹的内容以创建 manifest.zip。 确保 manifest.zip 仅包含 manifest.json 源文件、 color 图标和 outline 图标。

    1. 在 Windows 上,选择 .\manifest 目录中的所有文件并对其进行压缩。

    注意

    • 不要压缩包含的文件夹。
    • 为 zip 文件提供描述性名称。 例如,DiceRollerLiveShare

    有关清单的详细信息,请访问 Teams 清单文档

将自定义应用上传到会议

  1. 打开 Teams。

  2. 在 Teams 中从日历安排会议。 确保邀请至少一名与会者参加会议。

  3. 加入会议。

  4. 在顶部的会议窗口中,选择“+ 应用”>“管理应用”。

  5. 在“管理应用”窗格中,选择“上传自定义应用”。

    1. 如果看不到“上传自定义应用”选项,请按照 说明 在租户中启用自定义应用。
  6. 从计算机中选择并上传 manifest.zip 文件。

  7. 选择“添加”以将示例应用添加到会议中。

  8. 选择“+ 应用”,在“查找应用”搜索框中键入 "Dice Roller"。

  9. 选择应用以在会议中激活它。

  10. 选择“保存”

    Dice Roller 应用将添加到 Teams 会议面板。

  11. 在侧面板中,选择要暂存的共享图标。 Teams 开始与会议阶段的用户进行实时同步。

    共享到阶段图标

    你现在应该会在会议阶段看到骰子投掷。

    会议阶段图像

受邀加入会议的用户可以在加入会议时在台上查看你的应用。

部署

准备好部署代码后,可以使用 Teams 工具包 或 Teams 开发人员门户 来预配和上传应用的 zip 文件。

注意

在上传或分发应用之前,需要将预配的 appId 添加到 manifest.json

代码示例

示例名称 Description JavaScript
投掷骰子 启用所有连接的客户端以投掷骰子并查看结果。 View

后续步骤

另请参阅