Skip to content

Commit b4e4832

Browse files
authored
Merge pull request #208 from boostcamp-2020/dev
v0.0.7 배포
2 parents 4018bee + 2370261 commit b4e4832

File tree

27 files changed

+292
-106
lines changed

27 files changed

+292
-106
lines changed

backend/chatServer.js

+14-10
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { createServer } from 'http'
44
import createChatServer from 'socket.io'
55
import { createChatMessage, createReplyMessage } from './service/chat'
66
import { addReaction, removeReaction } from './service/reaction'
7+
import { SOCKET_EVENT } from './util/constant'
78
dotenv()
89

910
const server = createServer(express())
@@ -20,31 +21,34 @@ namespace.use((socket, next) => {
2021
namespace.on('connection', socket => {
2122
const { workspaceUserInfoId } = socket.handshake.query
2223
socket.join(workspaceUserInfoId)
23-
socket.on('invite channel', ({ channelId, origin, newMember }) => {
24+
socket.on(SOCKET_EVENT.INVITE_CHANNEL, ({ channelId, origin, newMember }) => {
2425
origin
2526
.concat(newMember)
2627
.forEach(member =>
27-
namespace.in(member).emit('invited channel', { channelId, newMember }),
28+
namespace
29+
.in(member)
30+
.emit(SOCKET_EVENT.INVITED_CHANNEL, { channelId, newMember }),
2831
)
2932
})
30-
socket.on('new message', async data => {
33+
socket.on(SOCKET_EVENT.NEW_MESSAGE, async data => {
3134
const { contents, channelId, file } = data
3235
const { data: result } = await createChatMessage({
3336
creator: workspaceUserInfoId,
3437
channelId,
3538
contents,
3639
file,
3740
})
38-
namespace.in(channelId).emit('new message', {
41+
namespace.in(channelId).emit(SOCKET_EVENT.NEW_MESSAGE, {
3942
message: {
4043
...data,
4144
_id: result._id,
4245
createdAt: result.createdAt,
4346
reactions: [],
47+
reply: [],
4448
},
4549
})
4650
})
47-
socket.on('new reply', async data => {
51+
socket.on(SOCKET_EVENT.NEW_REPLY, async data => {
4852
const { contents, channelId, parentId, file } = data
4953
const { data: result } = await createReplyMessage({
5054
creator: workspaceUserInfoId,
@@ -53,7 +57,7 @@ namespace.on('connection', socket => {
5357
parentId,
5458
file,
5559
})
56-
namespace.in(channelId).emit('new reply', {
60+
namespace.in(channelId).emit(SOCKET_EVENT.NEW_REPLY, {
5761
message: {
5862
...data,
5963
_id: result._id,
@@ -63,7 +67,7 @@ namespace.on('connection', socket => {
6367
},
6468
})
6569
})
66-
socket.on('update reaction', async data => {
70+
socket.on(SOCKET_EVENT.UPDAETE_REACTION, async data => {
6771
const { emoji, chatId, userInfo, channelId, type, parentId } = data
6872
//1 = add, 0 = remove
6973
const result =
@@ -79,7 +83,7 @@ namespace.on('connection', socket => {
7983
emoticon: emoji,
8084
})
8185

82-
namespace.in(channelId).emit('update reaction', {
86+
namespace.in(channelId).emit(SOCKET_EVENT.UPDAETE_REACTION, {
8387
reaction: {
8488
chatId: chatId,
8589
emoji: emoji,
@@ -91,10 +95,10 @@ namespace.on('connection', socket => {
9195
})
9296
})
9397

94-
socket.on('join-room', (channelList = []) => {
98+
socket.on(SOCKET_EVENT.JOIN_ROOM, (channelList = []) => {
9599
socket.join(channelList)
96100
})
97-
socket.on('leave-room', roomId => {
101+
socket.on(SOCKET_EVENT.LEAVE_ROOM, roomId => {
98102
socket.leave(roomId)
99103
})
100104
})

backend/model/Channel.js

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ const channelSchema = mongoose.Schema(
3333
},
3434
{ timestamps: true },
3535
)
36+
channelSchema.index({ workspaceId: 1, channelType: 1 })
3637

3738
channelSchema.statics.getChannelBrowserData = async function (
3839
workspaceId,

backend/model/ChannelConfig.js

+3
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ const channelConfigSchema = mongoose.Schema(
2929
{ timestamps: true },
3030
)
3131

32+
channelConfigSchema.index({ channelId: 1, workspaceUserInfoId: 1 })
33+
channelConfigSchema.index({ workspaceUserInfoId: 1 })
34+
3235
channelConfigSchema.statics.getChannelHeaderInfo = async function (
3336
channelId,
3437
workspaceUserInfoId,

backend/model/Chat.js

+6
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ const chatSchema = mongoose.Schema(
3333
},
3434
{ timestamps: true },
3535
)
36+
37+
chatSchema.index({ createdAt: 1, channel: 1 })
38+
chatSchema.index({ channel: 1, parentId: 1 })
39+
chatSchema.index({ pinned: 1 })
40+
chatSchema.index({ parentId: 1 })
41+
3642
chatSchema.statics.getChatMessages = ({ channelId, currentCursor, fromDate }) =>
3743
Chat.aggregate([
3844
{ $sort: { createdAt: -1 } },

backend/model/Reaction.js

+3
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,8 @@ const reactionSchema = mongoose.Schema({
1414
type: String,
1515
},
1616
})
17+
reactionSchema.index({ workspaceUserInfoId: 1, chatId: 1 })
18+
reactionSchema.index({ chatId: 1 })
19+
1720
const Reaction = mongoose.model('Reaction', reactionSchema)
1821
module.exports = { Reaction }

backend/model/Workspace.js

+2
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,7 @@ const workspaceSchema = mongoose.Schema(
2020
},
2121
{ timestamps: true },
2222
)
23+
workspaceSchema.index({ name: 1 })
24+
2325
const Workspace = mongoose.model('Workspace', workspaceSchema)
2426
module.exports = { Workspace }

backend/model/WorkspaceUserInfo.js

+3
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ const workspaceUserInfoSchema = mongoose.Schema(
5252
{ timestamps: true },
5353
)
5454

55+
workspaceUserInfoSchema.index({ workspaceId: 1, userId: 1 })
56+
workspaceUserInfoSchema.index({ userId: 1 })
57+
5558
workspaceUserInfoSchema.statics.getWorkspaceUserInfo = async function (
5659
workspaceUserInfoId,
5760
) {

backend/util/constant.js

+9
Original file line numberDiff line numberDiff line change
@@ -1 +1,10 @@
11
export const MAX_CHAT_MESSAGE = 20
2+
export const SOCKET_EVENT = {
3+
INVITE_CHANNEL: 'invite channel',
4+
INVITED_CHANNEL: 'invited channel',
5+
NEW_MESSAGE: 'new message',
6+
NEW_REPLY: 'new reply',
7+
UPDAETE_REACTION: 'update reaction',
8+
JOIN_ROOM: 'join room',
9+
LEAVE_ROOM: 'leave room',
10+
}

frontend/package-lock.json

+10-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"immer": "^8.0.0",
2323
"qs": "^6.9.4",
2424
"react": "^17.0.1",
25+
"react-copy-to-clipboard": "^5.0.2",
2526
"react-dom": "^17.0.1",
2627
"react-router-dom": "^5.2.0",
2728
"react-scripts": "4.0.0",

frontend/src/api/user.js

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import Request from '../util/request'
2+
3+
export const signOut = async () => {
4+
const data = await Request.DELETE('/api/user/sign-out')
5+
return data
6+
}

frontend/src/constant/index.js

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export const SOCKET_EVENT = {
2+
INVITE_CHANNEL: 'invite channel',
3+
INVITED_CHANNEL: 'invited channel',
4+
NEW_MESSAGE: 'new message',
5+
NEW_REPLY: 'new reply',
6+
UPDAETE_REACTION: 'update reaction',
7+
JOIN_ROOM: 'join room',
8+
LEAVE_ROOM: 'leave room',
9+
}

frontend/src/container/ChannelHeader/ChannelHeader.js

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react'
22
import styled from 'styled-components'
3-
import { useSetRecoilState } from 'recoil'
3+
import { useSetRecoilState, useRecoilValue } from 'recoil'
44

55
import Icon from '../../presenter/Icon'
66
import { ADDUSER, INFOCIRCLE } from '../../constant/icon'
@@ -9,15 +9,14 @@ import ChannelStarBtn from '../ChannelStarBtn'
99
import ChannelPinBtn from '../../presenter/ChannelPinBtn'
1010
import ChannelTopicBtn from '../../presenter/ChannelTopicBtn'
1111
import ChannelMemberThumbnail from '../../presenter/ChannelMemberThumbnail'
12-
import { modalRecoil } from '../../store'
12+
import { modalRecoil, currentChannelInfoRecoil } from '../../store'
1313
import InviteUserToChannelModal from '../Modal/InviteUserToChannelModal'
1414
import { COLOR } from '../../constant/style'
15-
import useChannelInfo from '../../hooks/useChannelInfo'
1615
import { isEmpty } from '../../util'
1716

1817
function ChannelHeader() {
1918
const setModal = useSetRecoilState(modalRecoil)
20-
const [channelInfo] = useChannelInfo()
19+
const channelInfo = useRecoilValue(currentChannelInfoRecoil)
2120
const openAddUserModal = () => {
2221
setModal(<InviteUserToChannelModal handleClose={() => setModal(null)} />)
2322
}

frontend/src/container/ChatMessage/ChatMessage.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ import ChatContent from '../../presenter/ChatContent'
77
import ThreadReactionList from '../../presenter/ThreadReactionList'
88
import ActionBar from '../ActionBar'
99
import ViewThreadButton from '../../presenter/Button/ViewThreadButton'
10-
import { isEmpty, isImage } from '../../util'
10+
import { isEmpty } from '../../util'
1111
import { SIZE, COLOR } from '../../constant/style'
1212
import { workspaceRecoil, socketRecoil } from '../../store'
1313
import FilePreview from '../FilePreview'
14-
14+
import { SOCKET_EVENT } from '../../constant'
1515
const ChatMessage = forwardRef(
1616
(
1717
{
@@ -45,7 +45,7 @@ const ChatMessage = forwardRef(
4545
displayName: workspaceUserInfo.displayName,
4646
},
4747
}
48-
socket.emit('update reaction', reaction)
48+
socket.emit(SOCKET_EVENT.UPDAETE_REACTION, reaction)
4949
}
5050

5151
const updateReactionHandler = emoji => {

frontend/src/container/ChatRoom/ChatRoom.js

+45-13
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
11
import React, { useEffect, useState, useRef, useCallback } from 'react'
22
import styled from 'styled-components'
33
import { useParams } from 'react-router-dom'
4-
import { useRecoilValue } from 'recoil'
4+
import { useRecoilState, useRecoilValue } from 'recoil'
55
import ChatMessage from '../ChatMessage'
66
import { COLOR } from '../../constant/style'
77
import { getChatMessage } from '../../api/chat'
88
import MessageEditor from '../MessageEditor/MessageEditor'
9-
import { workspaceRecoil, socketRecoil } from '../../store'
9+
import {
10+
workspaceRecoil,
11+
socketRecoil,
12+
currentChannelInfoRecoil,
13+
} from '../../store'
1014
import ChannelHeader from '../ChannelHeader'
1115
import { isEmpty } from '../../util'
1216
import { hasMyReaction, chageReactionState } from '../../util/reactionUpdate'
13-
import useChannelInfo from '../../hooks/useChannelInfo'
1417
import Icon from '../../presenter/Icon'
1518
import { ArrowDown } from '../../constant/icon'
19+
import { getChannelHeaderInfo } from '../../api/channel'
20+
import { SOCKET_EVENT } from '../../constant'
1621

1722
const ChatRoom = ({ width }) => {
1823
const viewport = useRef(null)
@@ -23,7 +28,7 @@ const ChatRoom = ({ width }) => {
2328
const isAllMessageFetched = useRef(false)
2429
const isReading = useRef(false)
2530
const workspaceUserInfo = useRecoilValue(workspaceRecoil)
26-
const [channelInfo] = useChannelInfo()
31+
const [channelInfo, setChannelInfo] = useRecoilState(currentChannelInfoRecoil)
2732
const { workspaceId, channelId } = useParams()
2833
const params = useParams()
2934
const socket = useRecoilValue(socketRecoil)
@@ -52,6 +57,20 @@ const ChatRoom = ({ width }) => {
5257
[messages],
5358
)
5459

60+
const updateChannelInfo = useCallback(async () => {
61+
if (workspaceUserInfo && channelId)
62+
setChannelInfo(
63+
await getChannelHeaderInfo({
64+
workspaceUserInfoId: workspaceUserInfo._id,
65+
channelId,
66+
}),
67+
)
68+
}, [channelId, workspaceUserInfo, setChannelInfo])
69+
70+
useEffect(() => {
71+
updateChannelInfo()
72+
}, [channelId, workspaceUserInfo, updateChannelInfo])
73+
5574
useEffect(() => {
5675
setMessages([])
5776
isLoading.current = false
@@ -74,7 +93,7 @@ const ChatRoom = ({ width }) => {
7493
profileUrl: workspaceUserInfo.profileUrl,
7594
},
7695
}
77-
socket.emit('new message', chat)
96+
socket.emit(SOCKET_EVENT.NEW_MESSAGE, chat)
7897
}
7998

8099
useEffect(() => {
@@ -83,17 +102,18 @@ const ChatRoom = ({ width }) => {
83102

84103
useEffect(() => {
85104
if (socket) {
86-
socket.on('new message', ({ message }) => {
105+
socket.on(SOCKET_EVENT.NEW_MESSAGE, ({ message }) => {
87106
if (message.channelId === channelId) {
88107
setMessages(messages => [
89108
...messages,
90109
...hasMyReaction([message], workspaceUserInfo),
91110
])
92-
if (isReading.current && document.hasFocus()) {
111+
if (message.userInfo._id === workspaceUserInfo._id) {
93112
setHasUnreadMessage(false)
94113
scrollTo()
95-
} else if (message.userInfo._id !== workspaceUserInfo._id)
114+
} else if (!isReading.current && !document.hasFocus()) {
96115
setHasUnreadMessage(true)
116+
}
97117
}
98118

99119
if (document.hidden) {
@@ -104,17 +124,30 @@ const ChatRoom = ({ width }) => {
104124

105125
if (message.userInfo._id === workspaceUserInfo._id) scrollTo()
106126
})
107-
socket.on('update reaction', ({ reaction }) => {
108-
setMessages(messages => chageReactionState(messages, reaction))
127+
socket.on(SOCKET_EVENT.NEW_REPLY, ({ message }) => {
128+
setMessages(messages =>
129+
messages.map(target =>
130+
target._id === message.parentId
131+
? { ...target, reply: [...target.reply, message] }
132+
: target,
133+
),
134+
)
135+
})
136+
socket.on(SOCKET_EVENT.UPDAETE_REACTION, ({ reaction }) => {
137+
setMessages(messages =>
138+
chageReactionState(messages, reaction, workspaceUserInfo),
139+
)
109140
})
110141
}
111142
return () => {
112143
if (socket) {
113-
socket.off('new message')
114-
socket.off('update reaction')
144+
socket.off(SOCKET_EVENT.NEW_REPLY)
145+
socket.off(SOCKET_EVENT.NEW_MESSAGE)
146+
socket.off(SOCKET_EVENT.UPDAETE_REACTION)
115147
}
116148
}
117149
}, [socket, channelId, document.hidden, params])
150+
118151
useEffect(() => {
119152
const handleIntersection = (entries, observer) => {
120153
entries.forEach(entry => {
@@ -225,7 +258,6 @@ const UnreadMessage = styled.div`
225258
background-color: ${COLOR.STARBLUE};
226259
color: ${COLOR.WHITE};
227260
width: 170px;
228-
height: 50px;
229261
margin-left: auto;
230262
margin-right: auto;
231263
position: sticky;

0 commit comments

Comments
 (0)