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 7d17cfa28c
No known key found for this signature in database
GPG Key ID: B1FA75F86D28E80E
38 changed files with 2197 additions and 879 deletions

15
frontend/.prettierrc Normal file
View File

@ -0,0 +1,15 @@
{
"useTabs": false,
"tabWidth": 4,
"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:
```sh
yarn run dev
```

View File

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

View File

@ -1,13 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>FF Jitsi Rooms</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>FF Jitsi Rooms</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

View File

@ -1,28 +1,34 @@
{
"name": "jitsi-roomsv2",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.4.0",
"@fortawesome/free-regular-svg-icons": "^6.4.0",
"@fortawesome/react-fontawesome": "^0.2.0",
"@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"
},
"devDependencies": {
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.9",
"@vitejs/plugin-react": "^3.0.0",
"typescript": "^4.9.3",
"vite": "^4.0.0"
}
"name": "jitsi-roomsv2",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.4.0",
"@fortawesome/free-regular-svg-icons": "^6.4.0",
"@fortawesome/react-fontawesome": "^0.2.0",
"@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"
},
"devDependencies": {
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.9",
"@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",
"vite": "^4.0.0"
}
}

View File

@ -1,6 +1,6 @@
.App {
height: 100%;
display: flex;
flex-direction: row;
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
width: 100%;
}

View File

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

View File

@ -1,13 +1,13 @@
const ISPROD = window.location.protocol == "https:";
const JITSI_DOMAIN = "thisisnotajitsi.filefighter.de";
const USE_REMOTE_BACKEND = true;
const ISPROD = window.location.protocol == 'https:'
const JITSI_DOMAIN = 'thisisnotajitsi.filefighter.de'
const USE_REMOTE_BACKEND = true
const getWebsocketUrl = () => {
if (ISPROD) return "wss://" + window.location.host + "/ws"
if (USE_REMOTE_BACKEND) return "wss://" + "discord.filefighter.de/ws"
return "ws://" + "localhost:9160/ws"
if (ISPROD) return 'wss://' + window.location.host + '/ws'
if (USE_REMOTE_BACKEND) return 'wss://' + 'discord.filefighter.de/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) {
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);
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)
}
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
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=/";
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 };
export { getCookie, setCookie }

View File

@ -1,49 +1,45 @@
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
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
}
export interface DisplayNameChangeEvent {
id: string; // the id of the participant that changed their display name
displayname: string; // the new display name (WTF why is capilization different)
id: string // the id of the participant that changed their display name
displayname: string // the new display name (WTF why is capilization different)
}
function videoConferenceJoinedListener(
setConferenceData: (newData: ConferenceData) => void,
videoConferenceJoinedEvent: ConferenceData
setConferenceData: (newData: ConferenceData) => void,
videoConferenceJoinedEvent: ConferenceData
) {
setConferenceData(videoConferenceJoinedEvent);
setConferenceData(videoConferenceJoinedEvent)
}
function displayNameChangeListenerWrapper(
setDisplayNameChangedEvent: (newData: DisplayNameChangeEvent) => void,
displayNameChangeEvent: DisplayNameChangeEvent
setDisplayNameChangedEvent: (newData: DisplayNameChangeEvent) => void,
displayNameChangeEvent: DisplayNameChangeEvent
) {
setDisplayNameChangedEvent(displayNameChangeEvent);
setDisplayNameChangedEvent(displayNameChangeEvent)
}
function displayNameChangeListener(
conferenceData: ConferenceData | undefined,
setConferenceData: (newData: ConferenceData) => void,
displayNameChangeEvent: DisplayNameChangeEvent
conferenceData: ConferenceData | undefined,
setConferenceData: (newData: ConferenceData) => void,
displayNameChangeEvent: DisplayNameChangeEvent
) {
console.log(
"[Rooms] displayNameChangeListener",
displayNameChangeEvent,
conferenceData
);
if (conferenceData && conferenceData.id == displayNameChangeEvent.id)
setConferenceData({
...conferenceData,
displayName: displayNameChangeEvent.displayname,
} as ConferenceData);
console.log('[Rooms] displayNameChangeListener', displayNameChangeEvent, conferenceData)
if (conferenceData && conferenceData.id == displayNameChangeEvent.id)
setConferenceData({
...conferenceData,
displayName: displayNameChangeEvent.displayname,
} as ConferenceData)
}
export {
videoConferenceJoinedListener,
displayNameChangeListenerWrapper,
displayNameChangeListener,
};
videoConferenceJoinedListener,
displayNameChangeListenerWrapper,
displayNameChangeListener,
}

View File

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

View File

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

View File

@ -1,46 +1,43 @@
.chat-input{
min-width: 0;
.chat-input {
min-width: 0;
}
.chat{
background-color: #181c25;
border-radius: 8px;
padding: 0.5em 0;
width: 100%;
margin: 0 -3px;
.chat {
background-color: #181c25;
border-radius: 8px;
padding: 0.5em 0;
width: 100%;
margin: 0 -3px;
}
.chat-sender{
font-size: 0.9em;
/* color: #213547; */
.chat-sender {
font-size: 0.9em;
/* color: #213547; */
}
.chat-input-form{
display: flex;
.chat-input-form {
display: flex;
}
.chat-messages{
max-height: 220px;
overflow-y: scroll;
display: flex;
flex-direction: column-reverse;
.chat-messages {
max-height: 220px;
overflow-y: scroll;
display: flex;
flex-direction: column-reverse;
}
.chat-bubble{
background-color: #2b2a33;
overflow-wrap: break-word;
margin-bottom: 7px;
border-radius: 8px;
padding: 3px 4px ;
.chat-bubble {
background-color: #2b2a33;
overflow-wrap: break-word;
margin-bottom: 7px;
border-radius: 8px;
padding: 3px 4px;
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;
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

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

View File

@ -1,31 +1,26 @@
import { JitsiMeeting } from "@jitsi/react-sdk";
import { JITSI_DOMAIN } from "../../background/constants";
import { UserInfo } from "./types";
import curry from "just-curry-it";
import { JitsiMeeting } from '@jitsi/react-sdk'
import { JITSI_DOMAIN } from '../../background/constants'
import { UserInfo } from './types'
import curry from 'just-curry-it'
import {
ConferenceData,
displayNameChangeListener,
displayNameChangeListenerWrapper,
videoConferenceJoinedListener,
} from "../../background/jitsi/eventListeners";
import useJitsiEventCapture from "../../hooks/useJitsiEventCapture";
} from '../../background/jitsi/eventListeners'
import useJitsiEventCapture from '../../hooks/useJitsiEventCapture'
interface Props {
roomName: string;
userInfo: UserInfo;
conferenceData: ConferenceData | undefined;
setConferenceData: (newData: ConferenceData) => void;
roomName: string
userInfo: UserInfo
conferenceData: ConferenceData | undefined
setConferenceData: (newData: ConferenceData) => void
}
function JitsiEntrypoint({
roomName,
userInfo,
conferenceData,
setConferenceData,
}: Props) {
function JitsiEntrypoint({ roomName, userInfo, conferenceData, setConferenceData }: Props) {
const { setDisplayNameChangeEvent } = useJitsiEventCapture(
curry(displayNameChangeListener)(conferenceData)(setConferenceData)
);
)
return (
<JitsiMeeting
@ -42,20 +37,20 @@ function JitsiEntrypoint({
userInfo={userInfo}
onApiReady={(externalApi) => {
externalApi.addEventListener(
"videoConferenceJoined",
'videoConferenceJoined',
curry(videoConferenceJoinedListener)(setConferenceData)
);
)
externalApi.addEventListener(
"displayNameChange",
'displayNameChange',
curry(displayNameChangeListenerWrapper)(setDisplayNameChangeEvent)
);
)
}}
getIFrameRef={(iframeRef) => {
iframeRef.style.height = "100%";
iframeRef.style.width = "100%";
iframeRef.style.height = '100%'
iframeRef.style.width = '100%'
}}
/>
);
)
}
export default JitsiEntrypoint;
export default JitsiEntrypoint

View File

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

View File

@ -1,39 +1,33 @@
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";
import MeetingNameInput from "./MeetingNameInput";
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'
import MeetingNameInput from './MeetingNameInput'
interface Props {
conferenceData: ConferenceData | undefined;
setConferenceData: (newData: ConferenceData) => void;
userInfo: UserInfo;
conferenceData: ConferenceData | undefined
setConferenceData: (newData: ConferenceData) => void
userInfo: UserInfo
}
function Meeting({ conferenceData, setConferenceData, userInfo }: Props) {
const [meetingStarted, setMeetingStarted] = useMeetingStarted()
const { roomName } = useRoomName()
const [meetingStarted, setMeetingStarted] = useMeetingStarted()
const { roomName } = useRoomName()
if (meetingStarted) {
return (
<JitsiEntrypoint
conferenceData={conferenceData}
roomName={roomName}
userInfo={userInfo}
setConferenceData={setConferenceData}
/>
)
}
if (meetingStarted) {
return (
<JitsiEntrypoint
conferenceData={conferenceData}
roomName={roomName}
userInfo={userInfo}
setConferenceData={setConferenceData}
/>
);
}
return (
<MeetingNameInput
roomName={roomName}
currentUser={userInfo.displayName}
/>
);
return <MeetingNameInput roomName={roomName} currentUser={userInfo.displayName} />
}
export default Meeting;
export default Meeting

View File

@ -1,4 +1,4 @@
.meeting-name-input {
width: 100%;
text-align: center;
width: 100%;
text-align: center;
}

View File

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

View File

@ -1,39 +1,39 @@
.sidebar {
display: flex;
flex-direction: column;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding-left: 5px;
padding-right: 5px;
background-color: #121115;
display: flex;
flex-direction: column;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding-left: 5px;
padding-right: 5px;
background-color: #121115;
}
.sidebar h3 {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.sidebar-footer {
margin-top: auto;
margin-top: auto;
}
.sidebar-small {
min-width: 60px;
max-width: 60px;
min-width: 60px;
max-width: 60px;
}
.sidebar-full {
width: 220px;
width: 220px;
}
.sidebar-hidden {
width: 0;
padding: 0;
width: 0;
padding: 0;
}
.sidebar-hidden > .sidebar-footer > .sidebar-toggle {
position: absolute;
bottom: 0;
position: absolute;
bottom: 0;
}

View File

@ -1,56 +1,60 @@
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";
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;
sendMessage: Function
usersData: UsersData
sendMessage: Function
}
function Sidebar(props: Props) {
const { sidebarVisibility, toggleSidebarVisibility, sidebarToggleText } =
useSidebarVisibility();
const [_, setMeetingStarted] = useMeetingStarted()
const { updateAndSubmitRoomName: updateAndSubmitRoomName } = useRoomName();
const { sidebarVisibility, toggleSidebarVisibility, sidebarToggleText } = useSidebarVisibility()
const [_, setMeetingStarted] = useMeetingStarted()
const { updateAndSubmitRoomName: updateAndSubmitRoomName } = useRoomName()
return (
<div className={`sidebar sidebar-${sidebarVisibility}`}>
<SidebarHeader sidebarVisibility={sidebarVisibility} />
<div className="sidebar-body">
{props.usersData.roomsData.map((roomData) => {
return (
<>
<h3>
<a href="#" onClick={() => {
updateAndSubmitRoomName(roomData.roomName)
setMeetingStarted(true)
}}>
{roomData.roomName}
</a>
</h3>
{roomData.participants.map((participant) => (
<div key={participant.jid}> {participant.displayName} </div>
))}
</>
);
})}
</div>
<div>
<h3> No room</h3>
{props.usersData.usersWithOutRoom.map((user) => (
<div key={user.uuid}>{user.name}</div>
))}
</div>
<div className="sidebar-footer">
{sidebarVisibility === "full" && <Chat sendMessage={props.sendMessage} />}
<button className="sidebar-toggle" onClick={toggleSidebarVisibility}>{sidebarToggleText}</button>
</div>
</div>
);
return (
<div className={`sidebar sidebar-${sidebarVisibility}`}>
<SidebarHeader sidebarVisibility={sidebarVisibility} />
<div className="sidebar-body">
{props.usersData.roomsData.map((roomData) => {
return (
<>
<h3>
<a
href="#"
onClick={() => {
updateAndSubmitRoomName(roomData.roomName)
setMeetingStarted(true)
}}
>
{roomData.roomName}
</a>
</h3>
{roomData.participants.map((participant) => (
<div key={participant.jid}> {participant.displayName} </div>
))}
</>
)
})}
</div>
<div>
<h3> No room</h3>
{props.usersData.usersWithOutRoom.map((user) => (
<div key={user.uuid}>{user.name}</div>
))}
</div>
<div className="sidebar-footer">
{sidebarVisibility === 'full' && <Chat sendMessage={props.sendMessage} />}
<button className="sidebar-toggle" onClick={toggleSidebarVisibility}>
{sidebarToggleText}
</button>
</div>
</div>
)
}
export default Sidebar;
export default Sidebar

View File

@ -1,16 +1,16 @@
import { sidebarVisibilityOptions } from "./useSidebarVisibility";
import { sidebarVisibilityOptions } from './useSidebarVisibility'
interface props {
sidebarVisibility: sidebarVisibilityOptions;
sidebarVisibility: sidebarVisibilityOptions
}
function SidebarHeader({ sidebarVisibility }: props) {
if (sidebarVisibility == "full") {
return <h2>hi there :)</h2>;
} else if (sidebarVisibility == "small") {
return <h2>hi</h2>;
}
return <></>;
if (sidebarVisibility == 'full') {
return <h2>hi there :)</h2>
} else if (sidebarVisibility == 'small') {
return <h2>hi</h2>
}
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() {
const [sidebarVisibility, setSideBarVisibility] =
useState<sidebarVisibilityOptions>("full");
const [sidebarVisibility, setSideBarVisibility] = useState<sidebarVisibilityOptions>('full')
const sidebarToggleText = getSidebarToggleText(sidebarVisibility);
const sidebarToggleText = getSidebarToggleText(sidebarVisibility)
const toggleSidebarVisibility = useCallback(() => {
if (sidebarVisibility === "full") {
setSideBarVisibility("small");
} else if (sidebarVisibility === "small") {
setSideBarVisibility("hidden");
} else if (sidebarVisibility === "hidden") {
setSideBarVisibility("full");
}
}, [setSideBarVisibility, sidebarVisibility, sidebarToggleText]);
const toggleSidebarVisibility = useCallback(() => {
if (sidebarVisibility === 'full') {
setSideBarVisibility('small')
} else if (sidebarVisibility === 'small') {
setSideBarVisibility('hidden')
} else if (sidebarVisibility === 'hidden') {
setSideBarVisibility('full')
}
}, [setSideBarVisibility, sidebarVisibility, sidebarToggleText])
return { sidebarVisibility, toggleSidebarVisibility, sidebarToggleText };
return { sidebarVisibility, toggleSidebarVisibility, sidebarToggleText }
}
const getSidebarToggleText = (sidebarVisibility: sidebarVisibilityOptions) => {
if (sidebarVisibility === "full") return "<-";
if (sidebarVisibility === "small") return "<-";
if (sidebarVisibility === "hidden") return "->";
};
export default useSidebarVisibility;
if (sidebarVisibility === 'full') return '<-'
if (sidebarVisibility === 'small') return '<-'
if (sidebarVisibility === 'hidden') return '->'
}
export default useSidebarVisibility

View File

@ -1,21 +1,21 @@
import { atom, useAtom } from "jotai";
import { atom, useAtom } from 'jotai'
import { atomWithReducer, useReducerAtom } from 'jotai/utils'
import { User } from "../background/types/roomData";
import { User } from '../background/types/roomData'
interface ChatMessage {
content: string
sender: User
uuid: string
timestamp: number
content: string
sender: User
uuid: string
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 [chatMessages, addChatMessage] = useAtom(allChatMessagesAtom)
return { chatMesages: chatMessages, addChatMessage }
const [chatMessages, addChatMessage] = useAtom(allChatMessagesAtom)
return { chatMesages: chatMessages, addChatMessage }
}
export default useAllChat

View File

@ -1,30 +1,29 @@
import { useEffect } from "react";
import { UserInfo } from "../components/jitsi/types";
import useAllChat from "./useAllChat";
import useRoomData from "./useRoomData";
import useWebSocketConnection from "./useWebSocketConnection";
import { useEffect } from 'react'
import { UserInfo } from '../components/jitsi/types'
import useAllChat from './useAllChat'
import useRoomData from './useRoomData'
import useWebSocketConnection from './useWebSocketConnection'
function useBackendData(userInfo: UserInfo) {
console.log("[Rooms] useBackendData");
const { onMessage, sendMessage, disconnect } =
useWebSocketConnection(userInfo);
console.log('[Rooms] useBackendData')
const { onMessage, sendMessage, disconnect } = useWebSocketConnection(userInfo)
const { roomData, setRoomData } = useRoomData();
const { roomData, setRoomData } = useRoomData()
const { chatMesages, addChatMessage } = useAllChat()
const { chatMesages, addChatMessage } = useAllChat()
useEffect(() => {
onMessage((messageString) => {
console.log("[Rooms] message from ws", messageString);
const messageObject = JSON.parse(messageString);
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]);
!!messageObject.roomsData && setRoomData(messageObject)
!!messageObject.content && addChatMessage(messageObject)
return 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 { ConferenceData } from "../background/jitsi/eventListeners";
import { UserInfo } from "../components/jitsi/types";
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
sendMessage: (message: string) => void,
setUserInfo: (newData: UserInfo) => void
) {
const [conferenceData, setConferenceDataLocal] = useState<ConferenceData>();
const [conferenceData, setConferenceDataLocal] = useState<ConferenceData>()
const setConferenceData = (newData: ConferenceData) => {
console.log("[Rooms] set conferenceData", conferenceData);
if (conferenceData?.roomName !== newData.roomName) {
// We joined a meeting
sendMessage(JSON.stringify({ roomName: newData.roomName }));
const setConferenceData = (newData: ConferenceData) => {
console.log('[Rooms] set conferenceData', conferenceData)
if (conferenceData?.roomName !== newData.roomName) {
// We joined a meeting
sendMessage(JSON.stringify({ roomName: newData.roomName }))
}
if (conferenceData?.displayName !== newData.displayName) {
// We updated the username
sendMessage(JSON.stringify({ displayName: newData.displayName }))
}
setConferenceDataLocal(newData)
setUserInfo({ displayName: newData.displayName, email: '' })
}
if (conferenceData?.displayName !== newData.displayName) {
// We updated the username
sendMessage(JSON.stringify({ displayName: newData.displayName }));
}
setConferenceDataLocal(newData);
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 { DisplayNameChangeEvent } from "../background/jitsi/eventListeners";
import { useCallback, useEffect, useRef, useState } from 'react'
import { DisplayNameChangeEvent } from '../background/jitsi/eventListeners'
function useJitsiEventCapture(displayNameChangeHandler: Function) {
const [displayNameChangeEvent, setDisplayNameChangeEventLocal] =
useState<DisplayNameChangeEvent>();
const [displayNameChangeEvent, setDisplayNameChangeEventLocal] =
useState<DisplayNameChangeEvent>()
const setDisplayNameChangeEvent = useCallback(
(newValue: DisplayNameChangeEvent) => {
console.log("[Rooms] newValue", newValue, displayNameChangeEvent);
if (displayNameChangeEvent != newValue) {
setDisplayNameChangeEventLocal(newValue);
}
},
[setDisplayNameChangeEventLocal]
);
const setDisplayNameChangeEvent = useCallback(
(newValue: DisplayNameChangeEvent) => {
console.log('[Rooms] newValue', newValue, displayNameChangeEvent)
if (displayNameChangeEvent != newValue) {
setDisplayNameChangeEventLocal(newValue)
}
},
[setDisplayNameChangeEventLocal]
)
useEffect(() => {
console.log("[Rooms] displayNameChangeHandler", displayNameChangeEvent);
displayNameChangeHandler &&
displayNameChangeEvent &&
displayNameChangeHandler(displayNameChangeEvent);
}, [displayNameChangeEvent]);
useEffect(() => {
console.log('[Rooms] displayNameChangeHandler', displayNameChangeEvent)
displayNameChangeHandler &&
displayNameChangeEvent &&
displayNameChangeHandler(displayNameChangeEvent)
}, [displayNameChangeEvent])
return { setDisplayNameChangeEvent };
return { setDisplayNameChangeEvent }
}
export default useJitsiEventCapture;
export default useJitsiEventCapture

View File

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

View File

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

View File

@ -1,50 +1,44 @@
import { useCallback, useState } from "react";
import { WEBSOCKET_URL } from "../background/constants";
import { UserInfo } from "../components/jitsi/types";
import { useCallback, useState } from 'react'
import { WEBSOCKET_URL } from '../background/constants'
import { UserInfo } from '../components/jitsi/types'
const createWebSocketConnection = (userInfo: UserInfo) => {
const webSocket = new WebSocket(WEBSOCKET_URL);
console.log("[Rooms] createWebSocketConnection");
webSocket.addEventListener("open", (_: Event) =>
webSocket.send(JSON.stringify(userInfo))
);
const webSocket = new WebSocket(WEBSOCKET_URL)
console.log('[Rooms] createWebSocketConnection')
webSocket.addEventListener('open', (_: Event) => webSocket.send(JSON.stringify(userInfo)))
return webSocket;
};
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);
},
[webSocketConnection]
);
const disconnect = useCallback(webSocketConnection.close, [
webSocketConnection,
]);
return { onMessage, sendMessage, disconnect };
return webSocket
}
export default useWebSocketConnection;
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)
},
[webSocketConnection]
)
const disconnect = useCallback(webSocketConnection.close, [webSocketConnection])
return { onMessage, sendMessage, disconnect }
}
export default useWebSocketConnection

View File

@ -1,96 +1,95 @@
:root {
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 24px;
font-weight: 400;
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 24px;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #181c25;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #181c25;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
height: 100%;
margin: 0;
overflow-y: hidden;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
height: 100%;
margin: 0;
overflow-y: hidden;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
height: 100%;
width: 100%;
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
height: 100%;
width: 100%;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
outline: 4px auto -webkit-focus-ring-color;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}
#root {
height: 100%;
width: 100%;
height: 100%;
width: 100%;
}
input {
font-size: 1em;
margin: 0.3em;
border: 0;
height: 2.3em;
padding-left: 0.4em;
box-shadow: none;
box-sizing: border-box;
font-size: 17px;
font-family: "Oxygen", sans-serif;
transition: top 0.1s ease-in-out;
background-color: #2b2a33;
border-color: #646cff;
border-radius: 8px;
font-size: 1em;
margin: 0.3em;
border: 0;
height: 2.3em;
padding-left: 0.4em;
box-shadow: none;
box-sizing: border-box;
font-size: 17px;
font-family: 'Oxygen', sans-serif;
transition: top 0.1s ease-in-out;
background-color: #2b2a33;
border-color: #646cff;
border-radius: 8px;
}

View File

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

View File

@ -1,21 +1,21 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}

View File

@ -1,9 +1,9 @@
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}

View File

@ -3,5 +3,5 @@ import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
plugins: [react()],
})

File diff suppressed because it is too large Load Diff