add prettier and reformat project
continuous-integration/drone/push Build is passing Details

This commit is contained in:
open-schnick 2023-04-10 23:28:28 +02:00
parent c86873fc28
commit 1aa7914289
No known key found for this signature in database
GPG Key ID: B1FA75F86D28E80E
31 changed files with 1894 additions and 576 deletions

15
frontend/.prettierrc Normal file
View File

@ -0,0 +1,15 @@
{
"useTabs": false,
"tabWidth": 2,
"semi": false,
"singleQuote": true,
"jsxSingleQuote": false,
"trailingComma": "es5",
"bracketSpacing": true,
"bracketSameLine": false,
"arrowParens": "always",
"embeddedLanguageFormatting": "auto",
"endOfLine": "lf",
"printWidth": 100,
"vueIndentScriptAndStyle": false
}

View File

@ -6,7 +6,6 @@ Using vite and react plus the jitsi react sdk for the frontend.
Use yarn: Use yarn:
```sh ```sh
yarn run dev yarn run dev
``` ```

View File

@ -1,5 +1,4 @@
#!/usr/bin/env sh #!/usr/bin/env sh
yarn build yarn build
scp -r dist/* ffs:/home/ffsys/apps/static/jitsi-rooms scp -r dist/* ffs:/home/ffsys/apps/static/jitsi-rooms

View File

@ -22,6 +22,12 @@
"@types/react": "^18.0.26", "@types/react": "^18.0.26",
"@types/react-dom": "^18.0.9", "@types/react-dom": "^18.0.9",
"@vitejs/plugin-react": "^3.0.0", "@vitejs/plugin-react": "^3.0.0",
"eslint": "^8.38.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"typescript": "^4.9.3", "typescript": "^4.9.3",
"vite": "^4.0.0" "vite": "^4.0.0"
} }

View File

@ -1,39 +1,34 @@
import { Provider } from "jotai"; import { Provider } from 'jotai'
import { useState } from "react"; import { useState } from 'react'
import "./App.css"; import './App.css'
import Meeting from "./components/meeting/Meeting"; import Meeting from './components/meeting/Meeting'
import Sidebar from "./components/sidebar/Sidebar"; import Sidebar from './components/sidebar/Sidebar'
import useBackendData from "./hooks/useBackendData"; import useBackendData from './hooks/useBackendData'
import useConferenceData from "./hooks/useConferenceData"; import useConferenceData from './hooks/useConferenceData'
import useLocalUser from "./hooks/useLocalUser"; import useLocalUser from './hooks/useLocalUser'
import { useRoomName } from "./hooks/useRoomName"; import { useRoomName } from './hooks/useRoomName'
function App() { function App() {
const { userInfo, setUserInfo } = useLocalUser(); const { userInfo, setUserInfo } = useLocalUser()
const { roomData, sendMessage } = useBackendData(userInfo); const { roomData, sendMessage } = useBackendData(userInfo)
const { conferenceData, setConferenceData } = useConferenceData( const { conferenceData, setConferenceData } = useConferenceData(sendMessage, setUserInfo)
sendMessage, const [meetingStarted, setMeetingStarted] = useState(false)
setUserInfo
);
const [meetingStarted, setMeetingStarted] = useState(false);
console.log(roomData); console.log(roomData)
if (roomData && userInfo) { if (roomData && userInfo) {
return ( return (
<div className="App"> <div className="App">
<Sidebar usersData={roomData} <Sidebar usersData={roomData} sendMessage={sendMessage} />
sendMessage={sendMessage}
/>
<Meeting <Meeting
conferenceData={conferenceData} conferenceData={conferenceData}
setConferenceData={setConferenceData} setConferenceData={setConferenceData}
userInfo={userInfo} userInfo={userInfo}
/> />
</div> </div>
); )
} }
return <h2>🌀 Loading...</h2>; return <h2>🌀 Loading...</h2>
} }
export default App; export default App

View File

@ -1,13 +1,13 @@
const ISPROD = window.location.protocol == "https:"; const ISPROD = window.location.protocol == 'https:'
const JITSI_DOMAIN = "thisisnotajitsi.filefighter.de"; const JITSI_DOMAIN = 'thisisnotajitsi.filefighter.de'
const USE_REMOTE_BACKEND = true; const USE_REMOTE_BACKEND = true
const getWebsocketUrl = () => { const getWebsocketUrl = () => {
if (ISPROD) return "wss://" + window.location.host + "/ws" if (ISPROD) return 'wss://' + window.location.host + '/ws'
if (USE_REMOTE_BACKEND) return "wss://" + "discord.filefighter.de/ws" if (USE_REMOTE_BACKEND) return 'wss://' + 'discord.filefighter.de/ws'
return "ws://" + "localhost:9160/ws" return 'ws://' + 'localhost:9160/ws'
} }
const WEBSOCKET_URL = getWebsocketUrl(); const WEBSOCKET_URL = getWebsocketUrl()
const USER_COOKIE_NAME = "jitsi-rooms-user"; const USER_COOKIE_NAME = 'jitsi-rooms-user'
export { JITSI_DOMAIN, WEBSOCKET_URL, USER_COOKIE_NAME }; export { JITSI_DOMAIN, WEBSOCKET_URL, USER_COOKIE_NAME }

View File

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

View File

@ -1,28 +1,28 @@
export interface ConferenceData { export interface ConferenceData {
roomName: string; // the room name of the conference roomName: string // the room name of the conference
id: string; // the id of the local participant id: string // the id of the local participant
displayName: string; // the display name of the local participant displayName: string // the display name of the local participant
avatarURL: string; // the avatar URL of the local participant avatarURL: string // the avatar URL of the local participant
breakoutRoom: boolean; // whether the current room is a breakout room breakoutRoom: boolean // whether the current room is a breakout room
} }
export interface DisplayNameChangeEvent { export interface DisplayNameChangeEvent {
id: string; // the id of the participant that changed their display name id: string // the id of the participant that changed their display name
displayname: string; // the new display name (WTF why is capilization different) displayname: string // the new display name (WTF why is capilization different)
} }
function videoConferenceJoinedListener( function videoConferenceJoinedListener(
setConferenceData: (newData: ConferenceData) => void, setConferenceData: (newData: ConferenceData) => void,
videoConferenceJoinedEvent: ConferenceData videoConferenceJoinedEvent: ConferenceData
) { ) {
setConferenceData(videoConferenceJoinedEvent); setConferenceData(videoConferenceJoinedEvent)
} }
function displayNameChangeListenerWrapper( function displayNameChangeListenerWrapper(
setDisplayNameChangedEvent: (newData: DisplayNameChangeEvent) => void, setDisplayNameChangedEvent: (newData: DisplayNameChangeEvent) => void,
displayNameChangeEvent: DisplayNameChangeEvent displayNameChangeEvent: DisplayNameChangeEvent
) { ) {
setDisplayNameChangedEvent(displayNameChangeEvent); setDisplayNameChangedEvent(displayNameChangeEvent)
} }
function displayNameChangeListener( function displayNameChangeListener(
@ -30,20 +30,16 @@ function displayNameChangeListener(
setConferenceData: (newData: ConferenceData) => void, setConferenceData: (newData: ConferenceData) => void,
displayNameChangeEvent: DisplayNameChangeEvent displayNameChangeEvent: DisplayNameChangeEvent
) { ) {
console.log( console.log('[Rooms] displayNameChangeListener', displayNameChangeEvent, conferenceData)
"[Rooms] displayNameChangeListener",
displayNameChangeEvent,
conferenceData
);
if (conferenceData && conferenceData.id == displayNameChangeEvent.id) if (conferenceData && conferenceData.id == displayNameChangeEvent.id)
setConferenceData({ setConferenceData({
...conferenceData, ...conferenceData,
displayName: displayNameChangeEvent.displayname, displayName: displayNameChangeEvent.displayname,
} as ConferenceData); } as ConferenceData)
} }
export { export {
videoConferenceJoinedListener, videoConferenceJoinedListener,
displayNameChangeListenerWrapper, displayNameChangeListenerWrapper,
displayNameChangeListener, displayNameChangeListener,
}; }

View File

@ -1,14 +1,13 @@
function getHslColor(i: number) { function getHslColor(i: number) {
return "hsl(" + i % 360 + ',' + return 'hsl(' + (i % 360) + ',' + '70%,' + '72%)'
'70%,' +
'72%)'
} }
function hashCode(str: string) { // java String#hashCode function hashCode(str: string) {
var hash = 0; // java String#hashCode
var hash = 0
for (var i = 0; i < str.length; i++) { for (var i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash); hash = str.charCodeAt(i) + ((hash << 5) - hash)
} }
return hash; return hash
} }
// function intToRGB(i: number) { // function intToRGB(i: number) {
@ -22,8 +21,6 @@ function hashCode(str: string) { // java String#hashCode
function getColorForUserName(userName: string) { function getColorForUserName(userName: string) {
const hash = hashCode(userName) const hash = hashCode(userName)
return getHslColor(hash) return getHslColor(hash)
} }
export { getColorForUserName } export { getColorForUserName }

View File

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

View File

@ -1,46 +1,43 @@
.chat-input{ .chat-input {
min-width: 0; min-width: 0;
} }
.chat{ .chat {
background-color: #181c25; background-color: #181c25;
border-radius: 8px; border-radius: 8px;
padding: 0.5em 0; padding: 0.5em 0;
width: 100%; width: 100%;
margin: 0 -3px; margin: 0 -3px;
} }
.chat-sender{ .chat-sender {
font-size: 0.9em; font-size: 0.9em;
/* color: #213547; */ /* color: #213547; */
} }
.chat-input-form{ .chat-input-form {
display: flex; display: flex;
} }
.chat-messages {
.chat-messages{ max-height: 220px;
max-height: 220px; overflow-y: scroll;
overflow-y: scroll;
display: flex; display: flex;
flex-direction: column-reverse; flex-direction: column-reverse;
} }
.chat-bubble{ .chat-bubble {
background-color: #2b2a33; background-color: #2b2a33;
overflow-wrap: break-word; overflow-wrap: break-word;
margin-bottom: 7px; margin-bottom: 7px;
border-radius: 8px; border-radius: 8px;
padding: 3px 4px ; padding: 3px 4px;
white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */
white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */ white-space: -pre-wrap; /* Opera 4-6 */
white-space: -pre-wrap; /* Opera 4-6 */ white-space: -o-pre-wrap; /* Opera 7 */
white-space: -o-pre-wrap; /* Opera 7 */ white-space: pre-wrap; /* css-3 */
white-space: pre-wrap; /* css-3 */ word-wrap: break-word; /* Internet Explorer 5.5+ */
word-wrap: break-word; /* Internet Explorer 5.5+ */ white-space: -webkit-pre-wrap; /* Newer versions of Chrome/Safari*/
white-space: -webkit-pre-wrap; /* Newer versions of Chrome/Safari*/ word-break: break-all;
word-break: break-all; white-space: normal;
white-space: normal;
} }

View File

@ -1,27 +1,27 @@
import { useState } from "react"; import { useState } from 'react'
import { FormEventHandler } from "react"; import { FormEventHandler } from 'react'
import useAllChat from "../../hooks/useAllChat"; import useAllChat from '../../hooks/useAllChat'
import "./Chat.css" import './Chat.css'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faPaperPlane } from "@fortawesome/free-regular-svg-icons"; import { faPaperPlane } from '@fortawesome/free-regular-svg-icons'
import { getColorForUserName } from "../../background/style/colorednames"; import { getColorForUserName } from '../../background/style/colorednames'
interface Props { interface Props {
sendMessage: Function sendMessage: Function
} }
function Chat({ sendMessage }: Props) { function Chat({ sendMessage }: Props) {
const [chatInput, setChatInput] = useState("") const [chatInput, setChatInput] = useState('')
const { chatMesages } = useAllChat() const { chatMesages } = useAllChat()
const onInput: React.ChangeEventHandler<HTMLInputElement> = (event) => { const onInput: React.ChangeEventHandler<HTMLInputElement> = (event) => {
setChatInput(event.target.value); setChatInput(event.target.value)
event.preventDefault(); event.preventDefault()
}; }
const onSubmit: FormEventHandler<HTMLFormElement> = (event) => { const onSubmit: FormEventHandler<HTMLFormElement> = (event) => {
event.preventDefault(); event.preventDefault()
sendMessage(JSON.stringify({ content: chatInput })); sendMessage(JSON.stringify({ content: chatInput }))
setChatInput("") setChatInput('')
} }
return ( return (
@ -30,15 +30,20 @@ function Chat({ sendMessage }: Props) {
{chatMesages {chatMesages
.slice() .slice()
.reverse() // reverse because of css, don't aks me .reverse() // reverse because of css, don't aks me
.map(message => { .map((message) => {
const nameStyle = { const nameStyle = {
color: getColorForUserName(message.sender.name) color: getColorForUserName(message.sender.name),
} }
return ( return (
<div className="chat-bubble" key={message.uuid}> <span className="chat-sender" style={nameStyle}>{message.sender.name} <br /> </span> {message.content} </div> <div className="chat-bubble" key={message.uuid}>
); {' '}
}) <span className="chat-sender" style={nameStyle}>
} {message.sender.name} <br />{' '}
</span>{' '}
{message.content}{' '}
</div>
)
})}
</div> </div>
<form className="chat-input-form" onSubmit={onSubmit}> <form className="chat-input-form" onSubmit={onSubmit}>
<input <input
@ -48,10 +53,12 @@ function Chat({ sendMessage }: Props) {
value={chatInput} value={chatInput}
onChange={onInput} onChange={onInput}
/> />
<button type="submit"><FontAwesomeIcon icon={faPaperPlane} /></button> <button type="submit">
<FontAwesomeIcon icon={faPaperPlane} />
</button>
</form> </form>
</div> </div>
); )
} }
export default Chat export default Chat

View File

@ -1,61 +1,56 @@
import { JitsiMeeting } from "@jitsi/react-sdk"; import { JitsiMeeting } from '@jitsi/react-sdk'
import { JITSI_DOMAIN } from "../../background/constants"; import { JITSI_DOMAIN } from '../../background/constants'
import { UserInfo } from "./types"; import { UserInfo } from './types'
import curry from "just-curry-it"; import curry from 'just-curry-it'
import { import {
ConferenceData, ConferenceData,
displayNameChangeListener, displayNameChangeListener,
displayNameChangeListenerWrapper, displayNameChangeListenerWrapper,
videoConferenceJoinedListener, videoConferenceJoinedListener,
} from "../../background/jitsi/eventListeners"; } from '../../background/jitsi/eventListeners'
import useJitsiEventCapture from "../../hooks/useJitsiEventCapture"; import useJitsiEventCapture from '../../hooks/useJitsiEventCapture'
interface Props { interface Props {
roomName: string; roomName: string
userInfo: UserInfo; userInfo: UserInfo
conferenceData: ConferenceData | undefined; conferenceData: ConferenceData | undefined
setConferenceData: (newData: ConferenceData) => void; setConferenceData: (newData: ConferenceData) => void
} }
function JitsiEntrypoint({ function JitsiEntrypoint({ roomName, userInfo, conferenceData, setConferenceData }: Props) {
roomName, const { setDisplayNameChangeEvent } = useJitsiEventCapture(
userInfo, curry(displayNameChangeListener)(conferenceData)(setConferenceData)
conferenceData, )
setConferenceData,
}: Props) {
const { setDisplayNameChangeEvent } = useJitsiEventCapture(
curry(displayNameChangeListener)(conferenceData)(setConferenceData)
);
return ( return (
<JitsiMeeting <JitsiMeeting
domain={JITSI_DOMAIN} domain={JITSI_DOMAIN}
roomName={roomName} roomName={roomName}
configOverwrite={{ configOverwrite={{
startWithAudioMuted: true, startWithAudioMuted: true,
disableModeratorIndicator: true, disableModeratorIndicator: true,
startScreenSharing: true, startScreenSharing: true,
enableEmailInStats: false, enableEmailInStats: false,
prejoinConfig: { enabled: false }, prejoinConfig: { enabled: false },
}} }}
interfaceConfigOverwrite={{}} interfaceConfigOverwrite={{}}
userInfo={userInfo} userInfo={userInfo}
onApiReady={(externalApi) => { onApiReady={(externalApi) => {
externalApi.addEventListener( externalApi.addEventListener(
"videoConferenceJoined", 'videoConferenceJoined',
curry(videoConferenceJoinedListener)(setConferenceData) curry(videoConferenceJoinedListener)(setConferenceData)
); )
externalApi.addEventListener( externalApi.addEventListener(
"displayNameChange", 'displayNameChange',
curry(displayNameChangeListenerWrapper)(setDisplayNameChangeEvent) curry(displayNameChangeListenerWrapper)(setDisplayNameChangeEvent)
); )
}} }}
getIFrameRef={(iframeRef) => { getIFrameRef={(iframeRef) => {
iframeRef.style.height = "100%"; iframeRef.style.height = '100%'
iframeRef.style.width = "100%"; iframeRef.style.width = '100%'
}} }}
/> />
); )
} }
export default JitsiEntrypoint; export default JitsiEntrypoint

View File

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

View File

@ -1,19 +1,18 @@
import { useCallback, useState } from "react"; import { useCallback, useState } from 'react'
import { ConferenceData } from "../../background/jitsi/eventListeners"; import { ConferenceData } from '../../background/jitsi/eventListeners'
import useMeetingStarted from "../../hooks/useMeetingStarted"; import useMeetingStarted from '../../hooks/useMeetingStarted'
import { useRoomName } from "../../hooks/useRoomName"; import { useRoomName } from '../../hooks/useRoomName'
import JitsiEntrypoint from "../jitsi/JitsiEntrypoint"; import JitsiEntrypoint from '../jitsi/JitsiEntrypoint'
import { UserInfo } from "../jitsi/types"; import { UserInfo } from '../jitsi/types'
import MeetingNameInput from "./MeetingNameInput"; import MeetingNameInput from './MeetingNameInput'
interface Props { interface Props {
conferenceData: ConferenceData | undefined; conferenceData: ConferenceData | undefined
setConferenceData: (newData: ConferenceData) => void; setConferenceData: (newData: ConferenceData) => void
userInfo: UserInfo; userInfo: UserInfo
} }
function Meeting({ conferenceData, setConferenceData, userInfo }: Props) { function Meeting({ conferenceData, setConferenceData, userInfo }: Props) {
const [meetingStarted, setMeetingStarted] = useMeetingStarted() const [meetingStarted, setMeetingStarted] = useMeetingStarted()
const { roomName } = useRoomName() const { roomName } = useRoomName()
@ -25,15 +24,10 @@ function Meeting({ conferenceData, setConferenceData, userInfo }: Props) {
userInfo={userInfo} userInfo={userInfo}
setConferenceData={setConferenceData} setConferenceData={setConferenceData}
/> />
); )
} }
return ( return <MeetingNameInput roomName={roomName} currentUser={userInfo.displayName} />
<MeetingNameInput
roomName={roomName}
currentUser={userInfo.displayName}
/>
);
} }
export default Meeting; export default Meeting

View File

@ -1,41 +1,33 @@
import { FormEventHandler } from "react"; import { FormEventHandler } from 'react'
import useMeetingStarted from "../../hooks/useMeetingStarted"; import useMeetingStarted from '../../hooks/useMeetingStarted'
import { useRoomName } from "../../hooks/useRoomName"; import { useRoomName } from '../../hooks/useRoomName'
import "./MeetingNameInput.css"; import './MeetingNameInput.css'
function MeetingNameInput(props: {
roomName: string; currentUser: string;
}) {
function MeetingNameInput(props: { roomName: string; currentUser: string }) {
const { roomName, updateRoomName, updateAndSubmitRoomName, submitRoomName } = useRoomName() const { roomName, updateRoomName, updateAndSubmitRoomName, submitRoomName } = useRoomName()
const [_, setMeetingStarted] = useMeetingStarted() const [_, setMeetingStarted] = useMeetingStarted()
const onInput: React.ChangeEventHandler<HTMLInputElement> = (event) => { const onInput: React.ChangeEventHandler<HTMLInputElement> = (event) => {
updateRoomName(event.target.value); updateRoomName(event.target.value)
event.preventDefault(); event.preventDefault()
}; }
const onSubmit: FormEventHandler<HTMLFormElement> = (event) => { const onSubmit: FormEventHandler<HTMLFormElement> = (event) => {
submitRoomName() submitRoomName()
setMeetingStarted(true) setMeetingStarted(true)
event.preventDefault(); event.preventDefault()
} }
console.log("[Rooms] MeetingName input comp"); console.log('[Rooms] MeetingName input comp')
return ( return (
<div className="meeting-name-input"> <div className="meeting-name-input">
<h1>Greetings {props.currentUser}</h1> <h1>Greetings {props.currentUser}</h1>
<form onSubmit={onSubmit}> <form onSubmit={onSubmit}>
<input <input placeholder="Roomname" type="text" value={roomName} onChange={onInput} />
placeholder="Roomname"
type="text"
value={roomName}
onChange={onInput}
/>
<button type="submit">Enter the adventure</button> <button type="submit">Enter the adventure</button>
</form> </form>
</div> </div>
); )
} }
export default MeetingNameInput; export default MeetingNameInput

View File

@ -1,21 +1,20 @@
import SidebarHeader from "./SidebarHeader"; import SidebarHeader from './SidebarHeader'
import useSidebarVisibility from "./useSidebarVisibility"; import useSidebarVisibility from './useSidebarVisibility'
import "./Sidebar.css"; import './Sidebar.css'
import { UsersData } from "../../background/types/roomData"; import { UsersData } from '../../background/types/roomData'
import useMeetingStarted from "../../hooks/useMeetingStarted"; import useMeetingStarted from '../../hooks/useMeetingStarted'
import { useRoomName } from "../../hooks/useRoomName"; import { useRoomName } from '../../hooks/useRoomName'
import Chat from "../chat/Chat"; import Chat from '../chat/Chat'
interface Props { interface Props {
usersData: UsersData; usersData: UsersData
sendMessage: Function sendMessage: Function
} }
function Sidebar(props: Props) { function Sidebar(props: Props) {
const { sidebarVisibility, toggleSidebarVisibility, sidebarToggleText } = const { sidebarVisibility, toggleSidebarVisibility, sidebarToggleText } = useSidebarVisibility()
useSidebarVisibility();
const [_, setMeetingStarted] = useMeetingStarted() const [_, setMeetingStarted] = useMeetingStarted()
const { updateAndSubmitRoomName: updateAndSubmitRoomName } = useRoomName(); const { updateAndSubmitRoomName: updateAndSubmitRoomName } = useRoomName()
return ( return (
<div className={`sidebar sidebar-${sidebarVisibility}`}> <div className={`sidebar sidebar-${sidebarVisibility}`}>
@ -25,10 +24,13 @@ function Sidebar(props: Props) {
return ( return (
<> <>
<h3> <h3>
<a href="#" onClick={() => { <a
updateAndSubmitRoomName(roomData.roomName) href="#"
setMeetingStarted(true) onClick={() => {
}}> updateAndSubmitRoomName(roomData.roomName)
setMeetingStarted(true)
}}
>
{roomData.roomName} {roomData.roomName}
</a> </a>
</h3> </h3>
@ -36,7 +38,7 @@ function Sidebar(props: Props) {
<div key={participant.jid}> {participant.displayName} </div> <div key={participant.jid}> {participant.displayName} </div>
))} ))}
</> </>
); )
})} })}
</div> </div>
<div> <div>
@ -46,11 +48,13 @@ function Sidebar(props: Props) {
))} ))}
</div> </div>
<div className="sidebar-footer"> <div className="sidebar-footer">
{sidebarVisibility === "full" && <Chat sendMessage={props.sendMessage} />} {sidebarVisibility === 'full' && <Chat sendMessage={props.sendMessage} />}
<button className="sidebar-toggle" onClick={toggleSidebarVisibility}>{sidebarToggleText}</button> <button className="sidebar-toggle" onClick={toggleSidebarVisibility}>
{sidebarToggleText}
</button>
</div> </div>
</div> </div>
); )
} }
export default Sidebar; export default Sidebar

View File

@ -1,16 +1,16 @@
import { sidebarVisibilityOptions } from "./useSidebarVisibility"; import { sidebarVisibilityOptions } from './useSidebarVisibility'
interface props { interface props {
sidebarVisibility: sidebarVisibilityOptions; sidebarVisibility: sidebarVisibilityOptions
} }
function SidebarHeader({ sidebarVisibility }: props) { function SidebarHeader({ sidebarVisibility }: props) {
if (sidebarVisibility == "full") { if (sidebarVisibility == 'full') {
return <h2>hi there :)</h2>; return <h2>hi there :)</h2>
} else if (sidebarVisibility == "small") { } else if (sidebarVisibility == 'small') {
return <h2>hi</h2>; return <h2>hi</h2>
} }
return <></>; return <></>
} }
export default SidebarHeader; export default SidebarHeader

View File

@ -1,28 +1,27 @@
import { useCallback, useState } from "react"; import { useCallback, useState } from 'react'
export type sidebarVisibilityOptions = "full" | "small" | "hidden"; export type sidebarVisibilityOptions = 'full' | 'small' | 'hidden'
function useSidebarVisibility() { function useSidebarVisibility() {
const [sidebarVisibility, setSideBarVisibility] = const [sidebarVisibility, setSideBarVisibility] = useState<sidebarVisibilityOptions>('full')
useState<sidebarVisibilityOptions>("full");
const sidebarToggleText = getSidebarToggleText(sidebarVisibility); const sidebarToggleText = getSidebarToggleText(sidebarVisibility)
const toggleSidebarVisibility = useCallback(() => { const toggleSidebarVisibility = useCallback(() => {
if (sidebarVisibility === "full") { if (sidebarVisibility === 'full') {
setSideBarVisibility("small"); setSideBarVisibility('small')
} else if (sidebarVisibility === "small") { } else if (sidebarVisibility === 'small') {
setSideBarVisibility("hidden"); setSideBarVisibility('hidden')
} else if (sidebarVisibility === "hidden") { } else if (sidebarVisibility === 'hidden') {
setSideBarVisibility("full"); setSideBarVisibility('full')
} }
}, [setSideBarVisibility, sidebarVisibility, sidebarToggleText]); }, [setSideBarVisibility, sidebarVisibility, sidebarToggleText])
return { sidebarVisibility, toggleSidebarVisibility, sidebarToggleText }; return { sidebarVisibility, toggleSidebarVisibility, sidebarToggleText }
} }
const getSidebarToggleText = (sidebarVisibility: sidebarVisibilityOptions) => { const getSidebarToggleText = (sidebarVisibility: sidebarVisibilityOptions) => {
if (sidebarVisibility === "full") return "<-"; if (sidebarVisibility === 'full') return '<-'
if (sidebarVisibility === "small") return "<-"; if (sidebarVisibility === 'small') return '<-'
if (sidebarVisibility === "hidden") return "->"; if (sidebarVisibility === 'hidden') return '->'
}; }
export default useSidebarVisibility; export default useSidebarVisibility

View File

@ -1,6 +1,6 @@
import { atom, useAtom } from "jotai"; import { atom, useAtom } from 'jotai'
import { atomWithReducer, useReducerAtom } from 'jotai/utils' import { atomWithReducer, useReducerAtom } from 'jotai/utils'
import { User } from "../background/types/roomData"; import { User } from '../background/types/roomData'
interface ChatMessage { interface ChatMessage {
content: string content: string
@ -9,7 +9,9 @@ interface ChatMessage {
timestamp: number timestamp: number
} }
export const allChatMessagesAtom = atomWithReducer([], (list: ChatMessage[], item: ChatMessage) => list.concat(item)) export const allChatMessagesAtom = atomWithReducer([], (list: ChatMessage[], item: ChatMessage) =>
list.concat(item)
)
const useAllChat = () => { const useAllChat = () => {
const [chatMessages, addChatMessage] = useAtom(allChatMessagesAtom) const [chatMessages, addChatMessage] = useAtom(allChatMessagesAtom)
@ -17,5 +19,3 @@ const useAllChat = () => {
} }
export default useAllChat export default useAllChat

View File

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

View File

@ -1,28 +1,28 @@
import { useState } from "react"; import { useState } from 'react'
import { ConferenceData } from "../background/jitsi/eventListeners"; import { ConferenceData } from '../background/jitsi/eventListeners'
import { UserInfo } from "../components/jitsi/types"; import { UserInfo } from '../components/jitsi/types'
function useConferenceData( function useConferenceData(
sendMessage: (message: string) => void, sendMessage: (message: string) => void,
setUserInfo: (newData: UserInfo) => void setUserInfo: (newData: UserInfo) => void
) { ) {
const [conferenceData, setConferenceDataLocal] = useState<ConferenceData>(); const [conferenceData, setConferenceDataLocal] = useState<ConferenceData>()
const setConferenceData = (newData: ConferenceData) => { const setConferenceData = (newData: ConferenceData) => {
console.log("[Rooms] set conferenceData", conferenceData); console.log('[Rooms] set conferenceData', conferenceData)
if (conferenceData?.roomName !== newData.roomName) { if (conferenceData?.roomName !== newData.roomName) {
// We joined a meeting // We joined a meeting
sendMessage(JSON.stringify({ roomName: newData.roomName })); sendMessage(JSON.stringify({ roomName: newData.roomName }))
} }
if (conferenceData?.displayName !== newData.displayName) { if (conferenceData?.displayName !== newData.displayName) {
// We updated the username // We updated the username
sendMessage(JSON.stringify({ displayName: newData.displayName })); sendMessage(JSON.stringify({ displayName: newData.displayName }))
} }
setConferenceDataLocal(newData); setConferenceDataLocal(newData)
setUserInfo({ displayName: newData.displayName, email: "" }); setUserInfo({ displayName: newData.displayName, email: '' })
}; }
return { conferenceData, setConferenceData }; return { conferenceData, setConferenceData }
} }
export default useConferenceData; export default useConferenceData

View File

@ -1,28 +1,28 @@
import { useCallback, useEffect, useRef, useState } from "react"; import { useCallback, useEffect, useRef, useState } from 'react'
import { DisplayNameChangeEvent } from "../background/jitsi/eventListeners"; import { DisplayNameChangeEvent } from '../background/jitsi/eventListeners'
function useJitsiEventCapture(displayNameChangeHandler: Function) { function useJitsiEventCapture(displayNameChangeHandler: Function) {
const [displayNameChangeEvent, setDisplayNameChangeEventLocal] = const [displayNameChangeEvent, setDisplayNameChangeEventLocal] =
useState<DisplayNameChangeEvent>(); useState<DisplayNameChangeEvent>()
const setDisplayNameChangeEvent = useCallback( const setDisplayNameChangeEvent = useCallback(
(newValue: DisplayNameChangeEvent) => { (newValue: DisplayNameChangeEvent) => {
console.log("[Rooms] newValue", newValue, displayNameChangeEvent); console.log('[Rooms] newValue', newValue, displayNameChangeEvent)
if (displayNameChangeEvent != newValue) { if (displayNameChangeEvent != newValue) {
setDisplayNameChangeEventLocal(newValue); setDisplayNameChangeEventLocal(newValue)
} }
}, },
[setDisplayNameChangeEventLocal] [setDisplayNameChangeEventLocal]
); )
useEffect(() => { useEffect(() => {
console.log("[Rooms] displayNameChangeHandler", displayNameChangeEvent); console.log('[Rooms] displayNameChangeHandler', displayNameChangeEvent)
displayNameChangeHandler && displayNameChangeHandler &&
displayNameChangeEvent && displayNameChangeEvent &&
displayNameChangeHandler(displayNameChangeEvent); displayNameChangeHandler(displayNameChangeEvent)
}, [displayNameChangeEvent]); }, [displayNameChangeEvent])
return { setDisplayNameChangeEvent }; return { setDisplayNameChangeEvent }
} }
export default useJitsiEventCapture; export default useJitsiEventCapture

View File

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

View File

@ -1,7 +1,7 @@
import { atom, useAtom } from "jotai"; import { atom, useAtom } from 'jotai'
const meetingStarted = atom(false) const meetingStarted = atom(false)
const useMeetingStarted = () => useAtom(meetingStarted); const useMeetingStarted = () => useAtom(meetingStarted)
export default useMeetingStarted export default useMeetingStarted

View File

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

View File

@ -1,5 +1,5 @@
import { atom, useAtom } from "jotai"; import { atom, useAtom } from 'jotai'
import { useCallback, useState } from "react"; import { useCallback, useState } from 'react'
const roomNameAtom = atom(getRoomNameFromUrl()) const roomNameAtom = atom(getRoomNameFromUrl())
@ -8,48 +8,46 @@ function useRoomName() {
const updateRoomName = useCallback( const updateRoomName = useCallback(
(newName: string) => { (newName: string) => {
setRoomName(newName); setRoomName(newName)
console.log("[Rooms] update room name", newName); console.log('[Rooms] update room name', newName)
}, },
[setRoomName] [setRoomName]
); )
const updateAndSubmitRoomName = useCallback( const updateAndSubmitRoomName = useCallback(
(newName: string) => { (newName: string) => {
setRoomName(newName); setRoomName(newName)
setRoomNameInUrl(newName); setRoomNameInUrl(newName)
setRoomNameInTitle(newName); setRoomNameInTitle(newName)
console.log("[Rooms] update and submit room name", newName); console.log('[Rooms] update and submit room name', newName)
}, },
[setRoomName] [setRoomName]
) )
const submitRoomName = useCallback(() => { const submitRoomName = useCallback(() => {
setRoomNameInUrl(roomName); setRoomNameInUrl(roomName)
setRoomNameInTitle(roomName); setRoomNameInTitle(roomName)
}, [roomName]); }, [roomName])
return { roomName, updateRoomName, updateAndSubmitRoomName, submitRoomName }; return { roomName, updateRoomName, updateAndSubmitRoomName, submitRoomName }
} }
function getRoomNameFromUrl(): string { function getRoomNameFromUrl(): string {
const pathName = location.pathname; const pathName = location.pathname
const roomName = pathName const roomName = pathName.substring(pathName.lastIndexOf('/'), pathName.length).replace('/', '')
.substring(pathName.lastIndexOf("/"), pathName.length) console.log('[Rooms] Got roomName from url', roomName)
.replace("/", ""); setRoomNameInTitle(roomName)
console.log("[Rooms] Got roomName from url", roomName); return roomName
setRoomNameInTitle(roomName);
return roomName;
} }
function setRoomNameInUrl(roomName: string) { function setRoomNameInUrl(roomName: string) {
history.pushState(history.state, "_", roomName); history.pushState(history.state, '_', roomName)
} }
function setRoomNameInTitle(roomName: string) { function setRoomNameInTitle(roomName: string) {
if (!!roomName) { if (!!roomName) {
document.title = roomName; document.title = roomName
} }
} }
export { useRoomName }; export { useRoomName }

View File

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

View File

@ -78,7 +78,6 @@ button:focus-visible {
width: 100%; width: 100%;
} }
input { input {
font-size: 1em; font-size: 1em;
margin: 0.3em; margin: 0.3em;
@ -88,7 +87,7 @@ input {
box-shadow: none; box-shadow: none;
box-sizing: border-box; box-sizing: border-box;
font-size: 17px; font-size: 17px;
font-family: "Oxygen", sans-serif; font-family: 'Oxygen', sans-serif;
transition: top 0.1s ease-in-out; transition: top 0.1s ease-in-out;
background-color: #2b2a33; background-color: #2b2a33;
border-color: #646cff; border-color: #646cff;

View File

@ -9,5 +9,5 @@ ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<Provider> <Provider>
<App /> <App />
</Provider> </Provider>
</React.StrictMode>, </React.StrictMode>
) )

File diff suppressed because it is too large Load Diff