Skip to content

Commit

Permalink
Add Live Share to Meeting Signing sample (OfficeDev#500)
Browse files Browse the repository at this point in the history
* Add Live Share to Meeting Signing

* fix typo

Co-authored-by: Wajeed-msft <[email protected]>

Co-authored-by: Wajeed-msft <[email protected]>
  • Loading branch information
eoinobrien and Wajeed-msft authored Oct 18, 2022
1 parent 4cf61d9 commit 3053974
Show file tree
Hide file tree
Showing 35 changed files with 3,642 additions and 1,114 deletions.
111 changes: 111 additions & 0 deletions samples/meetings-share-to-stage-signing/csharp/.tours/live-share.tour
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
{
"$schema": "https://aka.ms/codetour-schema",
"title": "Live Share",
"steps": [
{
"file": "Source/MeetingSigning.Web/ClientApp/src/components/DocumentStage/DocumentStage.tsx",
"description": "In this demo Live Share for Teams is used on our meeting stage. \r\n\r\nIn this file we call hooks to setup both the DOM event handlers and the Live Share event handlers we need to get the document scrolling, cursor location and taking of control set up.\r\n\r\nIn this code tour we will run through how we are handle the syncing of scroll location, our other Live Share features follow a similar structure. Scroll Location syncing is where as a presenter scrolls a document, others in the meeting will scroll to the same point, similar to how a presenter navigating slides in PowerPoint Live. We control who is the presenter by the `takeControl` event.",
"line": 30
},
{
"file": "Source/MeetingSigning.Web/ClientApp/src/components/DocumentStage/DocumentStage.tsx",
"description": "To keep the code easier to understand we have sepearated the code that concerns interations with the DOM, like reading the current scroll location and setting the document scroll position.",
"line": 37
},
{
"file": "Source/MeetingSigning.Web/ClientApp/src/hooks/useScrollOffsetDom.ts",
"description": "Here we listen for scroll events, and call a callback which sets a the scroll position in React state",
"line": 44
},
{
"file": "Source/MeetingSigning.Web/ClientApp/src/hooks/useScrollOffsetDom.ts",
"description": "When setting the scroll location it's important to know the size of the element you are scrolling. If your element (document in our case) is able to change size on different screen sizes the percentage scrolled might not line up correctly, especially if word wraps to different lines on screen size changes.\r\n\r\nThis is even more important if you are trying to highlight a specific word using an exact pixel placement like with cursors.",
"line": 36
},
{
"file": "Source/MeetingSigning.Web/ClientApp/src/hooks/useScrollOffsetDom.ts",
"description": "This hooks also provides a function that can be called to set the scroll location. This is called when a relevant Live Share event is recieved",
"line": 49
},
{
"file": "Source/MeetingSigning.Web/ClientApp/src/components/DocumentStage/DocumentStage.tsx",
"description": "The `useLiveShare` hook creates or joins our Live Share container and passes back it's objects so they can be used in feature specific hooks.",
"line": 41
},
{
"file": "Source/MeetingSigning.Web/ClientApp/src/hooks/useLiveShare.ts",
"description": "Here we [call the Live Share SDK](https://learn.microsoft.com/en-gb/microsoftteams/platform/apps-in-teams-meetings/teams-live-share-capabilities?tabs=typescript#join-a-meeting-session) to join the container. We pass in the schema of the data structures we want our container to have, and if we are able to join the container, it is returned.\r\n\r\nThe next user who joins the meeting will be given the same container.\r\n\r\nOne cavet, is that this container can last for longer than the length of the meeting (but less than 24 hours), so if you want to change the schema will developing you will have to create a new meeting or you will see failures.",
"line": 48
},
{
"file": "Source/MeetingSigning.Web/ClientApp/src/components/DocumentStage/DocumentStage.tsx",
"description": "`useTakeControl`, `useScrollOffsetLiveShare` and `useCursorLocationsLiveShare` is where we set up the Live Share portion of our features. ",
"line": 49,
"selection": {
"start": {
"line": 71,
"character": 5
},
"end": {
"line": 71,
"character": 32
}
}
},
{
"file": "Source/MeetingSigning.Web/ClientApp/src/components/DocumentStage/DocumentStage.tsx",
"description": "We have a simple spinner page that waits for the Live Share container, and events to be started.",
"line": 119
},
{
"file": "Source/MeetingSigning.Web/ClientApp/src/hooks/useScrollOffsetLiveShare.ts",
"description": "We start the scrollOffsetEvent here, allowing only organisers and presenters to send these events",
"line": 127
},
{
"file": "Source/MeetingSigning.Web/ClientApp/src/components/DocumentStage/DocumentStage.tsx",
"description": "Here is how we connect the DOM concerns with the Live Share concerns. Calling `sendScrollOffset` when the DOM `position` changes.",
"line": 109
},
{
"file": "Source/MeetingSigning.Web/ClientApp/src/hooks/useScrollOffsetLiveShare.ts",
"description": "`sendScrollOffset` is called when the document is scrolled, we use optimistic concurrency to save latest scroll position before calling `throttledSendLatestScrollOffset`.",
"line": 81
},
{
"file": "Source/MeetingSigning.Web/ClientApp/src/hooks/useScrollOffsetLiveShare.ts",
"description": "Here we throttle the sending of events to Live Share to once every 50ms. This helps to prevent overloading the Azure Fluid Relay with unnecessary events, and also fellow meeting attendees have to handle less events.",
"line": 77
},
{
"file": "Source/MeetingSigning.Web/ClientApp/src/hooks/useScrollOffsetLiveShare.ts",
"description": "We only want to send event if the user who scrolled is actually in control (that is handled by `useTakeControl`). Here we check that and then send the latest scroll position to other participants.",
"line": 46
},
{
"file": "Source/MeetingSigning.Web/ClientApp/src/hooks/useScrollOffsetLiveShare.ts",
"description": "Irrespective of who is presenting, when the document is scrolled this function will be called. If a viewer's document scrolls because they received a Live Share event of the presenter's scroll, they will end up in this function.\r\n\r\nHere we check to see if the user has scrolled more than a small percentage away from the presenter. If so, we suspended following the presenter to allow this user to scroll the document at their own pace.",
"line": 60
},
{
"file": "Source/MeetingSigning.Web/ClientApp/src/hooks/useScrollOffsetLiveShare.ts",
"description": "If a suspended follower decides to end their suspension they can follow again by calling this function",
"line": 90
},
{
"file": "Source/MeetingSigning.Web/ClientApp/src/hooks/useScrollOffsetLiveShare.ts",
"description": "When a new scroll offset event comes in we handle it here. Where we check that the event is from the person who we have marked as in control, and then if following we call `setPosition` which is a function from `useScrollOffsetDom` that will actually scroll the document",
"line": 104,
"selection": {
"start": {
"line": 117,
"character": 11
},
"end": {
"line": 117,
"character": 22
}
}
}
]
}
58 changes: 58 additions & 0 deletions samples/meetings-share-to-stage-signing/csharp/Docs/live-share.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Live Share tips

The below are some experiences I have had while working with Live Share, sharing here in an informal manner in case it is useful to someone else.

## Getting a user's roles
Getting a user's roles is very helpful if you have an Ephemeral Object that is limited to specific roles and you want to alter the UI based on those roles. For example, you might not want to show a "take control" button to someone who cannot take control.

In the response from `joinContainer` a `services` object is returned. That object contains a `audience` that can be used to get a user's clientIds. One of those clientIds in turn can be used to get the user's roles.

Please note, that currently the user's roles are cached for 5 minutes so if a user's role is changed while the app is sharing, it can take a number of minutes for the roles list to update.

Example:
```typescript
const ROLES_ALLOWED_TO_TAKE_CONTROL = [
UserMeetingRole.organizer,
UserMeetingRole.presenter,
];

...

try {
// The below is how we can get a user's roles in a meeting.
// It is used to change the UI based on the user's roles

// First we get the user's clientId from the audience.
// Note this value will change if the client reconnects to the container,
// and a user can have multiple clientIds, one for each connection.
let currentUserClientId = audience?.getMyself()?.connections[0]?.id;

if (currentUserClientId === undefined) {
return false;
}

// Next we call getClientRoles to get the user's roles based on their clientId.
let currentUserRoles = await EphemeralEvent.getClientRoles(
currentUserClientId,
);

// Then we check if the current user has any of the allowed roles.
return (
ROLES_ALLOWED_TO_TAKE_CONTROL.filter((role) =>
currentUserRoles.includes(role),
).length > 0
);
} catch (error) {
console.log(error);
return false;
}
```

## Inability to know who sent an event
The Live Share SDK is unable to verify who sent an event. Ephemeral Objects are limited by role, and it's impossible for a user to send an event for something that they do not have the correct role for. However, someone with the correct role could send an event that impersonates someone else.

For example, if you are sending an event for cursor movements on the screen and you want to include the user's name, you have to use send the user's name in the data object sent with the event. Therefore, someone could send an event saying their cursor is someone else.

At the moment there is no way to work around this except to limit the event to specific roles.

[The `live-share-canvas` package, sends user's details in the event body for their cursor implementation](https://github.com/microsoft/live-share-sdk/blob/db33713fe47f5c302478387adf8e0c960aab1e2b/packages/live-share-canvas/src/core/LiveCanvas.ts#L349) similar to what is described above.
14 changes: 14 additions & 0 deletions samples/meetings-share-to-stage-signing/csharp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,15 @@ sequenceDiagram
* A viewer can only view the document.
* All signers can view and sign a document.

## Live Share integration
We have added Live Share to this demo to showcase some features of the SDK. The examples we are showing are scroll syncing, taking control and shared cursors.

* Scroll Offset Syncing, when the person in control scrolls their view of the document, everyone following will see their document scroll too. We the controller scrolls we send an EphemeralEvent that viewers listen for, and if the viewer is following the controller their view will update. If the viewer has scrolled to a different part of the document, their following of the controller will be suspended until they follow the controller again.
* Take Control allows for other viewers to become the scroll controller. Only those that have an approved role (in our example Organiser and Presenter) will be able to claim control.
* Shared Cursors shares presenters cursors across every attendees screen. The approach we are using is primitive and uses DOM manipulation to position the cursors. If you plan to implement cursor support in your app using the [live-share-canvas SDK](https://github.com/microsoft/live-share-sdk/tree/main/packages/live-share-canvas) is recommended.

*[We have discussed some more thoughts on Live Share here](docs/live-share.md)*

## Known issues
### Feature Rollout
Currently, this app is not fully supported in the following scenarios:
Expand All @@ -96,6 +105,11 @@ Currently, this app is not fully supported in the following scenarios:
" Unable to get information about the App.
This happens if you are running the application in a normal browser, and not inside Teams. Install the app inside teams to test this application. To upload the app to Teams follow the instructions on https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/deploy-and-publish/apps-upload"

## Code Tours
This repository uses VSCode [Code Tours](https://marketplace.visualstudio.com/items?itemName=vsls-contrib.codetour#:~:text=A%20%22code%20tour%22%20is%20simply%20a%20series%20of,CONTRIBUTING.md%20file%20and%2For%20rely%20on%20help%20from%20others.) to explain _how_ the code works.

The tour files can be found in the `.tours` directory.

## Prerequisites
* Make sure you have an active [Azure subscription](https://azure.microsoft.com/en-us/free/).
* Make sure [Publish to organization's app store](https://docs.microsoft.com/en-us/MicrosoftTeams/manage-apps?toc=%2Fmicrosoftteams%2Fplatform%2Ftoc.json&bc=%2Fmicrosoftteams%2Fplatform%2Fbreadcrumb%2Ftoc.json#publish-a-custom-app-to-your-organizations-app-store) is available in Teams.
Expand Down
Loading

0 comments on commit 3053974

Please sign in to comment.