jotai & allchat
This commit is contained in:
parent
cf1c7625be
commit
5fbba6cb98
0
frontend/dev-with-remote-backend.sh
Normal file → Executable file
0
frontend/dev-with-remote-backend.sh
Normal file → Executable file
|
@ -10,6 +10,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@jitsi/react-sdk": "^1.3.0",
|
||||
"jotai": "^2.0.3",
|
||||
"just-curry-it": "^5.3.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { Provider } from "jotai";
|
||||
import { useState } from "react";
|
||||
import "./App.css";
|
||||
import Meeting from "./components/meeting/Meeting";
|
||||
|
@ -9,7 +10,6 @@ import { useRoomName } from "./hooks/useRoomName";
|
|||
|
||||
function App() {
|
||||
const { userInfo, setUserInfo } = useLocalUser();
|
||||
const { roomName, updateRoomName, updateAndSubmitRoomName, submitRoomName } = useRoomName();
|
||||
const { roomData, sendMessage } = useBackendData(userInfo);
|
||||
const { conferenceData, setConferenceData } = useConferenceData(
|
||||
sendMessage,
|
||||
|
@ -23,20 +23,12 @@ function App() {
|
|||
return (
|
||||
<div className="App">
|
||||
<Sidebar usersData={roomData}
|
||||
updateAndSubmitRoomName={(roomName: string) => {
|
||||
updateAndSubmitRoomName(roomName)
|
||||
setMeetingStarted(true)
|
||||
}}
|
||||
sendMessage={sendMessage}
|
||||
/>
|
||||
<Meeting
|
||||
conferenceData={conferenceData}
|
||||
setConferenceData={setConferenceData}
|
||||
userInfo={userInfo}
|
||||
roomName={roomName}
|
||||
updateRoomName={updateRoomName}
|
||||
submitRoomName={submitRoomName}
|
||||
meetingStarted={meetingStarted}
|
||||
setMeetingStarted={setMeetingStarted}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -10,7 +10,13 @@ export interface Participant {
|
|||
email: string;
|
||||
}
|
||||
|
||||
export interface User {
|
||||
uuid: string,
|
||||
name: string
|
||||
|
||||
}
|
||||
|
||||
export interface UsersData {
|
||||
roomsData: RoomData[];
|
||||
usersWithOutRoom: string[];
|
||||
usersWithOutRoom: User[];
|
||||
}
|
||||
|
|
20
frontend/src/components/chat/Chat.css
Normal file
20
frontend/src/components/chat/Chat.css
Normal 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;
|
||||
}
|
||||
|
53
frontend/src/components/chat/Chat.tsx
Normal file
53
frontend/src/components/chat/Chat.tsx
Normal 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
|
|
@ -1,5 +1,6 @@
|
|||
import { useCallback, useState } from "react";
|
||||
import { ConferenceData } from "../../background/jitsi/eventListeners";
|
||||
import useMeetingStarted from "../../hooks/useMeetingStarted";
|
||||
import { useRoomName } from "../../hooks/useRoomName";
|
||||
import JitsiEntrypoint from "../jitsi/JitsiEntrypoint";
|
||||
import { UserInfo } from "../jitsi/types";
|
||||
|
@ -9,17 +10,12 @@ interface Props {
|
|||
conferenceData: ConferenceData | undefined;
|
||||
setConferenceData: (newData: ConferenceData) => void;
|
||||
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(() => {
|
||||
submitRoomName();
|
||||
setMeetingStarted(true);
|
||||
}, [submitRoomName, setMeetingStarted]);
|
||||
const [meetingStarted, setMeetingStarted] = useMeetingStarted()
|
||||
const { roomName } = useRoomName()
|
||||
|
||||
if (meetingStarted) {
|
||||
return (
|
||||
|
@ -35,8 +31,6 @@ function Meeting({ conferenceData, setConferenceData, userInfo, roomName, update
|
|||
return (
|
||||
<MeetingNameInput
|
||||
roomName={roomName}
|
||||
setName={updateRoomName}
|
||||
submit={startMeeting}
|
||||
currentUser={userInfo.displayName}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -1,26 +1,35 @@
|
|||
import { FormEventHandler } from "react";
|
||||
import useMeetingStarted from "../../hooks/useMeetingStarted";
|
||||
import { useRoomName } from "../../hooks/useRoomName";
|
||||
import "./MeetingNameInput.css";
|
||||
|
||||
function MeetingNameInput(props: {
|
||||
roomName: string;
|
||||
setName: (name: string) => void;
|
||||
submit: FormEventHandler;
|
||||
currentUser: string;
|
||||
roomName: string; currentUser: string;
|
||||
}) {
|
||||
|
||||
const { roomName, updateRoomName, updateAndSubmitRoomName, submitRoomName } = useRoomName()
|
||||
const [_, setMeetingStarted] = useMeetingStarted()
|
||||
|
||||
const onInput: React.ChangeEventHandler<HTMLInputElement> = (event) => {
|
||||
props.setName(event.target.value);
|
||||
updateRoomName(event.target.value);
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
const onSubmit: FormEventHandler<HTMLFormElement> = (event) => {
|
||||
submitRoomName()
|
||||
setMeetingStarted(true)
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
console.log("[Rooms] MeetingName input comp");
|
||||
return (
|
||||
<div className="meeting-name-input">
|
||||
<h1>Greetings {props.currentUser}</h1>
|
||||
<form onSubmit={props.submit}>
|
||||
<form onSubmit={onSubmit}>
|
||||
<input
|
||||
placeholder="Roomname"
|
||||
type="text"
|
||||
value={props.roomName}
|
||||
value={roomName}
|
||||
onChange={onInput}
|
||||
/>
|
||||
<button type="submit">Enter the adventure</button>
|
||||
|
|
|
@ -25,7 +25,11 @@
|
|||
max-width: 220px;
|
||||
}
|
||||
|
||||
.sidebar-hidden > .sidebar-footer {
|
||||
.sidebar-hidden {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.sidebar-hidden > .sidebar-footer > .sidebar-toggle {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
}
|
||||
|
|
|
@ -2,15 +2,20 @@ import SidebarHeader from "./SidebarHeader";
|
|||
import useSidebarVisibility from "./useSidebarVisibility";
|
||||
import "./Sidebar.css";
|
||||
import { UsersData } from "../../background/types/roomData";
|
||||
import useMeetingStarted from "../../hooks/useMeetingStarted";
|
||||
import { useRoomName } from "../../hooks/useRoomName";
|
||||
import Chat from "../chat/Chat";
|
||||
|
||||
interface Props {
|
||||
usersData: UsersData;
|
||||
updateAndSubmitRoomName: Function
|
||||
sendMessage: Function
|
||||
}
|
||||
|
||||
function Sidebar(props: Props) {
|
||||
const { sidebarVisibility, toggleSidebarVisibility, sidebarToggleText } =
|
||||
useSidebarVisibility();
|
||||
const [_, setMeetingStarted] = useMeetingStarted()
|
||||
const { updateAndSubmitRoomName: updateAndSubmitRoomName } = useRoomName();
|
||||
|
||||
return (
|
||||
<div className={`sidebar sidebar-${sidebarVisibility}`}>
|
||||
|
@ -21,13 +26,14 @@ function Sidebar(props: Props) {
|
|||
<>
|
||||
<h3>
|
||||
<a href="#" onClick={() => {
|
||||
props.updateAndSubmitRoomName(roomData.roomName)
|
||||
updateAndSubmitRoomName(roomData.roomName)
|
||||
setMeetingStarted(true)
|
||||
}}>
|
||||
{roomData.roomName}
|
||||
</a>
|
||||
</h3>
|
||||
{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>
|
||||
<h3> No room</h3>
|
||||
{props.usersData.usersWithOutRoom.map((username) => (
|
||||
<div>{username}</div>
|
||||
{props.usersData.usersWithOutRoom.map((user) => (
|
||||
<div key={user.uuid}>{user.name}</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="sidebar-footer">
|
||||
<button onClick={toggleSidebarVisibility}>{sidebarToggleText}</button>
|
||||
<Chat sendMessage={props.sendMessage} />
|
||||
<button className="sidebar-toggle" onClick={toggleSidebarVisibility}>{sidebarToggleText}</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
27
frontend/src/hooks/useAllChat.tsx
Normal file
27
frontend/src/hooks/useAllChat.tsx
Normal 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
|
||||
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import { useEffect } from "react";
|
||||
import { UserInfo } from "../components/jitsi/types";
|
||||
import useAllChat from "./useAllChat";
|
||||
import useRoomData from "./useRoomData";
|
||||
import useWebSocketConnection from "./useWebSocketConnection";
|
||||
|
||||
|
@ -10,12 +11,15 @@ function useBackendData(userInfo: UserInfo) {
|
|||
|
||||
const { roomData, setRoomData } = useRoomData();
|
||||
|
||||
const { chatMesages, addChatMessage } = useAllChat()
|
||||
|
||||
useEffect(() => {
|
||||
onMessage((messageString) => {
|
||||
console.log("[Rooms] message from ws", messageString);
|
||||
const messageObject = JSON.parse(messageString);
|
||||
|
||||
!!messageObject.roomsData && setRoomData(messageObject);
|
||||
!!messageObject.content && addChatMessage(messageObject);
|
||||
return disconnect;
|
||||
});
|
||||
}, [onMessage, setRoomData, disconnect]);
|
||||
|
|
7
frontend/src/hooks/useMeetingStarted.ts
Normal file
7
frontend/src/hooks/useMeetingStarted.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { atom, useAtom } from "jotai";
|
||||
|
||||
const meetingStarted = atom(false)
|
||||
|
||||
const useMeetingStarted = () => useAtom(meetingStarted);
|
||||
|
||||
export default useMeetingStarted
|
|
@ -1,7 +1,10 @@
|
|||
import { atom, useAtom } from "jotai";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
const roomNameAtom = atom(getRoomNameFromUrl())
|
||||
|
||||
function useRoomName() {
|
||||
const [roomName, setRoomName] = useState(() => getRoomNameFromUrl());
|
||||
const [roomName, setRoomName] = useAtom(roomNameAtom)
|
||||
|
||||
const updateRoomName = useCallback(
|
||||
(newName: string) => {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { Provider } from 'jotai'
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App'
|
||||
|
@ -5,6 +6,8 @@ import './index.css'
|
|||
|
||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||
<React.StrictMode>
|
||||
<Provider>
|
||||
<App />
|
||||
</Provider>
|
||||
</React.StrictMode>,
|
||||
)
|
||||
|
|
|
@ -543,6 +543,11 @@ is-core-module@^2.9.0:
|
|||
dependencies:
|
||||
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:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
|
|
Loading…
Reference in a new issue