Skip to content
This repository has been archived by the owner before Nov 9, 2022. It is now read-only.
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
Cannot retrieve contributors at this time
99 lines (83 sloc) 3.14 KB
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
import { ActionHandler, Guid, User } from '..';
/** A callback that handles when a user joins or leaves the MRE session */
export type UserEntryExitCallback = (user: User) => void;
/** An event type for [[UserFilter.shouldForwardUserEvent]] */
export type UserInteractionType = 'joined' | 'input';
/** A class that has user joined/left callback hooks. Applies to [[Context]], and also [[UserFilter]]s. */
export interface UserEntryExitPoint {
users: User[];
onUserJoined(callback: UserEntryExitCallback): void;
onUserLeft(callback: UserEntryExitCallback): void;
offUserJoined(callback: UserEntryExitCallback): void;
offUserLeft(callback: UserEntryExitCallback): void;
/** Base class for classes that filter out users from MRE awareness */
export abstract class UserFilter implements UserEntryExitPoint {
private joinedCallbacks = new Set<UserEntryExitCallback>();
private leftCallbacks = new Set<UserEntryExitCallback>();
/** The set of joined users, indexed by ID */
protected joinedUsers = new Map<Guid, User>();
/** The set of joined users */
public get users() {
return [...this.joinedUsers.values()];
* Set up the user filter
* @param context An MRE.Context object, or another user filter instance
constructor(protected context: UserEntryExitPoint) {
for (const u of context.users) {
if (this.shouldForwardUserEvent(u, 'joined')) {
this.joinedUsers.set(, u);
this.context.onUserJoined(u => this.onUpstreamUserJoined(u));
this.context.onUserLeft(u => this.onUpstreamUserLeft(u));
/** Register a callback that will be called when a user that passes the filter joins the MRE session */
public onUserJoined(callback: UserEntryExitCallback) {
/** Deregister an [[onUserJoined]] callback */
public offUserJoined(callback: UserEntryExitCallback) {
/** Register a callback that will be called when a user that passes the filter leaves the MRE session */
public onUserLeft(callback: UserEntryExitCallback) {
/** Deregister an [[onUserLeft]] callback */
public offUserLeft(callback: UserEntryExitCallback) {
/** Process an input event only from users that pass the filter */
public filterInput<T = void>(eventHandler: ActionHandler<T>): ActionHandler<T> {
return (user: User, data: T) => {
if (this.shouldForwardUserEvent(user, 'input')) {
eventHandler(user, data);
/** Evaluates whether a user should be accepted by the filter for the given event type */
protected abstract shouldForwardUserEvent(user: User, type: UserInteractionType): boolean;
private onUpstreamUserJoined(user: User) {
if (this.shouldForwardUserEvent(user, 'joined')) {
this.joinedUsers.set(, user);
for (const cb of this.joinedCallbacks) {
private onUpstreamUserLeft(user: User) {
if (this.joinedUsers.has( {
for (const cb of this.leftCallbacks) {