jotai & allchat

This commit is contained in:
qvalentin 2023-04-08 15:58:11 +02:00
parent cf1c7625be
commit 5fbba6cb98
16 changed files with 172 additions and 37 deletions

0
frontend/dev-with-remote-backend.sh Normal file → Executable file
View File

View File

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

View File

@ -1,3 +1,4 @@
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";
@ -9,7 +10,6 @@ import { useRoomName } from "./hooks/useRoomName";
function App() { function App() {
const { userInfo, setUserInfo } = useLocalUser(); const { userInfo, setUserInfo } = useLocalUser();
const { roomName, updateRoomName, updateAndSubmitRoomName, submitRoomName } = useRoomName();
const { roomData, sendMessage } = useBackendData(userInfo); const { roomData, sendMessage } = useBackendData(userInfo);
const { conferenceData, setConferenceData } = useConferenceData( const { conferenceData, setConferenceData } = useConferenceData(
sendMessage, sendMessage,
@ -23,20 +23,12 @@ function App() {
return ( return (
<div className="App"> <div className="App">
<Sidebar usersData={roomData} <Sidebar usersData={roomData}
updateAndSubmitRoomName={(roomName: string) => { sendMessage={sendMessage}
updateAndSubmitRoomName(roomName)
setMeetingStarted(true)
}}
/> />
<Meeting <Meeting
conferenceData={conferenceData} conferenceData={conferenceData}
setConferenceData={setConferenceData} setConferenceData={setConferenceData}
userInfo={userInfo} userInfo={userInfo}
roomName={roomName}
updateRoomName={updateRoomName}
submitRoomName={submitRoomName}
meetingStarted={meetingStarted}
setMeetingStarted={setMeetingStarted}
/> />
</div> </div>
); );

View File

@ -10,7 +10,13 @@ export interface Participant {
email: string; email: string;
} }
export interface User {
uuid: string,
name: string
}
export interface UsersData { export interface UsersData {
roomsData: RoomData[]; roomsData: RoomData[];
usersWithOutRoom: string[]; usersWithOutRoom: User[];
} }

View File

@ -0,0 +1,20 @@
.chat-input{
max-width: 50%;
}
.chat-bubble{
background-color: #3d3d5c;
overflow-wrap: break-word;
margin-bottom: 7px;
white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
white-space: pre-wrap; /* css-3 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
white-space: -webkit-pre-wrap; /* Newer versions of Chrome/Safari*/
word-break: break-all;
white-space: normal;
}

View File

@ -0,0 +1,53 @@
import { useState } from "react";
import { FormEventHandler } from "react";
import useAllChat from "../../hooks/useAllChat";
import "./Chat.css"
interface Props {
sendMessage: Function
}
function Chat({ sendMessage }: Props) {
const [chatInput, setChatInput] = useState("")
const { chatMesages } = useAllChat()
const onInput: React.ChangeEventHandler<HTMLInputElement> = (event) => {
setChatInput(event.target.value);
event.preventDefault();
};
const onSubmit: FormEventHandler<HTMLFormElement> = (event) => {
event.preventDefault();
sendMessage(JSON.stringify({ content: chatInput }));
setChatInput("")
}
return (
<div className="">
{chatMesages.map(message => (
<div className="chat-bubble"> <span>{message.sender.name}: </span> {message.content} </div>
))
}
<form onSubmit={onSubmit}>
<input
className="chat-input"
placeholder="Say something funny :D"
type="text"
value={chatInput}
onChange={onInput}
/>
<button type="submit">Send</button>
</form>
</div>
);
}
export default Chat

View File

@ -1,5 +1,6 @@
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 { 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";
@ -9,17 +10,12 @@ interface Props {
conferenceData: ConferenceData | undefined; conferenceData: ConferenceData | undefined;
setConferenceData: (newData: ConferenceData) => void; setConferenceData: (newData: ConferenceData) => void;
userInfo: UserInfo; userInfo: UserInfo;
//@ts-ignore
roomName, updateRoomName, submitRoomName
meetingStarted: Boolean, setMeetingStarted: Function
} }
function Meeting({ conferenceData, setConferenceData, userInfo, roomName, updateRoomName, submitRoomName, meetingStarted, setMeetingStarted }: Props) { function Meeting({ conferenceData, setConferenceData, userInfo }: Props) {
const startMeeting = useCallback(() => { const [meetingStarted, setMeetingStarted] = useMeetingStarted()
submitRoomName(); const { roomName } = useRoomName()
setMeetingStarted(true);
}, [submitRoomName, setMeetingStarted]);
if (meetingStarted) { if (meetingStarted) {
return ( return (
@ -35,8 +31,6 @@ function Meeting({ conferenceData, setConferenceData, userInfo, roomName, update
return ( return (
<MeetingNameInput <MeetingNameInput
roomName={roomName} roomName={roomName}
setName={updateRoomName}
submit={startMeeting}
currentUser={userInfo.displayName} currentUser={userInfo.displayName}
/> />
); );

View File

@ -1,26 +1,35 @@
import { FormEventHandler } from "react"; import { FormEventHandler } from "react";
import useMeetingStarted from "../../hooks/useMeetingStarted";
import { useRoomName } from "../../hooks/useRoomName";
import "./MeetingNameInput.css"; import "./MeetingNameInput.css";
function MeetingNameInput(props: { function MeetingNameInput(props: {
roomName: string; roomName: string; currentUser: string;
setName: (name: string) => void;
submit: FormEventHandler;
currentUser: string;
}) { }) {
const { roomName, updateRoomName, updateAndSubmitRoomName, submitRoomName } = useRoomName()
const [_, setMeetingStarted] = useMeetingStarted()
const onInput: React.ChangeEventHandler<HTMLInputElement> = (event) => { const onInput: React.ChangeEventHandler<HTMLInputElement> = (event) => {
props.setName(event.target.value); updateRoomName(event.target.value);
event.preventDefault(); event.preventDefault();
}; };
const onSubmit: FormEventHandler<HTMLFormElement> = (event) => {
submitRoomName()
setMeetingStarted(true)
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={props.submit}> <form onSubmit={onSubmit}>
<input <input
placeholder="Roomname" placeholder="Roomname"
type="text" type="text"
value={props.roomName} value={roomName}
onChange={onInput} onChange={onInput}
/> />
<button type="submit">Enter the adventure</button> <button type="submit">Enter the adventure</button>

View File

@ -25,7 +25,11 @@
max-width: 220px; max-width: 220px;
} }
.sidebar-hidden > .sidebar-footer { .sidebar-hidden {
width: 0;
}
.sidebar-hidden > .sidebar-footer > .sidebar-toggle {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
} }

View File

@ -2,15 +2,20 @@ 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 { useRoomName } from "../../hooks/useRoomName";
import Chat from "../chat/Chat";
interface Props { interface Props {
usersData: UsersData; usersData: UsersData;
updateAndSubmitRoomName: 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 { updateAndSubmitRoomName: updateAndSubmitRoomName } = useRoomName();
return ( return (
<div className={`sidebar sidebar-${sidebarVisibility}`}> <div className={`sidebar sidebar-${sidebarVisibility}`}>
@ -21,13 +26,14 @@ function Sidebar(props: Props) {
<> <>
<h3> <h3>
<a href="#" onClick={() => { <a href="#" onClick={() => {
props.updateAndSubmitRoomName(roomData.roomName) updateAndSubmitRoomName(roomData.roomName)
setMeetingStarted(true)
}}> }}>
{roomData.roomName} {roomData.roomName}
</a> </a>
</h3> </h3>
{roomData.participants.map((participant) => ( {roomData.participants.map((participant) => (
<div> {participant.displayName} </div> <div key={participant.jid}> {participant.displayName} </div>
))} ))}
</> </>
); );
@ -35,12 +41,13 @@ function Sidebar(props: Props) {
</div> </div>
<div> <div>
<h3> No room</h3> <h3> No room</h3>
{props.usersData.usersWithOutRoom.map((username) => ( {props.usersData.usersWithOutRoom.map((user) => (
<div>{username}</div> <div key={user.uuid}>{user.name}</div>
))} ))}
</div> </div>
<div className="sidebar-footer"> <div className="sidebar-footer">
<button onClick={toggleSidebarVisibility}>{sidebarToggleText}</button> <Chat sendMessage={props.sendMessage} />
<button className="sidebar-toggle" onClick={toggleSidebarVisibility}>{sidebarToggleText}</button>
</div> </div>
</div> </div>
); );

View File

@ -0,0 +1,27 @@
import { atom, useAtom } from "jotai";
import { atomWithReducer, useReducerAtom } from 'jotai/utils'
import { User } from "../background/types/roomData";
interface ChatMessage {
content: string
sender: User
}
export const allChatMessagesAtom = atomWithReducer([], (list: ChatMessage[], item: ChatMessage) => list.concat(item))
const useAllChat = () => {
const [chatMessages, addChatMessage] = useAtom(allChatMessagesAtom)
return { chatMesages: chatMessages, addChatMessage }
}
export default useAllChat

View File

@ -1,5 +1,6 @@
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 useRoomData from "./useRoomData"; import useRoomData from "./useRoomData";
import useWebSocketConnection from "./useWebSocketConnection"; import useWebSocketConnection from "./useWebSocketConnection";
@ -10,12 +11,15 @@ function useBackendData(userInfo: UserInfo) {
const { roomData, setRoomData } = useRoomData(); const { roomData, setRoomData } = useRoomData();
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);
return disconnect; return disconnect;
}); });
}, [onMessage, setRoomData, disconnect]); }, [onMessage, setRoomData, disconnect]);

View File

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

View File

@ -1,7 +1,10 @@
import { atom, useAtom } from "jotai";
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
const roomNameAtom = atom(getRoomNameFromUrl())
function useRoomName() { function useRoomName() {
const [roomName, setRoomName] = useState(() => getRoomNameFromUrl()); const [roomName, setRoomName] = useAtom(roomNameAtom)
const updateRoomName = useCallback( const updateRoomName = useCallback(
(newName: string) => { (newName: string) => {

View File

@ -1,3 +1,4 @@
import { Provider } from 'jotai'
import React from 'react' import React from 'react'
import ReactDOM from 'react-dom/client' import ReactDOM from 'react-dom/client'
import App from './App' import App from './App'
@ -5,6 +6,8 @@ import './index.css'
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode> <React.StrictMode>
<Provider>
<App /> <App />
</Provider>
</React.StrictMode>, </React.StrictMode>,
) )

View File

@ -543,6 +543,11 @@ is-core-module@^2.9.0:
dependencies: dependencies:
has "^1.0.3" has "^1.0.3"
jotai@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/jotai/-/jotai-2.0.3.tgz#3b67cda9f6d5feb70a14db0b842a9873aacda8b5"
integrity sha512-MMjhSPAL3RoeZD9WbObufRT2quThEAEknHHridf2ma8Ml7ZVQmUiHk0ssdbR3F0h3kcwhYqSGJ59OjhPge7RRg==
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"