Principais recursos do Live Share

Captura de ecrã a mostrar um exemplo de utilizadores a jogar póquer ágil numa reunião do Teams, que mostra a capacidade de Partilha em direto.

O SDK do Live Share pode ser adicionado aos contextos sidePanel e meetingStage da extensão da sua reunião com o mínimo de esforço. Também pode utilizar o SDK em contextos de chat e de canal content , como separadores configuráveis, separadores estáticos e visão de palco colaborativa.

Observação

Os contextos de Partilha content em Direto em chat e canais só são suportados em clientes Web e de ambiente de trabalho do Teams.

Este artigo se concentra em como integrar o SDK do Live Share ao seu aplicativo e aos principais recursos do SDK.

Pré-requisitos

Instalar o SDK do JavaScript

O SDK live share é um pacote JavaScript publicado no npm e pode transferir através de npm ou yarn. Também tem de instalar dependências de peering do Live Share, que incluem fluid-framework e @fluidframework/azure-client. Se estiver a utilizar o Live Share na sua aplicação de separador, também tem de instalar @microsoft/teams-js a versão 2.23.0 ou posterior. Se quiser utilizar a classe para o TestLiveShareHost desenvolvimento do browser local, tem de instalar @fluidframework/test-client-utils e start-server-and-test empacotar no seu devDependencies.

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

Registrar permissões de RSC

Para ativar o SDK live share para a sua extensão de separador, primeiro tem de adicionar as seguintes permissões RSC ao manifesto da aplicação:

{
  // ...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"
        }
      ]​
    }​
  }​
}

Participar numa sessão

Siga os passos para participar numa sessão associada à reunião, conversa ou canal de um utilizador:

  1. Inicialize LiveShareClient.
  2. Defina as estruturas de dados que você deseja sincronizar. Por exemplo: LiveState ou SharedMap.
  3. Ingressar no contêiner.

Exemplo:

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

Foi tudo o que foi preciso para configurar o contentor e participar na sessão mapeada para a reunião, chat ou canal. Agora, vamos revisar os diferentes tipos de estruturas de dados distribuídos que você pode usar com o SDK do Live Share.

Dica

Certifique-se de que o SDK do Cliente do Teams é inicializado antes de chamar LiveShareHost.create().

Estruturas de dados do Live Share

O SDK live share inclui um conjunto de novas estruturas de dados distribuídos que expandem a classe do DataObject Fluid, fornecendo novos tipos de objetos com monitorização de estado e sem estado. Ao contrário das estruturas de dados fluidas, as classes do LiveDataObject Live Share não escrevem alterações no contentor Fluid, permitindo uma sincronização mais rápida. Além disso, estas turmas foram concebidas de raiz para cenários comuns em reuniões, conversas e canais do Teams. Os cenários comuns incluem sincronizar o conteúdo que o apresentador está a ver, apresentar metadados para cada utilizador na sessão ou apresentar um temporizador de contagem decrescente.

Objeto Dinâmico Descrição
LivePresence Veja quais usuários estão online, defina propriedades personalizadas para cada usuário e transmita as alterações em sua presença.
LiveState Sincronize qualquer valor serializável state JSON.
LiveTimer Sincronize um temporizador de contagem decrescente para um determinado intervalo.
LiveEvent Transmita eventos individuais com quaisquer atributos de dados personalizados no conteúdo.
LiveFollowMode Siga utilizadores específicos, apresente-os a todas as pessoas na sessão e inicie ou termine as suspensões.

Exemplo de LivePresence

Captura de ecrã a mostrar um exemplo de como mostrar as pessoas que estão disponíveis numa sessãoTeams com a presença de Live Share.

A LivePresence classe torna o controlo de quem está na sessão mais fácil do que nunca. Ao chamar os .initialize() métodos ou .updatePresence() , pode atribuir metadados personalizados a esse utilizador, como a imagem de perfil, o identificador do conteúdo que está a ver e muito mais. Ao ouvir presenceChanged eventos, cada cliente recebe o objeto mais recenteLivePresenceUser, ao fechar todas as atualizações de presença num único registo para cada .userId

Seguem-se alguns exemplos nos quais LivePresence pode ser utilizado na sua aplicação:

  • Obter o Microsoft Teams userId, displayNamee roles de cada utilizador na sessão.
  • Apresentar informações personalizadas sobre cada utilizador ligado à sessão, como um URL de imagem de perfil.
  • Sincronizar as coordenadas numa cena 3D onde está localizado o avatar de cada utilizador.
  • Comunicar a posição do cursor de cada utilizador num documento de texto.
  • Publicar a resposta de cada utilizador a uma pergunta de quebra-gelo durante uma atividade de grupo.
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,
});

Os utilizadores que participam numa sessão a partir de um único dispositivo têm um único LivePresenceUser registo que é partilhado com todos os respetivos dispositivos. Para aceder às ligações mais recentes data e state para cada uma das respetivas ligações ativas, pode utilizar a getConnections() API da LivePresenceUser classe . Esta ação devolve uma lista de LivePresenceConnection objetos. Pode ver se uma determinada LivePresenceConnection instância é do dispositivo local com a isLocalConnection propriedade .

Cada LivePresenceUser instância e LivePresenceConnection tem uma state propriedade, que pode ser online, offlineou away. Um presenceChanged evento é emitido quando o estado de um utilizador é alterado. Por exemplo, se um utilizador se desligar da sessão ou fechar a aplicação, o estado será alterado para offline.

Observação

Pode demorar até 20 segundos para que um LivePresenceUser's state atualize para offline depois de um utilizador se desligar da sessão.

Exemplo de LiveState

Captura de ecrã a mostrar um exemplo do estado live share para sincronizar o planeta no sistema solar que é ativamente apresentado à reunião.

A LiveState classe permite sincronizar o estado da aplicação simples para os participantes ligados. LiveState sincroniza um único state valor, permitindo-lhe sincronizar qualquer valor serializável JSON, como string, numberou object.

Seguem-se alguns exemplos nos quais LiveState pode ser utilizado na sua aplicação:

  • Definir o identificador de utilizador do apresentador atual para criar uma funcionalidade assumir o controlo .
  • Sincronizar o caminho de rota atual da sua aplicação para garantir que todos estão na mesma página. Por exemplo, /whiteboard/:whiteboardId.
  • Manter o identificador de conteúdo que o apresentador atual está a ver. Por exemplo, um num taskId quadro de tarefas.
  • Sincronizar o passo atual numa atividade de grupo de várias voltas. Por exemplo, a fase de adivinhação durante o jogo do Poker Ágil.
  • Manter uma posição de deslocamento sincronizada para uma funcionalidade seguir-me .

Observação

Ao contrário SharedMapde , o state valor em LiveState é reposto depois de todos os utilizadores se desligarem de uma sessão.

Exemplo:

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");

Exemplo de LiveEvent

Captura de ecrã a mostrar um exemplo de cliente do Teams a apresentar uma notificação quando há uma alteração no evento.

LiveEvent é uma excelente forma de enviar eventos simples para outros clientes ligados que só são necessários no momento da entrega. É útil para cenários como enviar notificações de sessão ou implementar reações personalizadas.

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);

Exemplo de LiveTimer

Captura de ecrã a mostrar um exemplo de um temporizador de contagem de inatividade com 9 segundos restantes.

LiveTimer fornece um temporizador de contagem decrescente simples que é sincronizado para todos os participantes ligados. É útil para cenários que têm um limite de tempo, como um temporizador de meditação de grupo ou um temporizador redondo para um jogo. Também pode utilizá-lo para agendar tarefas para todas as pessoas na sessão, como apresentar um pedido de lembrete.

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();

Exemplo de LiveFollowMode

Imagem a mostrar três clientes com três vistas separadas: um apresentador, um utilizador que segue o apresentador e um utilizador com a sua própria vista privada com a opção de voltar a sincronizar com o apresentador.

A LiveFollowMode classe combina LivePresence e LiveState numa única classe, permitindo-lhe implementar facilmente os modos de seguidor e apresentador na sua aplicação. Isto permite-lhe implementar padrões familiares a partir de aplicações colaborativas populares, como o PowerPoint Live, Excel Live e Whiteboard. Ao contrário da partilha de ecrã, LiveFollowMode permite-lhe compor conteúdos com elevada qualidade, acessibilidade melhorada e desempenho melhorado. Os utilizadores podem alternar facilmente entre as suas vistas privadas e seguir outros utilizadores.

Pode utilizar a startPresenting() função para assumir o controlo da aplicação para todos os outros utilizadores na sessão. Em alternativa, pode permitir que os utilizadores selecionem individualmente utilizadores específicos que pretendam seguir com a followUser() função . Em ambos os cenários, os utilizadores podem introduzir temporariamente uma vista privada com a beginSuspension() função ou sincronizar novamente com o apresentador com a endSuspension() função . Entretanto, a update() função permite que o utilizador local informe outros clientes na sessão do seu pessoal stateValue. Semelhante a LivePresence, pode ouvir as alterações efetuadas a cada utilizador através de stateValue um presenceChanged serviço de escuta de eventos.

LiveFollowMode também expõe um state objeto, que atualiza dinamicamente consoante o utilizador local que o utilizador local está a seguir. Por exemplo, se o utilizador local não estiver a seguir ninguém, a state.value propriedade corresponde à transmissão mais recente stateValue do utilizador local através update()de . No entanto, se o utilizador local estiver a seguir um apresentador, a state.value propriedade corresponde à mais recente stateValuedo utilizador a apresentar. Semelhante a LiveState, pode ouvir as alterações ao state valor através de um stateChanged serviço de escuta de eventos. Para obter mais informações sobre o state objeto, veja IFollowModeState interface reference (Referência da interface IFollowModeState).

Seguem-se alguns exemplos nos quais pode utilizar LiveFollowMode na sua aplicação:

  • Sincronize as posições da câmara numa cena 3D para cocriar durante uma revisão da estrutura.
  • Atualize o slideId para abrir num carrossel para apresentações e debates produtivos.
  • Difunda o path para abrir no router da sua aplicação.

Exemplo:

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;
    }
  }
}

Em meetingStage contextos, os seus utilizadores estão a colaborar e a apresentar de forma síncrona para facilitar debates mais produtivos. Quando um utilizador apresenta conteúdo à fase da reunião, deve ligar para a startPresenting() API do apresentador inicial. Em content contextos como a visão de fase de colaboração, o conteúdo é mais frequentemente consumido de forma assíncrona. Neste caso, é melhor permitir que os utilizadores optem pela colaboração em tempo real, como através de um botão "Seguir". Ao utilizar a teamsJs.app.getContext() API no SDK JavaScript do Teams, pode ajustar facilmente a sua funcionalidade em conformidade.

Exemplo:

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

Verificação de funções para estruturas de dados dinâmicos

As reuniões no Teams incluem chamadas, reuniões com todas as mãos e salas de aula online. Os participantes da reunião podem abranger organizações, ter privilégios diferentes ou ter objetivos diferentes. Por conseguinte, é importante respeitar os privilégios de diferentes funções de utilizador durante as reuniões. Os objetos dinâmicos foram concebidos para suportar a verificação de funções, permitindo-lhe definir as funções que têm permissão para enviar mensagens para cada objeto dinâmico individual. Por exemplo, selecionou a opção que permite que apenas os apresentadores e organizadores da reunião controlem a reprodução de vídeo. No entanto, os convidados e participantes ainda podem pedir os próximos vídeos para ver.

Observação

Ao aceder ao Live Share a partir de um content chat ou contexto de canal, todos os utilizadores terão as Organizer funções e Presenter .

No exemplo seguinte, em que apenas os apresentadores e organizadores podem assumir o controlo, LiveState é utilizado para sincronizar o utilizador que é o apresentador ativo:

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);
  }
}

Ouça seus clientes para entender seus cenários antes de implementar a verificação de função em seu aplicativo, principalmente para a função de Organizador. Não há garantias de que um organizador da reunião esteja presente na reunião. Regra geral, todos os utilizadores são Organizador ou Apresentador ao colaborar numa organização. Se um usuário for um Participante, geralmente é uma decisão intencional do organizador.

Em alguns casos, um utilizador pode ter várias funções. Por exemplo, um Organizador também é um Apresentador. Além disso, os participantes da reunião externos ao inquilino que aloja a reunião têm a função convidado , mas também podem ter privilégios de Apresentador . Isto proporciona mais flexibilidade na forma como utiliza a verificação de funções na sua aplicação.

Observação

O SDK live share não é suportado para utilizadores convidados em reuniões de canal.

Estruturas de dados distribuídos do Fluid

O SDK do Live Share dá suporte a qualquer estrutura de dados distribuída incluída no Fluid Framework. Estas funcionalidades servem como um conjunto de primitivos que pode utilizar para criar cenários de colaboração robustos, como atualizações em tempo real de uma lista de tarefas ou cocriação de texto num HTML <textarea>.

Ao contrário das LiveDataObject classes mencionadas neste artigo, as estruturas de dados fluidas não são repostas depois de a aplicação ser fechada. Isto é ideal para cenários como a reunião sidePanel e content os contextos, em que os utilizadores fecham e reabrem frequentemente a sua aplicação.

O Fluid Framework suporta oficialmente os seguintes tipos de estruturas de dados distribuídas:

Objeto Compartilhado Descrição
SharedMap Um repositório de chave-valor distribuído. Defina qualquer objeto serializável JSON de uma determinada chave para sincronizar esse objeto para todos na sessão.
SharedSegmentSequence Uma estrutura de dados semelhante à lista para armazenar um conjunto de itens (chamados segmentos) em posições definidas.
SharedString Uma sequência de cadeia distribuída otimizada para editar o texto de documentos ou áreas de texto.

Vamos ver como o SharedMap funciona. Neste exemplo, usamos SharedMap para criar um recurso de playlist.

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);
}

Observação

Os objetos DDS do Fluid Framework principal não dão suporte à verificação de função de reunião. Todas as pessoas na reunião podem alterar os dados armazenados através destes objetos.

Teste do browser local

Pode testar localmente o SDK live share no seu browser através da TestLiveShareHost classe sem instalar a sua aplicação no Teams. Isto é útil para testar as principais capacidades de colaboração da sua aplicação num ambiente familiar localhost .

Exemplo:

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

A TestLiveShareHost classe utiliza tinylicious o servidor de teste do Fluid Framework, em vez do nosso serviço de produção do Azure Fluid Relay. Para tal, tem de adicionar alguns scripts ao seu package.json para iniciar o servidor de teste. Também tem de adicionar os @fluidframework/test-client-utils pacotes e start-server-and-test ao devDependencies no no .package.json

{
  "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"
  }
}

Quando inicia a sua aplicação desta forma, o LiveShareClient adiciona #{containerId} ao URL, se não existir. Em seguida, pode copiar e colar o URL numa nova janela do browser para ligar ao mesmo contentor fluido.

Observação

Por predefinição, todos os clientes ligados através TestLiveShareHost do terão presenter funções e organizer .

Exemplos de código

Nome do exemplo Descrição JavaScript TypeScript
Dice Roller Habilite todos os clientes conectados para rolar um dado e exibir o resultado. View Exibir
Agile Poker Permita que todos os clientes conectados joguem o Agile Poker. Exibir NA
Modelo 3D Permitir que todos os clientes ligados vejam um modelo 3D em conjunto. NA View
Timer Ative todos os clientes ligados para ver um temporizador de contagem decrescente. NA View
Avatares de presença Apresentar avatares de presença para todos os clientes ligados. NA View

Próxima etapa

Confira também