Updates from: 04/28/2023 01:31:43
Service Microsoft Docs article Related commit history on GitHub Change details
platform Teams Live Share Canvas https://github.com/MicrosoftDocs/msteams-docs/commits/main/msteams-platform/apps-in-teams-meetings/teams-live-share-canvas.md
document.getElementById("line-tip-size").onclick = () => {
You can clear all strokes in the canvas by calling `inkingManager.clear()`. This deletes all strokes from the canvas.
+#### Import and export raw strokes
+
+Live Share Canvas supports importing and exporting raw strokes from `InkingManager`, which enables you to export them to your back-end for later use in a future session.
+
+```javascript
+// Export raw strokes
+const strokes = inkingManager.exportRaw();
+
+// Optionally clear out existing strokes, and import strokes
+inkingManager.clear();
+inkingManager.importRaw(strokes);
+```
+
+#### Export strokes as an SVG
+
+You can export your entire drawing within the `InkingManager` to a scalable vector graphic (SVG). The SVG contents are returned as a string, which you can then store in your server as an .svg file extension.
+
+```javascript
+// Export raw strokes
+const svgText = inkingManager.exportSVG();
+```
+ ### Cursors :::image type="content" source="../assets/images/teams-live-share/canvas-cursors.gif" alt-text="GIF shows an example of users sharing a cursor on a canvas.":::
platform Teams Live Share Capabilities https://github.com/MicrosoftDocs/msteams-docs/commits/main/msteams-platform/apps-in-teams-meetings/teams-live-share-capabilities.md
timer.play();
:::image type="content" source="../assets/images/teams-live-share/live-share-state.png" alt-text="Screenshot shows an example of Live Share state to synchronize what planet in the solar system is actively presented to the meeting.":::
-The `LiveState` class enables synchronizing simple application state for everyone in a meeting. `LiveState` synchronizes two values: a `state` string and a corresponding `data` object, which allows you to build an ephemeral distributed-state machine.
+The `LiveState` class enables synchronizing simple application state for everyone in a meeting. `LiveState` synchronizes a single `state` value, allowing you to synchronize any JSON serializable value, such as a `string`, `number`, or `object`.
The following are a few examples in which `LiveState` can be used in your application:
The following are a few examples in which `LiveState` can be used in your applic
- Synchronizing the current step in a multi-round group activity. For example, the guessing phase during the Agile Poker game. > [!NOTE]
-> Unlike `SharedMap`, the `state` and `data` values in `LiveState` will be reset after all the users disconnect from a session.
+> Unlike `SharedMap`, the `state` value in `LiveState` will be reset after all the users disconnect from a session.
Example:
const schema = {
const { container } = await liveShare.joinContainer(schema); const { appState } = container.initialObjects;
-// Register listener for changes to state and corresponding custom data
-appState.on("stateChanged", (state, data, local) => {
- if (state === "planet-viewer") {
- const planetName = data?.planetName;
- // Update app to display an image of the selected planet for the selected solar system
- } else {
- // No planet is yet selected
- }
+// Register listener for changes to state
+appState.on("stateChanged", (planetName, local) => {
+ // Update app with newly selected planet
});
-// Set roles who can change state and start listening for changes
-appState.initialize();
+// Set a default value and start listening for changes
+await appState.initialize("Mercury");
function onSelectPlanet(planetName) {
- appState.changeState("planet-viewer", {
- planetName,
- });
+ appState.set(planetName);
} ```
enum PlanetName {
NEPTUNE = "Neptune", }
-// Declare interface for type of custom data for user
-interface ICustomState {
- planetName: PlanetName;
-}
- // Join the Fluid container const host = LiveShareHost.create(); const liveShare = new LiveShareClient(host); const schema = { initialObjects: {
- appState: LiveState<ICustomState>,
+ appState: LiveState<PlanetName>,
}, }; const { container } = await liveShare.joinContainer(schema);
-const appState = container.initialObjects.appState as LiveState<ICustomState>;
+const appState = container.initialObjects.appState as LiveState<PlanetName>;
-// Register listener for changes to state and corresponding custom data
-appState.on("stateChanged", (state: string, data: ICustomState | undefined, local: boolean) => {
- if (state === "planet-viewer") {
- const planetName = data?.planetName;
- // Update app to display an image of the selected planet for the selected solar system
- } else {
- // No planet is yet selected
- }
+// Register listener for changes to state
+appState.on("stateChanged", (planetName: PlanetName, local: boolean) => {
+ // Update app to display an image of the selected planet for the selected solar system
});
-// Set roles who can change state and start listening for changes
-appState.initialize();
+// Set a default value and start listening for changes
+await appState.initialize(PlanetName.MERCURY);
function onSelectPlanet(planetName: PlanetName) {
- appState.changeState("planet-viewer", {
- planetName,
- });
+ appState.set(planetName);
} ```
const schema = {
const { container } = await liveShare.joinContainer(schema); const { appState } = container.initialObjects;
-// Register listener for changes to state and corresponding custom data
-appState.on("stateChanged", (state, data, local) => {
+// 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];
-appState.initialize(allowedRoles);
+await appState.initialize(initialState, allowedRoles);
function onSelectEditMode(documentId) {
- appState.changeState("editing", {
+ appState.set({
documentId, }); } function onSelectPresentMode(documentId) {
- appState.changeState("presenting", {
+ appState.set({
documentId, presentingUserId: "LOCAL_USER_ID", });
const schema = {
const { container } = await liveShare.joinContainer(schema); const appState = container.initialObjects.appState as LiveState<ICustomState>;
-// Register listener for changes to state and corresponding custom data
-appState.on("stateChanged", (state: string, data: ICustomState | undefined, local: boolean) => {
+// Register listener for changes to state
+appState.on("stateChanged", (state: ICustomState, local: boolean) => {
// Update local app state }); // Set roles who can change state and start listening for changes
+const initialState: ICustomState = {
+ documentId: "INITIAL_DOCUMENT_ID",
+};
const allowedRoles: UserMeetingRole[] = [UserMeetingRole.organizer, UserMeetingRole.presenter];
-appState.initialize(allowedRoles);
+await appState.initialize(initialState, allowedRoles);
function onSelectEditMode(documentId: string) {
- appState.changeState("editing", {
+ appState.set({
documentId, }); } function onSelectPresentMode(documentId: string) {
- appState.changeState("presenting", {
+ appState.set({
documentId, presentingUserId: "LOCAL_USER_ID", });
platform Locally With An Ide https://github.com/MicrosoftDocs/msteams-docs/commits/main/msteams-platform/bots/how-to/debug/locally-with-an-ide.md
If you're hosting your bot locally during development, you need to use a tunneli
ngrok http <port> --host-header=localhost:<port> ```
-Use the https endpoint provided by ngrok in your app manifest.
+Use the https endpoint provided by ngrok in your [app manifest](../../../resources/schem).
> [!NOTE] > If you close your command window and restart, a new URL is generated and you need to update your bot endpoint address to use it.
platform Get Teams Context https://github.com/MicrosoftDocs/msteams-docs/commits/main/msteams-platform/bots/how-to/get-teams-context.md
public class MyBot : TeamsActivityHandler
* [SDK reference](/javascript/api/botbuilder/teamsinfo?view=botbuilder-ts-latest#botbuilder-teamsinfo-getmember&preserve-view=true)
-* [Sample code reference](https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/bot-conversation/nodejs/bots/teamsConversationBot.js#L157)
+* [Sample code reference](https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/bot-conversation/nodejs/bots/teamsConversationBot.js#L186)
```typescript export class MyBot extends TeamsActivityHandler {
platform Manifest Schema https://github.com/MicrosoftDocs/msteams-docs/commits/main/msteams-platform/resources/schema/manifest-schema.md
The following is the sample manifest schema:
{ "name": "keyword", "title": "Search keywords",
- "inputType": "text",
+ "inputType": "choiceset",
"description": "Enter the keywords to search for", "value": "Initial value for the parameter", "choices": [
platform Using Teams Client Library https://github.com/MicrosoftDocs/msteams-docs/commits/main/msteams-platform/tabs/how-to/using-teams-client-library.md
For more info, see [Extend Teams apps across Microsoft 365](../../m365-apps/over
### Callbacks converted to promises
+> [!NOTE]
+> The `getTabInstances` API isn't implemented on Teams mobile.
+ Teams APIs that previously took a callback parameter have been updated to return a JavaScript [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) object. These include the following APIs: ```js