Merge pull request #25 from vector-im/feature/guest-access
Add guest access for rooms
This commit is contained in:
		
				commit
				
					
						4d7e5583eb
					
				
			
		
					 11 changed files with 338 additions and 60 deletions
				
			
		
							
								
								
									
										26
									
								
								src/App.jsx
									
										
									
									
									
								
							
							
						
						
									
										26
									
								
								src/App.jsx
									
										
									
									
									
								
							| 
						 | 
					@ -24,7 +24,7 @@ import {
 | 
				
			||||||
} from "react-router-dom";
 | 
					} from "react-router-dom";
 | 
				
			||||||
import { useConferenceCallManager } from "./ConferenceCallManagerHooks";
 | 
					import { useConferenceCallManager } from "./ConferenceCallManagerHooks";
 | 
				
			||||||
import { Home } from "./Home";
 | 
					import { Home } from "./Home";
 | 
				
			||||||
import { Room } from "./Room";
 | 
					import { Room, RoomAuth } from "./Room";
 | 
				
			||||||
import { GridDemo } from "./GridDemo";
 | 
					import { GridDemo } from "./GridDemo";
 | 
				
			||||||
import { RegisterPage } from "./RegisterPage";
 | 
					import { RegisterPage } from "./RegisterPage";
 | 
				
			||||||
import { LoginPage } from "./LoginPage";
 | 
					import { LoginPage } from "./LoginPage";
 | 
				
			||||||
| 
						 | 
					@ -34,8 +34,15 @@ export default function App() {
 | 
				
			||||||
  const { protocol, host } = window.location;
 | 
					  const { protocol, host } = window.location;
 | 
				
			||||||
  // Assume homeserver is hosted on same domain (proxied in development by vite)
 | 
					  // Assume homeserver is hosted on same domain (proxied in development by vite)
 | 
				
			||||||
  const homeserverUrl = `${protocol}//${host}`;
 | 
					  const homeserverUrl = `${protocol}//${host}`;
 | 
				
			||||||
  const { loading, authenticated, error, manager, login, register } =
 | 
					  const {
 | 
				
			||||||
    useConferenceCallManager(homeserverUrl);
 | 
					    loading,
 | 
				
			||||||
 | 
					    authenticated,
 | 
				
			||||||
 | 
					    error,
 | 
				
			||||||
 | 
					    manager,
 | 
				
			||||||
 | 
					    login,
 | 
				
			||||||
 | 
					    loginAsGuest,
 | 
				
			||||||
 | 
					    register,
 | 
				
			||||||
 | 
					  } = useConferenceCallManager(homeserverUrl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Router>
 | 
					    <Router>
 | 
				
			||||||
| 
						 | 
					@ -55,12 +62,13 @@ export default function App() {
 | 
				
			||||||
            <Route exact path="/register">
 | 
					            <Route exact path="/register">
 | 
				
			||||||
              <RegisterPage onRegister={register} error={error} />
 | 
					              <RegisterPage onRegister={register} error={error} />
 | 
				
			||||||
            </Route>
 | 
					            </Route>
 | 
				
			||||||
            <AuthenticatedRoute
 | 
					            <Route path="/room/:roomId">
 | 
				
			||||||
              authenticated={authenticated}
 | 
					              {authenticated ? (
 | 
				
			||||||
              path="/room/:roomId"
 | 
					                <Room manager={manager} error={error} />
 | 
				
			||||||
            >
 | 
					              ) : (
 | 
				
			||||||
              <Room manager={manager} error={error} />
 | 
					                <RoomAuth error={error} onLoginAsGuest={loginAsGuest} />
 | 
				
			||||||
            </AuthenticatedRoute>
 | 
					              )}
 | 
				
			||||||
 | 
					            </Route>
 | 
				
			||||||
            <Route exact path="/grid">
 | 
					            <Route exact path="/grid">
 | 
				
			||||||
              <GridDemo />
 | 
					              <GridDemo />
 | 
				
			||||||
            </Route>
 | 
					            </Route>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -107,6 +107,36 @@ export class ConferenceCallManager extends EventEmitter {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static async loginAsGuest(homeserverUrl, displayName) {
 | 
				
			||||||
 | 
					    const registrationClient = matrixcs.createClient(homeserverUrl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const { user_id, device_id, access_token } =
 | 
				
			||||||
 | 
					      await registrationClient.registerGuest();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const client = matrixcs.createClient({
 | 
				
			||||||
 | 
					      baseUrl: homeserverUrl,
 | 
				
			||||||
 | 
					      accessToken: access_token,
 | 
				
			||||||
 | 
					      userId: user_id,
 | 
				
			||||||
 | 
					      deviceId: device_id,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await client.setDisplayName(displayName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    client.setGuest(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const manager = new ConferenceCallManager(client);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await client.startClient({
 | 
				
			||||||
 | 
					      // dirty hack to reduce chance of gappy syncs
 | 
				
			||||||
 | 
					      // should be fixed by spotting gaps and backpaginating
 | 
				
			||||||
 | 
					      initialSyncLimit: 50,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await waitForSync(client);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return manager;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static async register(homeserverUrl, username, password) {
 | 
					  static async register(homeserverUrl, username, password) {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const registrationClient = matrixcs.createClient(homeserverUrl);
 | 
					      const registrationClient = matrixcs.createClient(homeserverUrl);
 | 
				
			||||||
| 
						 | 
					@ -250,6 +280,7 @@ export class ConferenceCallManager extends EventEmitter {
 | 
				
			||||||
    this.localParticipant = {
 | 
					    this.localParticipant = {
 | 
				
			||||||
      local: true,
 | 
					      local: true,
 | 
				
			||||||
      userId,
 | 
					      userId,
 | 
				
			||||||
 | 
					      displayName: this.client.getUser(this.client.getUserId()).rawDisplayName,
 | 
				
			||||||
      sessionId: this.sessionId,
 | 
					      sessionId: this.sessionId,
 | 
				
			||||||
      call: null,
 | 
					      call: null,
 | 
				
			||||||
      stream,
 | 
					      stream,
 | 
				
			||||||
| 
						 | 
					@ -478,12 +509,15 @@ export class ConferenceCallManager extends EventEmitter {
 | 
				
			||||||
        "m.room.member",
 | 
					        "m.room.member",
 | 
				
			||||||
        participant.userId
 | 
					        participant.userId
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
      const participantInfo = memberStateEvent.getContent()[CONF_PARTICIPANT];
 | 
					
 | 
				
			||||||
 | 
					      const memberStateContent = memberStateEvent.getContent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (
 | 
					      if (
 | 
				
			||||||
        !participantInfo ||
 | 
					        !memberStateContent ||
 | 
				
			||||||
        typeof participantInfo !== "object" ||
 | 
					        !memberStateContent[CONF_PARTICIPANT] ||
 | 
				
			||||||
        (participantInfo.expiresAt && participantInfo.expiresAt < now)
 | 
					        typeof memberStateContent[CONF_PARTICIPANT] !== "object" ||
 | 
				
			||||||
 | 
					        (memberStateContent[CONF_PARTICIPANT].expiresAt &&
 | 
				
			||||||
 | 
					          memberStateContent[CONF_PARTICIPANT].expiresAt < now)
 | 
				
			||||||
      ) {
 | 
					      ) {
 | 
				
			||||||
        this.emit("debugstate", participant.userId, null, "inactive");
 | 
					        this.emit("debugstate", participant.userId, null, "inactive");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -533,27 +567,21 @@ export class ConferenceCallManager extends EventEmitter {
 | 
				
			||||||
    const audioMuted = remoteFeed ? remoteFeed.isAudioMuted() : false;
 | 
					    const audioMuted = remoteFeed ? remoteFeed.isAudioMuted() : false;
 | 
				
			||||||
    const videoMuted = remoteFeed ? remoteFeed.isVideoMuted() : false;
 | 
					    const videoMuted = remoteFeed ? remoteFeed.isVideoMuted() : false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (remoteFeed) {
 | 
					 | 
				
			||||||
      remoteFeed.on("mute_state_changed", () =>
 | 
					 | 
				
			||||||
        this._onCallFeedMuteStateChanged(participant, remoteFeed)
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
      remoteFeed.setSpeakingThreshold(SPEAKING_THRESHOLD);
 | 
					 | 
				
			||||||
      remoteFeed.measureVolumeActivity(true);
 | 
					 | 
				
			||||||
      remoteFeed.on("speaking", (speaking) => {
 | 
					 | 
				
			||||||
        this._onCallFeedSpeaking(participant, speaking);
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
      remoteFeed.on("volume_changed", (maxVolume) =>
 | 
					 | 
				
			||||||
        this._onCallFeedVolumeChange(participant, maxVolume)
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const userId = call.opponentMember.userId;
 | 
					    const userId = call.opponentMember.userId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const memberStateEvent = this.room.currentState.getStateEvents(
 | 
					    const memberStateEvent = this.room.currentState.getStateEvents(
 | 
				
			||||||
      "m.room.member",
 | 
					      "m.room.member",
 | 
				
			||||||
      userId
 | 
					      userId
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    const { sessionId } = memberStateEvent.getContent()[CONF_PARTICIPANT];
 | 
					
 | 
				
			||||||
 | 
					    const memberStateContent = memberStateEvent.getContent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!memberStateContent || !memberStateContent[CONF_PARTICIPANT]) {
 | 
				
			||||||
 | 
					      call.reject();
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const { sessionId } = memberStateContent[CONF_PARTICIPANT];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Check if the user calling has an existing participant and use this call instead.
 | 
					    // Check if the user calling has an existing participant and use this call instead.
 | 
				
			||||||
    const existingParticipant = this.participants.find(
 | 
					    const existingParticipant = this.participants.find(
 | 
				
			||||||
| 
						 | 
					@ -562,6 +590,8 @@ export class ConferenceCallManager extends EventEmitter {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let participant;
 | 
					    let participant;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    console.log(call.opponentMember);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (existingParticipant) {
 | 
					    if (existingParticipant) {
 | 
				
			||||||
      participant = existingParticipant;
 | 
					      participant = existingParticipant;
 | 
				
			||||||
      // This also fires the hangup event and triggers those side-effects
 | 
					      // This also fires the hangup event and triggers those side-effects
 | 
				
			||||||
| 
						 | 
					@ -577,6 +607,7 @@ export class ConferenceCallManager extends EventEmitter {
 | 
				
			||||||
      participant = {
 | 
					      participant = {
 | 
				
			||||||
        local: false,
 | 
					        local: false,
 | 
				
			||||||
        userId,
 | 
					        userId,
 | 
				
			||||||
 | 
					        displayName: call.opponentMember.rawDisplayName,
 | 
				
			||||||
        sessionId,
 | 
					        sessionId,
 | 
				
			||||||
        call,
 | 
					        call,
 | 
				
			||||||
        stream,
 | 
					        stream,
 | 
				
			||||||
| 
						 | 
					@ -588,6 +619,20 @@ export class ConferenceCallManager extends EventEmitter {
 | 
				
			||||||
      this.participants.push(participant);
 | 
					      this.participants.push(participant);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (remoteFeed) {
 | 
				
			||||||
 | 
					      remoteFeed.on("mute_state_changed", () =>
 | 
				
			||||||
 | 
					        this._onCallFeedMuteStateChanged(participant, remoteFeed)
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					      remoteFeed.setSpeakingThreshold(SPEAKING_THRESHOLD);
 | 
				
			||||||
 | 
					      remoteFeed.measureVolumeActivity(true);
 | 
				
			||||||
 | 
					      remoteFeed.on("speaking", (speaking) => {
 | 
				
			||||||
 | 
					        this._onCallFeedSpeaking(participant, speaking);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      remoteFeed.on("volume_changed", (maxVolume) =>
 | 
				
			||||||
 | 
					        this._onCallFeedVolumeChange(participant, maxVolume)
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    call.on("state", (state) =>
 | 
					    call.on("state", (state) =>
 | 
				
			||||||
      this._onCallStateChanged(participant, call, state)
 | 
					      this._onCallStateChanged(participant, call, state)
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
| 
						 | 
					@ -629,7 +674,13 @@ export class ConferenceCallManager extends EventEmitter {
 | 
				
			||||||
      "m.room.member",
 | 
					      "m.room.member",
 | 
				
			||||||
      member.userId
 | 
					      member.userId
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    const participantInfo = memberStateEvent.getContent()[CONF_PARTICIPANT];
 | 
					    const memberStateContent = memberStateEvent.getContent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!memberStateContent) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const participantInfo = memberStateContent[CONF_PARTICIPANT];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!participantInfo || typeof participantInfo !== "object") {
 | 
					    if (!participantInfo || typeof participantInfo !== "object") {
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
| 
						 | 
					@ -680,6 +731,7 @@ export class ConferenceCallManager extends EventEmitter {
 | 
				
			||||||
      participant = {
 | 
					      participant = {
 | 
				
			||||||
        local: false,
 | 
					        local: false,
 | 
				
			||||||
        userId: member.userId,
 | 
					        userId: member.userId,
 | 
				
			||||||
 | 
					        displayName: member.rawDisplayName,
 | 
				
			||||||
        sessionId,
 | 
					        sessionId,
 | 
				
			||||||
        call,
 | 
					        call,
 | 
				
			||||||
        stream: null,
 | 
					        stream: null,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -95,6 +95,34 @@ export function useConferenceCallManager(homeserverUrl) {
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
  }, []);
 | 
					  }, []);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const loginAsGuest = useCallback(async (displayName) => {
 | 
				
			||||||
 | 
					    setState((prevState) => ({
 | 
				
			||||||
 | 
					      ...prevState,
 | 
				
			||||||
 | 
					      authenticated: false,
 | 
				
			||||||
 | 
					      error: undefined,
 | 
				
			||||||
 | 
					    }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ConferenceCallManager.loginAsGuest(homeserverUrl, displayName)
 | 
				
			||||||
 | 
					      .then((manager) => {
 | 
				
			||||||
 | 
					        setState({
 | 
				
			||||||
 | 
					          manager,
 | 
				
			||||||
 | 
					          loading: false,
 | 
				
			||||||
 | 
					          authenticated: true,
 | 
				
			||||||
 | 
					          error: undefined,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      .catch((err) => {
 | 
				
			||||||
 | 
					        console.error(err);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setState({
 | 
				
			||||||
 | 
					          manager: undefined,
 | 
				
			||||||
 | 
					          loading: false,
 | 
				
			||||||
 | 
					          authenticated: false,
 | 
				
			||||||
 | 
					          error: err,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					  }, []);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const register = useCallback(async (username, password, cb) => {
 | 
					  const register = useCallback(async (username, password, cb) => {
 | 
				
			||||||
    setState((prevState) => ({
 | 
					    setState((prevState) => ({
 | 
				
			||||||
      ...prevState,
 | 
					      ...prevState,
 | 
				
			||||||
| 
						 | 
					@ -135,7 +163,15 @@ export function useConferenceCallManager(homeserverUrl) {
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  }, [manager]);
 | 
					  }, [manager]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return { loading, authenticated, manager, error, login, register };
 | 
					  return {
 | 
				
			||||||
 | 
					    loading,
 | 
				
			||||||
 | 
					    authenticated,
 | 
				
			||||||
 | 
					    manager,
 | 
				
			||||||
 | 
					    error,
 | 
				
			||||||
 | 
					    login,
 | 
				
			||||||
 | 
					    loginAsGuest,
 | 
				
			||||||
 | 
					    register,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function useVideoRoom(manager, roomId, timeout = 5000) {
 | 
					export function useVideoRoom(manager, roomId, timeout = 5000) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										61
									
								
								src/Home.jsx
									
										
									
									
									
								
							
							
						
						
									
										61
									
								
								src/Home.jsx
									
										
									
									
									
								
							| 
						 | 
					@ -28,6 +28,7 @@ const colorHash = new ColorHash({ lightness: 0.3 });
 | 
				
			||||||
export function Home({ manager }) {
 | 
					export function Home({ manager }) {
 | 
				
			||||||
  const history = useHistory();
 | 
					  const history = useHistory();
 | 
				
			||||||
  const roomNameRef = useRef();
 | 
					  const roomNameRef = useRef();
 | 
				
			||||||
 | 
					  const guestAccessRef = useRef();
 | 
				
			||||||
  const [createRoomError, setCreateRoomError] = useState();
 | 
					  const [createRoomError, setCreateRoomError] = useState();
 | 
				
			||||||
  const rooms = useRooms(manager);
 | 
					  const rooms = useRooms(manager);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,16 +37,51 @@ export function Home({ manager }) {
 | 
				
			||||||
      e.preventDefault();
 | 
					      e.preventDefault();
 | 
				
			||||||
      setCreateRoomError(undefined);
 | 
					      setCreateRoomError(undefined);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      manager.client
 | 
					      async function createRoom(name, guestAccess) {
 | 
				
			||||||
        .createRoom({
 | 
					        const { room_id } = await manager.client.createRoom({
 | 
				
			||||||
          visibility: "private",
 | 
					          visibility: "private",
 | 
				
			||||||
          preset: "public_chat",
 | 
					          preset: "public_chat",
 | 
				
			||||||
          name: roomNameRef.current.value,
 | 
					          name,
 | 
				
			||||||
        })
 | 
					          power_level_content_override: guestAccess
 | 
				
			||||||
        .then(({ room_id }) => {
 | 
					            ? {
 | 
				
			||||||
          history.push(`/room/${room_id}`);
 | 
					                invite: 100,
 | 
				
			||||||
        })
 | 
					                kick: 100,
 | 
				
			||||||
        .catch(setCreateRoomError);
 | 
					                ban: 100,
 | 
				
			||||||
 | 
					                redact: 50,
 | 
				
			||||||
 | 
					                state_default: 0,
 | 
				
			||||||
 | 
					                events_default: 0,
 | 
				
			||||||
 | 
					                users_default: 0,
 | 
				
			||||||
 | 
					                events: {
 | 
				
			||||||
 | 
					                  "m.room.power_levels": 100,
 | 
				
			||||||
 | 
					                  "m.room.history_visibility": 100,
 | 
				
			||||||
 | 
					                  "m.room.tombstone": 100,
 | 
				
			||||||
 | 
					                  "m.room.encryption": 100,
 | 
				
			||||||
 | 
					                  "m.room.name": 50,
 | 
				
			||||||
 | 
					                  "m.room.message": 0,
 | 
				
			||||||
 | 
					                  "m.room.encrypted": 50,
 | 
				
			||||||
 | 
					                  "m.sticker": 50,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                users: {
 | 
				
			||||||
 | 
					                  [manager.client.getUserId()]: 100,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            : undefined,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (guestAccess) {
 | 
				
			||||||
 | 
					          await manager.client.setGuestAccess(room_id, {
 | 
				
			||||||
 | 
					            allowJoin: true,
 | 
				
			||||||
 | 
					            allowRead: true,
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        history.push(`/room/${room_id}`);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      createRoom(
 | 
				
			||||||
 | 
					        roomNameRef.current.value,
 | 
				
			||||||
 | 
					        guestAccessRef.current.checked
 | 
				
			||||||
 | 
					      ).catch(setCreateRoomError);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    [manager]
 | 
					    [manager]
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
| 
						 | 
					@ -87,6 +123,15 @@ export function Home({ manager }) {
 | 
				
			||||||
                    ref={roomNameRef}
 | 
					                    ref={roomNameRef}
 | 
				
			||||||
                  />
 | 
					                  />
 | 
				
			||||||
                </FieldRow>
 | 
					                </FieldRow>
 | 
				
			||||||
 | 
					                <FieldRow>
 | 
				
			||||||
 | 
					                  <InputField
 | 
				
			||||||
 | 
					                    id="guestAccess"
 | 
				
			||||||
 | 
					                    name="guestAccess"
 | 
				
			||||||
 | 
					                    label="Allow Guest Access"
 | 
				
			||||||
 | 
					                    type="checkbox"
 | 
				
			||||||
 | 
					                    ref={guestAccessRef}
 | 
				
			||||||
 | 
					                  />
 | 
				
			||||||
 | 
					                </FieldRow>
 | 
				
			||||||
                {createRoomError && (
 | 
					                {createRoomError && (
 | 
				
			||||||
                  <FieldRow>
 | 
					                  <FieldRow>
 | 
				
			||||||
                    <ErrorMessage>{createRoomError.message}</ErrorMessage>
 | 
					                    <ErrorMessage>{createRoomError.message}</ErrorMessage>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
import React, { forwardRef } from "react";
 | 
					import React, { forwardRef } from "react";
 | 
				
			||||||
import classNames from "classnames";
 | 
					import classNames from "classnames";
 | 
				
			||||||
import styles from "./Input.module.css";
 | 
					import styles from "./Input.module.css";
 | 
				
			||||||
 | 
					import { ReactComponent as CheckIcon } from "./icons/Check.svg";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function FieldRow({ children, rightAlign, className, ...rest }) {
 | 
					export function FieldRow({ children, rightAlign, className, ...rest }) {
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
| 
						 | 
					@ -21,11 +22,23 @@ export function Field({ children, className, ...rest }) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const InputField = forwardRef(
 | 
					export const InputField = forwardRef(
 | 
				
			||||||
  ({ id, label, className, ...rest }, ref) => {
 | 
					  ({ id, label, className, type, checked, ...rest }, ref) => {
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <Field>
 | 
					      <Field
 | 
				
			||||||
        <input id={id} {...rest} ref={ref} />
 | 
					        className={classNames(
 | 
				
			||||||
        <label htmlFor={id}>{label}</label>
 | 
					          type === "checkbox" ? styles.checkboxField : styles.inputField,
 | 
				
			||||||
 | 
					          className
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <input id={id} {...rest} ref={ref} type={type} checked={checked} />
 | 
				
			||||||
 | 
					        <label htmlFor={id}>
 | 
				
			||||||
 | 
					          {type === "checkbox" && (
 | 
				
			||||||
 | 
					            <div className={styles.checkbox}>
 | 
				
			||||||
 | 
					              <CheckIcon />
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          )}
 | 
				
			||||||
 | 
					          {label}
 | 
				
			||||||
 | 
					        </label>
 | 
				
			||||||
      </Field>
 | 
					      </Field>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,9 +9,6 @@
 | 
				
			||||||
  min-width: 0;
 | 
					  min-width: 0;
 | 
				
			||||||
  position: relative;
 | 
					  position: relative;
 | 
				
			||||||
  margin: 1em 0;
 | 
					  margin: 1em 0;
 | 
				
			||||||
  border-radius: 4px;
 | 
					 | 
				
			||||||
  transition: border-color .25s;
 | 
					 | 
				
			||||||
  border: 1px solid #394049;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.fieldRow.rightAlign {
 | 
					.fieldRow.rightAlign {
 | 
				
			||||||
| 
						 | 
					@ -30,7 +27,13 @@
 | 
				
			||||||
  margin-right: 0;
 | 
					  margin-right: 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.field input {
 | 
					.inputField {
 | 
				
			||||||
 | 
					  border-radius: 4px;
 | 
				
			||||||
 | 
					  transition: border-color .25s;
 | 
				
			||||||
 | 
					  border: 1px solid #394049;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.inputField input {
 | 
				
			||||||
  font-weight: 400;
 | 
					  font-weight: 400;
 | 
				
			||||||
  font-size: 14px;
 | 
					  font-size: 14px;
 | 
				
			||||||
  border: none;
 | 
					  border: none;
 | 
				
			||||||
| 
						 | 
					@ -42,17 +45,17 @@
 | 
				
			||||||
  min-width: 0;
 | 
					  min-width: 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.field input::placeholder {
 | 
					.inputField input::placeholder {
 | 
				
			||||||
  transition: color 0.25s ease-in 0s;
 | 
					  transition: color 0.25s ease-in 0s;
 | 
				
			||||||
  color: transparent;
 | 
					  color: transparent;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.field input:placeholder-shown:focus::placeholder {
 | 
					.inputField input:placeholder-shown:focus::placeholder {
 | 
				
			||||||
  transition: color .25s ease-in .1s;
 | 
					  transition: color .25s ease-in .1s;
 | 
				
			||||||
  color: #6f7882;
 | 
					  color: #6f7882;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.field label {
 | 
					.inputField label {
 | 
				
			||||||
  transition: font-size .25s ease-out .1s,color .25s ease-out .1s,top .25s ease-out .1s,background-color .25s ease-out .1s;
 | 
					  transition: font-size .25s ease-out .1s,color .25s ease-out .1s,top .25s ease-out .1s,background-color .25s ease-out .1s;
 | 
				
			||||||
  color: white;
 | 
					  color: white;
 | 
				
			||||||
  background-color: transparent;
 | 
					  background-color: transparent;
 | 
				
			||||||
| 
						 | 
					@ -69,15 +72,15 @@
 | 
				
			||||||
  max-width: calc(100% - 20px);
 | 
					  max-width: calc(100% - 20px);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.field:focus-within {
 | 
					.inputField:focus-within {
 | 
				
			||||||
  border-color: #0086e6;
 | 
					  border-color: #0086e6;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.field input:focus {
 | 
					.inputField input:focus {
 | 
				
			||||||
  outline: 0;
 | 
					  outline: 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.field input:focus + label, .field input:not(:placeholder-shown) + label {
 | 
					.inputField input:focus + label, .inputField input:not(:placeholder-shown) + label {
 | 
				
			||||||
  background-color: #21262c;
 | 
					  background-color: #21262c;
 | 
				
			||||||
  transition: font-size .25s ease-out 0s,color .25s ease-out 0s,top .25s ease-out 0s,background-color .25s ease-out 0s;
 | 
					  transition: font-size .25s ease-out 0s,color .25s ease-out 0s,top .25s ease-out 0s,background-color .25s ease-out 0s;
 | 
				
			||||||
  font-size: 10px;
 | 
					  font-size: 10px;
 | 
				
			||||||
| 
						 | 
					@ -86,10 +89,59 @@
 | 
				
			||||||
  pointer-events: auto;
 | 
					  pointer-events: auto;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.field input:focus + label {
 | 
					.inputField input:focus + label {
 | 
				
			||||||
  color: #0086e6;
 | 
					  color: #0086e6;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.checkboxField {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: flex-start;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.checkboxField label {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  flex-grow: 1;
 | 
				
			||||||
 | 
					  font-size: 13px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.checkboxField input {
 | 
				
			||||||
 | 
					  appearance: none;
 | 
				
			||||||
 | 
					  margin: 0;
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.checkbox {
 | 
				
			||||||
 | 
					  display: inline-flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					  position: relative;
 | 
				
			||||||
 | 
					  flex-shrink: 0;
 | 
				
			||||||
 | 
					  height: 16px;
 | 
				
			||||||
 | 
					  width: 16px;
 | 
				
			||||||
 | 
					  border: 1.5px solid rgba(185,190,198,.5);
 | 
				
			||||||
 | 
					  box-sizing: border-box;
 | 
				
			||||||
 | 
					  border-radius: 4px;
 | 
				
			||||||
 | 
					  margin-right: 10px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.checkbox svg {
 | 
				
			||||||
 | 
					  display: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.checkbox svg * {
 | 
				
			||||||
 | 
					  stroke: #FFF;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.checkboxField input[type="checkbox"]:checked + label > .checkbox {
 | 
				
			||||||
 | 
					  background: #0dbd8b;
 | 
				
			||||||
 | 
					  border-color: #0dbd8b;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.checkboxField input[type="checkbox"]:checked + label > .checkbox svg {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.button {
 | 
					.button {
 | 
				
			||||||
  vertical-align: middle;
 | 
					  vertical-align: middle;
 | 
				
			||||||
  border: 0;
 | 
					  border: 0;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -60,8 +60,8 @@ export function LoginPage({ onLogin, error }) {
 | 
				
			||||||
                  ref={loginUsernameRef}
 | 
					                  ref={loginUsernameRef}
 | 
				
			||||||
                  placeholder="Username"
 | 
					                  placeholder="Username"
 | 
				
			||||||
                  label="Username"
 | 
					                  label="Username"
 | 
				
			||||||
                  autocorrect="off"
 | 
					                  autoCorrect="off"
 | 
				
			||||||
                  autocapitalize="none"
 | 
					                  autoCapitalize="none"
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
              </FieldRow>
 | 
					              </FieldRow>
 | 
				
			||||||
              <FieldRow>
 | 
					              <FieldRow>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -60,8 +60,8 @@ export function RegisterPage({ onRegister, error }) {
 | 
				
			||||||
                  ref={registerUsernameRef}
 | 
					                  ref={registerUsernameRef}
 | 
				
			||||||
                  placeholder="Username"
 | 
					                  placeholder="Username"
 | 
				
			||||||
                  label="Username"
 | 
					                  label="Username"
 | 
				
			||||||
                  autocorrect="off"
 | 
					                  autoCorrect="off"
 | 
				
			||||||
                  autocapitalize="none"
 | 
					                  autoCapitalize="none"
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
              </FieldRow>
 | 
					              </FieldRow>
 | 
				
			||||||
              <FieldRow>
 | 
					              <FieldRow>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										73
									
								
								src/Room.jsx
									
										
									
									
									
								
							
							
						
						
									
										73
									
								
								src/Room.jsx
									
										
									
									
									
								
							| 
						 | 
					@ -22,7 +22,7 @@ import React, {
 | 
				
			||||||
  useCallback,
 | 
					  useCallback,
 | 
				
			||||||
} from "react";
 | 
					} from "react";
 | 
				
			||||||
import styles from "./Room.module.css";
 | 
					import styles from "./Room.module.css";
 | 
				
			||||||
import { useParams, useLocation } from "react-router-dom";
 | 
					import { useParams, useLocation, useHistory, Link } from "react-router-dom";
 | 
				
			||||||
import { useVideoRoom } from "./ConferenceCallManagerHooks";
 | 
					import { useVideoRoom } from "./ConferenceCallManagerHooks";
 | 
				
			||||||
import { DevTools } from "./DevTools";
 | 
					import { DevTools } from "./DevTools";
 | 
				
			||||||
import { VideoGrid } from "./VideoGrid";
 | 
					import { VideoGrid } from "./VideoGrid";
 | 
				
			||||||
| 
						 | 
					@ -34,7 +34,8 @@ import {
 | 
				
			||||||
  LayoutToggleButton,
 | 
					  LayoutToggleButton,
 | 
				
			||||||
} from "./RoomButton";
 | 
					} from "./RoomButton";
 | 
				
			||||||
import { Header, LeftNav, RightNav, CenterNav } from "./Header";
 | 
					import { Header, LeftNav, RightNav, CenterNav } from "./Header";
 | 
				
			||||||
import { Button } from "./Input";
 | 
					import { Button, FieldRow, InputField, ErrorMessage } from "./Input";
 | 
				
			||||||
 | 
					import { Center, Content, Info, Modal } from "./Layout";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function useQuery() {
 | 
					function useQuery() {
 | 
				
			||||||
  const location = useLocation();
 | 
					  const location = useLocation();
 | 
				
			||||||
| 
						 | 
					@ -145,6 +146,74 @@ export function Room({ manager }) {
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function RoomAuth({ onLoginAsGuest, error }) {
 | 
				
			||||||
 | 
					  const displayNameRef = useRef();
 | 
				
			||||||
 | 
					  const history = useHistory();
 | 
				
			||||||
 | 
					  const location = useLocation();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const onSubmitLoginForm = useCallback(
 | 
				
			||||||
 | 
					    (e) => {
 | 
				
			||||||
 | 
					      e.preventDefault();
 | 
				
			||||||
 | 
					      onLoginAsGuest(displayNameRef.current.value);
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    [onLoginAsGuest, location, history]
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <>
 | 
				
			||||||
 | 
					      <Header>
 | 
				
			||||||
 | 
					        <LeftNav />
 | 
				
			||||||
 | 
					      </Header>
 | 
				
			||||||
 | 
					      <Content>
 | 
				
			||||||
 | 
					        <Center>
 | 
				
			||||||
 | 
					          <Modal>
 | 
				
			||||||
 | 
					            <h2>Login As Guest</h2>
 | 
				
			||||||
 | 
					            <form onSubmit={onSubmitLoginForm}>
 | 
				
			||||||
 | 
					              <FieldRow>
 | 
				
			||||||
 | 
					                <InputField
 | 
				
			||||||
 | 
					                  type="text"
 | 
				
			||||||
 | 
					                  ref={displayNameRef}
 | 
				
			||||||
 | 
					                  placeholder="Display Name"
 | 
				
			||||||
 | 
					                  label="Display Name"
 | 
				
			||||||
 | 
					                  autoCorrect="off"
 | 
				
			||||||
 | 
					                  autoCapitalize="none"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					              </FieldRow>
 | 
				
			||||||
 | 
					              {error && (
 | 
				
			||||||
 | 
					                <FieldRow>
 | 
				
			||||||
 | 
					                  <ErrorMessage>{error.message}</ErrorMessage>
 | 
				
			||||||
 | 
					                </FieldRow>
 | 
				
			||||||
 | 
					              )}
 | 
				
			||||||
 | 
					              <FieldRow rightAlign>
 | 
				
			||||||
 | 
					                <Button type="submit">Login as guest</Button>
 | 
				
			||||||
 | 
					              </FieldRow>
 | 
				
			||||||
 | 
					            </form>
 | 
				
			||||||
 | 
					            <Info>
 | 
				
			||||||
 | 
					              <Link
 | 
				
			||||||
 | 
					                to={{
 | 
				
			||||||
 | 
					                  pathname: "/login",
 | 
				
			||||||
 | 
					                  state: location.state,
 | 
				
			||||||
 | 
					                }}
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					                Sign in
 | 
				
			||||||
 | 
					              </Link>
 | 
				
			||||||
 | 
					              {" or "}
 | 
				
			||||||
 | 
					              <Link
 | 
				
			||||||
 | 
					                to={{
 | 
				
			||||||
 | 
					                  pathname: "/register",
 | 
				
			||||||
 | 
					                  state: location.state,
 | 
				
			||||||
 | 
					                }}
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					                Create account
 | 
				
			||||||
 | 
					              </Link>
 | 
				
			||||||
 | 
					            </Info>
 | 
				
			||||||
 | 
					          </Modal>
 | 
				
			||||||
 | 
					        </Center>
 | 
				
			||||||
 | 
					      </Content>
 | 
				
			||||||
 | 
					    </>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function JoinRoom({
 | 
					function JoinRoom({
 | 
				
			||||||
  joining,
 | 
					  joining,
 | 
				
			||||||
  joinCall,
 | 
					  joinCall,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -746,7 +746,7 @@ function ParticipantTile({ style, participant, remove, presenter, ...rest }) {
 | 
				
			||||||
        ) : participant.audioMuted ? (
 | 
					        ) : participant.audioMuted ? (
 | 
				
			||||||
          <MuteMicIcon className={styles.muteMicIcon} />
 | 
					          <MuteMicIcon className={styles.muteMicIcon} />
 | 
				
			||||||
        ) : null}
 | 
					        ) : null}
 | 
				
			||||||
        <span>{participant.userId}</span>
 | 
					        <span>{participant.displayName}</span>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      {participant.videoMuted && (
 | 
					      {participant.videoMuted && (
 | 
				
			||||||
        <DisableVideoIcon
 | 
					        <DisableVideoIcon
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										3
									
								
								src/icons/Check.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/icons/Check.svg
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					<svg fill="none" height="24" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
 | 
				
			||||||
 | 
					    <path d="m20 6-11 11-5-5"/>
 | 
				
			||||||
 | 
					</svg>
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 213 B  | 
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue