Showing rooms works

This commit is contained in:
qvalentin 2023-02-07 20:31:07 +01:00
parent b0ebeda23a
commit f93cabb99d
17 changed files with 265 additions and 59 deletions

View File

@ -10,6 +10,7 @@
},
"dependencies": {
"@jitsi/react-sdk": "^1.3.0",
"just-curry-it": "^5.3.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},

View File

@ -1,14 +1,29 @@
import "./App.css";
import Meeting from "./components/meeting/Meeting";
import Sidebar from "./components/sidebar/Sidebar";
import useBackendData from "./hooks/useBackendData";
import useConferenceData from "./hooks/useConferenceData";
import useLocalUser from "./hooks/useLocalUser";
function App() {
const { userInfo, setUserInfo } = useLocalUser();
const { roomData, sendMessage } = useBackendData(userInfo);
const { conferenceData, setConferenceData } = useConferenceData(
sendMessage,
setUserInfo
);
console.log(roomData);
if (roomData && userInfo) {
return (
<div className="App">
<Sidebar />
<Meeting />
<Sidebar usersData={roomData} />
<Meeting setConferenceData={setConferenceData} userInfo={userInfo} />
</div>
);
}
return <h2>🌀 Loading...</h2>;
}
export default App;

View File

@ -1,4 +1,14 @@
const JITSI_DOMAIN = "thisisnotajitsi.filefighter.de";
const WEBSOCKET_URL = "ws" + (window.location.protocol == "https:" ? "s" : "") + "://" + window.location.host + "/ws"
const ISPROD = window.location.protocol == "https:";
const JITSI_DOMAIN = ISPROD
? "thisisnotajitsi.filefighter.de"
: "localhost:8443";
const WEBSOCKET_URL =
"ws" +
(ISPROD ? "s" : "") +
"://" +
(ISPROD ? window.location.host : "localhost:9160") +
"/ws";
export { JITSI_DOMAIN, WEBSOCKET_URL };
const USER_COOKIE_NAME = "jitsi-rooms-user";
export { JITSI_DOMAIN, WEBSOCKET_URL, USER_COOKIE_NAME };

View File

@ -0,0 +1,24 @@
function getCookie(cname: string) {
let name = cname + "=";
let decodedCookie = decodeURIComponent(document.cookie);
let ca = decodedCookie.split(";");
for (let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) == " ") {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
function setCookie(cname: string, cvalue: string, exdays = 30) {
const d = new Date();
d.setTime(d.getTime() + exdays * 24 * 60 * 60 * 1000);
let expires = "expires=" + d.toUTCString();
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
}
export { getCookie, setCookie };

View File

@ -0,0 +1,16 @@
export interface ConferenceData {
roomName: string; // the room name of the conference
id: string; // the id of the local participant
displayName: string; // the display name of the local participant
avatarURL: string; // the avatar URL of the local participant
breakoutRoom: boolean; // whether the current room is a breakout room
}
function videoConferenceJoinedListener(
setConferenceData: (newData: ConferenceData) => void,
videoConferenceJoinedEvent: ConferenceData
) {
setConferenceData(videoConferenceJoinedEvent);
}
export { videoConferenceJoinedListener };

View File

@ -0,0 +1,16 @@
export interface RoomData {
roomName: string;
participants: Participant[];
}
export interface Participant {
avatarURL: string;
displayName: string;
jid: string;
email: string;
}
export interface UsersData {
roomsData: RoomData[];
usersWithOutRoom: string[];
}

View File

@ -1,13 +1,19 @@
import { JitsiMeeting } from "@jitsi/react-sdk";
import { JITSI_DOMAIN } from "../../background/constants";
import { UserInfo } from "./types";
import curry from "just-curry-it";
import {
ConferenceData,
videoConferenceJoinedListener,
} from "../../background/jitsi/eventListeners";
interface Props {
roomName: string;
userInfo: UserInfo;
setConferenceData: (newData: ConferenceData) => void;
}
function JitsiEntrypoint({ roomName, userInfo }: Props) {
function JitsiEntrypoint({ roomName, userInfo, setConferenceData }: Props) {
return (
<JitsiMeeting
domain={JITSI_DOMAIN}
@ -24,6 +30,11 @@ function JitsiEntrypoint({ roomName, userInfo }: Props) {
onApiReady={(externalApi) => {
// here you can attach custom event listeners to the Jitsi Meet External API
// you can also store it locally to execute commands
externalApi.addEventListener(
"videoConferenceJoined",
curry(videoConferenceJoinedListener)(setConferenceData)
);
}}
getIFrameRef={(iframeRef) => {
iframeRef.style.height = "100%";

View File

@ -1,7 +1,4 @@
interface UserInfo {
export interface UserInfo {
displayName: string;
email: string
email: string;
}
export { UserInfo }

View File

@ -1,23 +1,32 @@
import { useCallback, useState } from "react";
import { ConferenceData } from "../../background/jitsi/eventListeners";
import { useRoomName } from "../../hooks/useRoomName";
import JitsiEntrypoint from "../jitsi/JitsiEntrypoint";
import { UserInfo } from "../jitsi/types";
import MeetingNameInput from "./MeetingNameInput";
function Meeting() {
interface Props {
setConferenceData: (newData: ConferenceData) => void;
userInfo: UserInfo;
}
function Meeting({ setConferenceData, userInfo }: Props) {
const { roomName, updateRoomName, submitRoomName } = useRoomName();
const [meetingStarted, setMeetingStarted] = useState(false);
const userInfo: UserInfo = { displayName: "unknown traveller", email: "" }
const startMeeting = useCallback(() => {
submitRoomName();
setMeetingStarted(true);
}, [submitRoomName, setMeetingStarted]);
if (meetingStarted) {
return <JitsiEntrypoint roomName={roomName} userInfo={userInfo} />;
return (
<JitsiEntrypoint
roomName={roomName}
userInfo={userInfo}
setConferenceData={setConferenceData}
/>
);
}
return (

View File

@ -1,14 +1,37 @@
import SidebarHeader from "./SidebarHeader";
import useSidebarVisibility from "./useSidebarVisibility";
import "./Sidebar.css";
import { UsersData } from "../../background/types/roomData";
function Sidebar() {
interface Props {
usersData: UsersData;
}
function Sidebar(props: Props) {
const { sidebarVisibility, toggleSidebarVisibility, sidebarToggleText } =
useSidebarVisibility();
return (
<div className={`sidebar sidebar-${sidebarVisibility}`}>
<SidebarHeader sidebarVisibility={sidebarVisibility} />
<div>
<h3> No room</h3>
{props.usersData.usersWithOutRoom.map((username) => (
<div>{username}</div>
))}
</div>
<div>
{props.usersData.roomsData.map((roomData) => {
return (
<>
<h3> {roomData.roomName} </h3>
{roomData.participants.map((participant) => (
<div> {participant.displayName} </div>
))}
</>
);
})}
</div>
<div className="sidebar-footer">
<button onClick={toggleSidebarVisibility}>{sidebarToggleText}</button>
</div>

View File

@ -1,20 +1,26 @@
import { useEffect } from "react";
import { UserInfo } from "../components/jitsi/types";
import useRoomData from "./useRoomData";
import useWebSocketConnection from "./useWebSocketConnection";
function useBackendData() {
function useBackendData(userInfo: UserInfo) {
console.log("[Rooms] useBackendData");
const { onMessage, sendMessage } = useWebSocketConnection(userInfo);
const { onMessage, sendMessage } = useWebSocketConnection();
const { roomData, setRoomData } = useRoomData();
const { roomData, setRoomData } = useRoomData()
useEffect(
() =>
onMessage((messageString) => {
console.log("[Rooms] message from ws", messageString);
const messageObject = JSON.parse(messageString);
!!messageObject.roomsData && setRoomData(messageObject);
}),
[onMessage, setRoomData]
);
onMessage(
(messageString) => {
const messageObject = JSON.parse(messageString)
!!messageObject.roomData && setRoomData(messageObject.roomData)
return { roomData, sendMessage };
}
)
}
export default useBackendData;

View File

@ -0,0 +1,21 @@
import { useState } from "react";
import { ConferenceData } from "../background/jitsi/eventListeners";
import { UserInfo } from "../components/jitsi/types";
function useConferenceData(
sendMessage: (message: string) => void,
setUserInfo: (newData: UserInfo) => void
) {
const [conferenceData, setConferenceDataLocal] = useState<ConferenceData>();
const setConferenceData = (newData: ConferenceData) => {
console.log("[Rooms] set conferenceData");
sendMessage(JSON.stringify(newData));
setConferenceDataLocal(newData);
setUserInfo({ displayName: newData.displayName, email: "" });
};
return { conferenceData, setConferenceData };
}
export default useConferenceData;

View File

@ -0,0 +1,33 @@
import { useState } from "react";
import { USER_COOKIE_NAME } from "../background/constants";
import { getCookie, setCookie } from "../background/cookies";
import { UserInfo } from "../components/jitsi/types";
function useLocalUser() {
const [userInfo, setUserInfoLocal] = useState<UserInfo>(() =>
getUserInfoFromCookie()
);
const setUserInfo = (newData: UserInfo) => {
storeUserInfoInCookie(newData);
setUserInfoLocal(newData);
};
return { userInfo, setUserInfo };
}
function getUserInfoFromCookie(): UserInfo {
let cookie = getCookie(USER_COOKIE_NAME);
console.log("[Rooms] getUserNameFromCookie 1", cookie);
if (cookie) return JSON.parse(cookie);
return {
displayName: "unknown traveller",
email: "",
};
}
function storeUserInfoInCookie(userInfo: UserInfo) {
setCookie(USER_COOKIE_NAME, JSON.stringify(userInfo));
}
export default useLocalUser;

View File

@ -0,0 +1,10 @@
import { useState } from "react";
import { UsersData } from "../background/types/roomData";
function useRoomData() {
const [roomData, setRoomData] = useState<UsersData>();
return { roomData, setRoomData };
}
export default useRoomData;

View File

@ -1,7 +1,7 @@
import { useCallback, useState } from "react";
function useRoomName() {
const [roomName, setRoomName] = useState(getRoomNameFromUrl());
const [roomName, setRoomName] = useState(() => getRoomNameFromUrl());
const updateRoomName = useCallback(
(newName: string) => {
@ -11,13 +11,10 @@ function useRoomName() {
[setRoomName]
);
const submitRoomName = useCallback(
() => {
const submitRoomName = useCallback(() => {
setRoomNameInUrl(roomName);
setRoomNameInTitle(roomName);
},
[roomName]
);
}, [roomName]);
return { roomName, updateRoomName, submitRoomName };
}

View File

@ -1,34 +1,46 @@
import { useCallback, useState } from "react";
import { WEBSOCKET_URL } from "../background/constants";
import { UserInfo } from "../components/jitsi/types";
function useWebSocketConnection() {
const createWebSocketConnection = () => {
const createWebSocketConnection = (userInfo: UserInfo) => {
const webSocket = new WebSocket(WEBSOCKET_URL);
return webSocket
}
console.log("[Rooms] createWebSocketConnection");
webSocket.addEventListener("open", (_: Event) =>
webSocket.send(JSON.stringify(userInfo.displayName))
);
const [webSocketConnection] = useState<WebSocket>(createWebSocketConnection())
return webSocket;
};
const sendMessage = useCallback((message: string) => {
function useWebSocketConnection(userInfo: UserInfo) {
console.log("[Rooms] useWebSocketConnection");
const [webSocketConnection] = useState<WebSocket>(() =>
createWebSocketConnection(userInfo)
);
const sendMessage = useCallback(
(message: string) => {
//if the socket's open, send a message:
if (webSocketConnection.readyState === WebSocket.OPEN) {
console.log("[Rooms] sending to ws", message);
webSocketConnection.send(message);
}
},
[webSocketConnection]
)
);
const onMessage = useCallback(
(messageHandler: (messageHandler: string) => void) => {
const wsMessageHandler = (ev: MessageEvent<any>) => { messageHandler(ev.data) }
webSocketConnection.addEventListener('message', wsMessageHandler)
const wsMessageHandler = (ev: MessageEvent<any>) => {
messageHandler(ev.data);
};
webSocketConnection.addEventListener("message", wsMessageHandler);
},
[webSocketConnection]
)
);
return { onMessage, sendMessage }
return { onMessage, sendMessage };
}
export default useWebSocketConnection;

View File

@ -558,6 +558,11 @@ json5@^2.2.2:
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
just-curry-it@^5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/just-curry-it/-/just-curry-it-5.3.0.tgz#1463602e932c5beb431a2a384dddcd48bb3c9c42"
integrity sha512-silMIRiFjUWlfaDhkgSzpuAyQ6EX/o09Eu8ZBfmFwQMbax7+LQzeIU2CBrICT6Ne4l86ITCGvUCBpCubWYy0Yw==
loose-envify@^1.1.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"