Add guest access for rooms
This commit is contained in:
parent
196c8eeeeb
commit
46f8eb84fc
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…
Reference in a new issue