-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
frontend: Added a chat box in the game page
Added a collapsible chat box in the gamepage, the necessary files are added in a folder Chatbox in src/library fixes #125
- Loading branch information
Showing
8 changed files
with
258 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import React, { useState, useEffect } from 'react'; | ||
import MessageList from './MessageList'; | ||
import MessageInput from './MessageInput'; | ||
import { Message as MessageType } from '../../../../backend/src/types'; | ||
import { FaComments } from 'react-icons/fa'; | ||
|
||
const Chatbox: React.FC = () => { | ||
const [messages, setMessages] = useState<MessageType[]>([]); | ||
const [isVisible, setIsVisible] = useState(false); | ||
|
||
useEffect(() => { | ||
const handleReact = (event: CustomEvent) => { | ||
const { content, emoji } = event.detail; | ||
setMessages((prevMessages) => | ||
prevMessages.map((message) => | ||
message.content === content | ||
? { | ||
...message, | ||
reactions: [ | ||
...message.reactions!, | ||
[emoji, 'Player 1'], | ||
], | ||
} | ||
: message | ||
) | ||
); | ||
}; | ||
}, []); | ||
|
||
return ( | ||
<div className="fixed bottom-5 right-5 z-50 flex flex-col items-end"> | ||
<div | ||
className={`w-80 h-96 bg-lime-400 rounded-lg overflow-hidden mb-2 transition-all duration-300 ${ | ||
isVisible | ||
? 'opacity-100 shadow-2xl' | ||
: 'opacity-0 pointer-events-none shadow-none' | ||
}`} | ||
style={{ | ||
boxShadow: isVisible | ||
? '0px 20px 30px rgba(0, 0, 0, 0.2)' | ||
: 'none', | ||
}} | ||
> | ||
<div className="flex flex-col h-full"> | ||
<div className="flex-grow overflow-y-auto"> | ||
<MessageList messages={messages} /> | ||
</div> | ||
<div className="border-t border-gray-300"> | ||
<MessageInput setMessages={setMessages} /> | ||
</div> | ||
</div> | ||
</div> | ||
<button | ||
className="p-3 bg-lime-500 text-gray-700 rounded-full focus:outline-none transform transition-transform duration-300 hover:scale-105 hover:bg-lime-400 active:bg-lime-600 shadow-md" | ||
onClick={() => setIsVisible(!isVisible)} | ||
> | ||
<FaComments className="w-7 h-7" /> | ||
</button> | ||
</div> | ||
); | ||
}; | ||
|
||
export default Chatbox; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import React, { useState } from 'react'; | ||
import EmojiPicker, { EmojiClickData } from 'emoji-picker-react'; | ||
import { Message as MessageType } from '../../../../backend/src/types'; | ||
import { FaRegSmile } from 'react-icons/fa'; | ||
|
||
interface MessageProps { | ||
message: MessageType; | ||
} | ||
|
||
const Message: React.FC<MessageProps> = ({ message }) => { | ||
const [showEmojiPicker, setShowEmojiPicker] = useState(false); | ||
|
||
const handleEmojiClick = (emojiData: EmojiClickData) => { | ||
const event = new CustomEvent('MESSAGE_REACT', { | ||
detail: { content: message.content, emoji: emojiData.emoji }, | ||
}); | ||
window.dispatchEvent(event); | ||
setShowEmojiPicker(false); | ||
}; | ||
|
||
return ( | ||
<div className="relative bg-gray-200 text-gray-800 p-2.5 mb-6 max-w-xs rounded-xl"> | ||
<div className="absolute -top-2 left-0 text-blue-900 font-kavoon text-xs px-2 py-0 rounded-t-2xl bg-gray-200"> | ||
{message.playerName} | ||
</div> | ||
<div className="break-words font-kavoon">{message.content}</div> | ||
<div className="absolute bottom-0 left-3 transform translate-x-1/4 translate-y-1/2 w-4 h-4 bg-gray-200 rotate-45"></div> | ||
<div className="mt-1 flex space-x-1 items-center relative"> | ||
{message.reactions?.map(([emoji], index) => ( | ||
<span key={index} className="text-sm"> | ||
{emoji} | ||
</span> | ||
))} | ||
<button | ||
className="text-gray-500 hover:text-gray-700" | ||
onClick={() => setShowEmojiPicker(!showEmojiPicker)} | ||
> | ||
<FaRegSmile className="w-4 h-4" /> | ||
</button> | ||
{showEmojiPicker && ( | ||
<div className="bg-white border border-gray-300 -left-6 absolute z-50"> | ||
<EmojiPicker onEmojiClick={handleEmojiClick} /> | ||
</div> | ||
)} | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default Message; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import React, { useState } from 'react'; | ||
import EmojiPicker, { EmojiClickData } from 'emoji-picker-react'; | ||
import Button from '../button'; | ||
import { Message as MessageType } from '../../../../backend/src/types'; | ||
|
||
interface MessageInputProps { | ||
setMessages: React.Dispatch<React.SetStateAction<MessageType[]>>; | ||
} | ||
|
||
const MessageInput: React.FC<MessageInputProps> = () => { | ||
const [content, setContent] = useState(''); | ||
const [showEmojiPicker, setShowEmojiPicker] = useState(false); | ||
|
||
const handleSend = async () => { | ||
if (content.trim() !== '') { | ||
const newMessage: MessageType = { | ||
content, | ||
ref: null, | ||
atMentions: [], | ||
reactions: [], | ||
playerName: 'Player 1', | ||
}; | ||
|
||
// Simulate an API call to send the message | ||
setContent(''); | ||
} | ||
}; | ||
|
||
const onEmojiClick = (emojiData: EmojiClickData) => { | ||
setContent(content + emojiData.emoji); | ||
setShowEmojiPicker(false); | ||
}; | ||
|
||
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => { | ||
if (e.ctrlKey && e.key === 'Enter') { | ||
handleSend(); | ||
} | ||
}; | ||
|
||
return ( | ||
<div className="relative flex p-1 border-t border-gray-300"> | ||
<button | ||
className="mr-1 p-2 bg-white text-gray-800 rounded-xl border-2 border-gray-500" | ||
onClick={() => setShowEmojiPicker(!showEmojiPicker)} | ||
> | ||
😊 | ||
</button> | ||
{showEmojiPicker && ( | ||
<div className="absolute bottom-14 left-0 z-50"> | ||
<EmojiPicker onEmojiClick={onEmojiClick} /> | ||
</div> | ||
)} | ||
<input | ||
type="text" | ||
value={content} | ||
onChange={(e) => setContent(e.target.value)} | ||
onKeyDown={handleKeyDown} | ||
className="flex-0.5 px-1 py-1 border-2 border-gray-500 rounded-xl font-kavoon text-sm" | ||
placeholder="Type a message..." | ||
/> | ||
<Button | ||
variant="accept" | ||
size="medium" | ||
backgroundColor="bg-gray-400" | ||
buttonSize="w-34 h-11" | ||
className="ml-2 border-2" | ||
onClick={handleSend} | ||
> | ||
Send | ||
</Button> | ||
</div> | ||
); | ||
}; | ||
|
||
// Simulate an API call to send the message | ||
|
||
export default MessageInput; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import React from 'react'; | ||
import Message from './Message'; | ||
import { Message as MessageType } from '../../../../backend/src/types'; | ||
interface MessageListProps { | ||
messages: MessageType[]; | ||
} | ||
|
||
const MessageList: React.FC<MessageListProps> = ({ messages }) => { | ||
return ( | ||
<div className="flex-l p-2"> | ||
{messages.map((message, index) => ( | ||
<Message key={index} message={message} /> | ||
))} | ||
</div> | ||
); | ||
}; | ||
|
||
export default MessageList; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters