Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/delete chat #143

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
initial commit for delete front
hbin0701 committed May 17, 2023
commit 6970ecc0ca35082bab9896adf98f807185506703
1 change: 1 addition & 0 deletions src/common/enums.ts
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ export enum MessageEnum {
MESSAGE = 'message',
VOTESTART = 'votestart',
VOTEEND = 'voteend',
MSGDELETED = 'msgdeleted',
}

export enum AgendaStatus {
1 change: 1 addition & 0 deletions src/common/theme.ts
Original file line number Diff line number Diff line change
@@ -64,6 +64,7 @@ export const lightTheme = {
MSG_CONTENT_USER_TEXT: '#000000',
MSG_CONTENT_AWAY_BACK: '#f7f6f3',
MSG_CONTENT_AWAY_TEXT: '#000000',
MSG_DELETED: '#dddddd',
MSG_USERNAME: '#000000',

CHATBOX_INPUTGROUP_TEXTAREA: '#444444',
66 changes: 61 additions & 5 deletions src/components/ChatBox/ChatBox.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import React, { useState, useEffect, useCallback, useRef } from 'react';
import { MemberState, MessageEnum } from '@/common/enums';
import { Agenda, MessageType } from '@/common/types';
import Modal from './Modal';
import {
ChatBoxScrollable,
MessageContainer,
MessageUsername,
MessageContent,
MessageDate,
MessageDelete,
ChatBoxInputGroup,
ChatBoxInternalContainer,
ChatBoxExternalContainer,
@@ -30,6 +32,7 @@ interface Props {

interface MessageProps {
message: MessageType;
socket: SocketIOClient.Socket;
}

const messageParse = (message: string) => {
@@ -64,7 +67,7 @@ const parseURL = content => {
);
};

const Message: React.FC<MessageProps> = ({ message }) => {
const Message: React.FC<MessageProps> = ({ message, socket }) => {
const parseDate = (date: string) => {
const splitted = date.split('T');
const day = splitted[0];
@@ -81,13 +84,17 @@ const Message: React.FC<MessageProps> = ({ message }) => {
return message.message;
case MessageEnum.VOTEEND:
return message.message;
case MessageEnum.MSGDELETED:
return '삭제된 메시지입니다. ';
}
})();

const justification = (() => {
switch (message.type) {
case MessageEnum.MESSAGE:
return message.username ? 'start' : 'end';
case MessageEnum.MSGDELETED:
return message.username ? 'start' : 'end';
case MessageEnum.VOTESTART:
case MessageEnum.VOTEEND:
return 'around';
@@ -97,14 +104,56 @@ const Message: React.FC<MessageProps> = ({ message }) => {
})();

const date =
message.type === MessageEnum.MESSAGE ? parseDate(message.date) : null;
message.type === MessageEnum.MESSAGE || MessageEnum.MSGDELETED
? parseDate(message.date)
: null;

const [isModalOn, setIsModalOn] = useState(false);
const containerRef = useRef(null);

const handleMouseEnter = useCallback(() => {
setIsModalOn(true);
}, []);

const handleMouseLeave = useCallback(() => {
setTimeout(() => {
setIsModalOn(false);
}, 3000);
}, []);

useEffect(() => {
const handleClickOutside = event => {
if (
containerRef.current &&
!containerRef.current.contains(event.target)
) {
setIsModalOn(false);
}
};

window.addEventListener('click', handleClickOutside);
return () => {
window.removeEventListener('click', handleClickOutside);
};
}, []);

// const [modalProps, setModalProps] = useState{{ "modal": null })

return (
<MessageContainer justification={justification}>
<MessageContainer
ref={containerRef}
onMouseDown={handleMouseEnter}
onMouseLeave={handleMouseLeave}
justification={justification}
>
{message.username && (
<MessageUsername>{message.username}</MessageUsername>
)}
<MessageContent username={message.username} messageType={message.type}>
<MessageContent
isModalOn={isModalOn}
username={message.username}
messageType={message.type}
>
<div style={{ lineHeight: '140%' }}>{parseURL(content)}</div>
{date && (
<MessageDate justification={justification}>
@@ -113,6 +162,9 @@ const Message: React.FC<MessageProps> = ({ message }) => {
</MessageDate>
)}
</MessageContent>
{message.type === MessageEnum.MESSAGE && !message.username && (
<Modal isVisible={isModalOn} message={message} socket={socket} />
)}
</MessageContainer>
);
};
@@ -144,8 +196,11 @@ const ChatBox: React.FC<Props> = ({ socket }) => {

/* Initialize socket events for 'enter', 'members', 'out', 'vacant', 'message'. */
useEffect(() => {
// debugger;
// console.log("members", _members);
let broadcastId: number;
socket.on('chat:enter', (username: string) => {
console.log('members', _members);
setMembers(members => [
...members,
{ sparcsId: username, state: MemberState.ONLINE },
@@ -283,6 +338,7 @@ const ChatBox: React.FC<Props> = ({ socket }) => {
};

const sendMessage = () => {
console.log('members', _members);
const trimMsg = message.trim();
if (trimMsg === '') return;
const msgObject = { message: trimMsg, date: currentTime() };
@@ -384,7 +440,7 @@ const ChatBox: React.FC<Props> = ({ socket }) => {
</ChatBroadcast>
<ChatBoxScrollable>
{chatlog.map((chat, idx) => (
<Message key={idx} message={chat} />
<Message key={idx} message={chat} socket={socket} />
))}
{loading && <p>Loading...</p>}
{error && <p>Error...</p>}
40 changes: 40 additions & 0 deletions src/components/ChatBox/Modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React, { useEffect } from 'react';
import { ModalBox, ModalElement } from './styled';
import { FaTimes } from 'react-icons/fa';
import { IoMdCreate } from 'react-icons/io';
import { Agenda, MessageType } from '@/common/types';
import { MemberState, MessageEnum } from '@/common/enums';

interface ModalProps {
isVisible: boolean;
message: MessageType;
socket: SocketIOClient.Socket; // Add socket prop
}

const Modal: React.FC<ModalProps> = ({ isVisible, message, socket }) => {
const handleDelete = () => {
// Arbitrary name
socket.emit('delete:message', message);
message.type = MessageEnum.MSGDELETED;
};

useEffect(() => {
socket.on('delete:message', (receivedMessage: MessageType) => {
message.type = MessageEnum.MSGDELETED;
debugger;
});
}, []);

return (
<ModalBox isVisible={isVisible}>
<ModalElement onClick={handleDelete}>
<FaTimes style={{ marginRight: '8px' }} /> 삭제
</ModalElement>
<ModalElement>
<IoMdCreate style={{ marginRight: '8px' }} /> 수정
</ModalElement>
</ModalBox>
);
};

export default Modal;
89 changes: 81 additions & 8 deletions src/components/ChatBox/styled.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,71 @@
import { MessageEnum } from '@/common/enums';
import styled from 'styled-components';

interface ModalBoxProps {
isVisible: boolean;
}

interface JustificationProps {
justification: string;
}

export const ModalBox = styled.div<ModalBoxProps>`
position: absolute;
bottom: 100%;
margin-bottom: 2px;
right: 3 %;
background-color: white;
font-size: 12px;
box-sizing: border-box;
border: 1px solid #d9d9d9; /* light gray border color */
border-radius: 10px;
margin-top: 0;
width: 80;
height: 100 %;
max-height: 800px;
display: flex;
flex-direction: column;
overflow: auto;
z-index: 2;
color: black;
opacity: ${props =>
props.isVisible ? 1 : 0} !important; /* Show/hide the modal box */
visibility: ${props =>
props.isVisible ? 'visible' : 'hidden'}; /* Show/hide the modal box */
box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.2); /* Add a pretty shadow */
transition: ${props =>
props.isVisible
? 'all 0.3s ease-in-out'
: 'none'}; /* Add a transition effect or none */
`;

export const ModalElement = styled.div`
display: flex;
height: 100%;
flex: 1;

padding-left: 10px;
padding: 8px 12px;
justify-content: space-between;
transition: background-color 0.3s ease;

&::before {
content: '';
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
height: 1px;
width: 100%;
background-color: #d9d9d9; /* light gray color */
}

&:hover {
cursor: pointer;
background-color: #f7f7f7;
}
`;

export const ChatBoxExternalContainer = styled.div`
display: flex;
flex-direction: column;
@@ -104,10 +169,6 @@ export const ChatBoxInputGroup = styled.div`
}
`;

interface JustificationProps {
justification: string;
}

export const MessageContainer = styled.div`
display: flex;
justify-content: ${(props: JustificationProps) =>
@@ -120,11 +181,10 @@ export const MessageContainer = styled.div`
: props.justification === 'start'
? '35px'
: '10px'};
${(props: JustificationProps) =>
props.justification === 'around' && `font-weight: bold`};
${props => props.justification === 'around' && `font-weight: bold`};
position: relative;
max-width: 100%;

opacity: 1;
-webkit-animation: bound 1s linear;

@keyframes bound {
@@ -163,13 +223,16 @@ export const MessageUsername = styled.span`
export const MessageContent = styled.div<{
username: string;
messageType: MessageEnum;
isModalOn: boolean;
}>`
background-color: ${props =>
props.messageType === MessageEnum.VOTESTART ||
props.messageType === MessageEnum.VOTEEND
? props.theme.MSG_CONTENT_USER_BACK
: props.username
? props.theme.MSG_CONTENT_AWAY_BACK
: props.messageType === MessageEnum.MSGDELETED
? props.theme.MSG_DELETED
: props.theme
.MSG_CONTENT_USER_BACK}; //109, 110 번째 줄과의 통일 성을 위해서 수정
border-radius: 10px;
@@ -180,12 +243,22 @@ export const MessageContent = styled.div<{
position: relative;
word-break: break-word;
white-space: pre-wrap;
opacity: ${props => (props.isModalOn ? 0.4 : 1)};
transition: opacity 0.2s ease-in-out;
color: ${props =>
props.username
? props.theme.MSG_CONTENT_AWAY_TEXT
: props.theme.MSG_CONTENT_USER_TEXT};
`;

export const MessageDelete = styled.button`
position: relative;
font-size: 6px;
background-color: transparent;
border: none;
cursor: pointer;
`;

export const MessageDate = styled.div`
position: absolute;
display: flex;
@@ -199,7 +272,7 @@ export const MessageDate = styled.div`
align-items: flex-start;
`
: `
right: calc(100% + 8px);
right: calc(100% + 8px);
align-items: flex-end;
`}

4 changes: 2 additions & 2 deletions src/utils/auth.ts
Original file line number Diff line number Diff line change
@@ -59,7 +59,7 @@ export const requestUserInfo = async (
};

const response: TokenResponse = await auth
.get(`${AUTH_URL}/api/auth/login/callback?code=${code}&state=${state}`)
.get(`/auth/login/callback?code=${code}&state=${state}`)
.catch(() => {
throw new Error('Login callback error!');
});
@@ -71,7 +71,7 @@ export const requestRedirectURL = async (): Promise<string> => {
type RedirectURLResponse = { data: { url: string } };

const response: RedirectURLResponse = await auth
.post(`${AUTH_URL}/api/auth/login`)
.post(`/auth/login`)
.catch(() => {
throw new Error('Redirect URL fetch error!');
});