Skip to content

Commit 275f568

Browse files
authored
Feat/#300-K: 투표 블럭 서비스 로직 연동
2 parents 4387f66 + 1baf875 commit 275f568

File tree

14 files changed

+230
-173
lines changed

14 files changed

+230
-173
lines changed

@wabinar/constants/socket-message.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@ export const MOM_EVENT = {
2323
};
2424

2525
export const BLOCK_EVENT = {
26-
INIT: 'init-block',
2726
LOAD_TYPE: 'load-type',
2827
UPDATE_TYPE: 'update-type',
28+
INIT_TEXT: 'init-text',
2929
INSERT_TEXT: 'insert-text',
3030
DELETE_TEXT: 'delete-text',
3131
UPDATE_TEXT: 'update-text',
32-
CREATE_VOTE: 'create-vote',
32+
REGISTER_VOTE: 'register-vote',
3333
UPDATE_VOTE: 'update-vote',
3434
END_VOTE: 'end-vote',
3535
FETCH_QUESTIONS: 'fetch-questions',

client/src/components/Block/TextBlock.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,15 +110,15 @@ function TextBlock({
110110

111111
// crdt의 초기화와 소켓을 통해 전달받는 리모트 연산 처리
112112
useEffect(() => {
113-
socket.emit(BLOCK_EVENT.INIT, id);
113+
socket.emit(BLOCK_EVENT.INIT_TEXT, id);
114114

115-
ee.on(`${BLOCK_EVENT.INIT}-${id}`, onInitialize);
115+
ee.on(`${BLOCK_EVENT.INIT_TEXT}-${id}`, onInitialize);
116116
ee.on(`${BLOCK_EVENT.UPDATE_TEXT}-${id}`, onInitialize);
117117
ee.on(`${BLOCK_EVENT.INSERT_TEXT}-${id}`, onInsert);
118118
ee.on(`${BLOCK_EVENT.DELETE_TEXT}-${id}`, onDelete);
119119

120120
return () => {
121-
ee.off(`${BLOCK_EVENT.INIT}-${id}`, onInitialize);
121+
ee.off(`${BLOCK_EVENT.INIT_TEXT}-${id}`, onInitialize);
122122
ee.off(`${BLOCK_EVENT.UPDATE_TEXT}-${id}`, onInitialize);
123123
ee.off(`${BLOCK_EVENT.INSERT_TEXT}-${id}`, onInsert);
124124
ee.off(`${BLOCK_EVENT.DELETE_TEXT}-${id}`, onDelete);

client/src/components/common/Templates/VoteBlock/index.tsx renamed to client/src/components/Block/VoteBlock/VoteBlockTemplate.tsx

Lines changed: 73 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@ function VoteBlockTemplate({
3030
options,
3131
setOptions,
3232
}: VoteBlockProps) {
33-
const [isCreateMode, isRegisteredMode, isEndMode] = [
33+
const [isCreateMode, isRegisteringMode, isRegisteredMode, isEndMode] = [
3434
mode === VoteMode.CREATE,
35+
mode === VoteMode.REGISTERING,
3536
mode === VoteMode.REGISTERED,
3637
mode === VoteMode.END,
3738
];
@@ -67,7 +68,7 @@ function VoteBlockTemplate({
6768
setOptions(validOptions);
6869
setVoteMode(VoteMode.REGISTERED);
6970

70-
socket.emit(BLOCK_EVENT.CREATE_VOTE, validOptions);
71+
socket.emit(BLOCK_EVENT.REGISTER_VOTE, id, validOptions);
7172

7273
toast('투표 등록 완료 ^^', { type: 'info' });
7374
};
@@ -87,7 +88,7 @@ function VoteBlockTemplate({
8788
};
8889

8990
const onEnd = () => {
90-
socket.emit(BLOCK_EVENT.END_VOTE);
91+
socket.emit(BLOCK_EVENT.END_VOTE, id);
9192
};
9293

9394
const onChange: ChangeEventHandler<HTMLInputElement> = ({ target }) => {
@@ -109,20 +110,23 @@ function VoteBlockTemplate({
109110

110111
setSelectedOptionId(targetId);
111112

112-
socket.emit(BLOCK_EVENT.UPDATE_VOTE, targetId, user?.id);
113+
socket.emit(BLOCK_EVENT.UPDATE_VOTE, id, targetId, user?.id);
113114
};
114115

115116
useEffect(() => {
116-
socket.on(BLOCK_EVENT.UPDATE_VOTE, (participantCount) => {
117+
socket.on(`${BLOCK_EVENT.UPDATE_VOTE}-${id}`, (participantCount) => {
117118
setParticipantCount(participantCount);
118119
});
119120

120-
socket.on(BLOCK_EVENT.END_VOTE, ({ options, participantCount }) => {
121-
setVoteMode(VoteMode.END);
122-
setOptions(options);
123-
setParticipantCount(participantCount);
124-
toast('투표가 종료되었어요 ^^');
125-
});
121+
socket.on(
122+
`${BLOCK_EVENT.END_VOTE}-${id}`,
123+
({ options, participantCount }) => {
124+
setVoteMode(VoteMode.END);
125+
setOptions(options);
126+
setParticipantCount(participantCount);
127+
toast('투표가 종료되었어요 ^^');
128+
},
129+
);
126130

127131
return () => {
128132
socket.off(BLOCK_EVENT.UPDATE_VOTE);
@@ -140,63 +144,70 @@ function VoteBlockTemplate({
140144

141145
return (
142146
<div className={style['vote-container']}>
143-
<h3 className={style.title}>투표</h3>
147+
<h3 className={style.title}>{'투표'}</h3>
144148
{(isRegisteredMode || isEndMode) && (
145149
<span className={style['participant-cnt']}>
146150
{participantCount}명 참여
147151
</span>
148152
)}
149-
150-
<ul>
151-
{options.map(({ id, text, count }, index) => (
152-
<li
153-
className={cx('option-item', {
154-
'selected-item':
155-
(isRegisteredMode || isEndMode) && id === selectedOptionId,
156-
})}
157-
key={id}
158-
onClick={() => onSelect(id)}
159-
>
160-
{isEndMode && (
161-
<div
162-
className={style['vote-result-bar']}
163-
style={{
164-
width: `${getPercent(count)}%`,
165-
backgroundColor: color.highlight100,
166-
}}
167-
></div>
168-
)}
169-
170-
<div className={style['box-fill']}>{index + 1}</div>
171-
<input
172-
type="text"
173-
className={cx('option-input', {
174-
selected: isRegisteredMode,
175-
})}
176-
placeholder="항목을 입력해주세요"
177-
onChange={onChange}
178-
data-id={id}
179-
readOnly={isRegisteredMode || isEndMode}
180-
defaultValue={text}
181-
/>
182-
{isCreateMode && (
183-
<Button
184-
icon={<BiX size="20" color="white" />}
185-
ariaLabel="항목 삭제"
186-
onClick={() => onDelete(id)}
187-
/>
188-
)}
189-
{isEndMode && (
190-
<div className={style['vote-result-text']}>
191-
{getVoteResultText(count)}
192-
</div>
193-
)}
194-
</li>
195-
))}
196-
</ul>
197-
153+
{
154+
<ul>
155+
{isCreateMode ? (
156+
<li className={style['option-item']}>
157+
<div className={style['box-fill']}>{'^^'}</div>
158+
<div>등록될 때까지 기다려주세요</div>
159+
</li>
160+
) : (
161+
options.map(({ id, text, count }, index) => (
162+
<li
163+
className={cx('option-item', {
164+
'selected-item':
165+
(isRegisteredMode || isEndMode) && id === selectedOptionId,
166+
})}
167+
key={id}
168+
onClick={() => onSelect(id)}
169+
>
170+
{isEndMode && (
171+
<div
172+
className={style['vote-result-bar']}
173+
style={{
174+
width: `${getPercent(count)}%`,
175+
backgroundColor: color.highlight100,
176+
}}
177+
></div>
178+
)}
179+
180+
<div className={style['box-fill']}>{index + 1}</div>
181+
<input
182+
type="text"
183+
className={cx('option-input', {
184+
selected: isRegisteredMode,
185+
})}
186+
placeholder="항목을 입력해주세요"
187+
onChange={onChange}
188+
data-id={id}
189+
readOnly={isRegisteredMode || isEndMode}
190+
defaultValue={text}
191+
/>
192+
{isRegisteringMode && (
193+
<Button
194+
icon={<BiX size="20" color="white" />}
195+
ariaLabel="항목 삭제"
196+
onClick={() => onDelete(id)}
197+
/>
198+
)}
199+
{isEndMode && (
200+
<div className={style['vote-result-text']}>
201+
{getVoteResultText(count)}
202+
</div>
203+
)}
204+
</li>
205+
))
206+
)}
207+
</ul>
208+
}
198209
<div className={style['vote-buttons']}>
199-
{isCreateMode && (
210+
{isRegisteringMode && (
200211
<>
201212
<Button onClick={onAdd} text="항목 추가" />
202213
<Button onClick={onRegister} text="투표 등록" />

client/src/components/Block/VoteBlock.tsx renamed to client/src/components/Block/VoteBlock/index.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,27 @@
11
import { BLOCK_EVENT } from '@wabinar/constants/socket-message';
22
import { useState, useEffect } from 'react';
3-
import VoteBlockTemplate from 'src/components/common/Templates/VoteBlock';
43
import { VoteMode } from 'src/constants/block';
54
import useSocketContext from 'src/hooks/useSocketContext';
65
import { Option } from 'src/types/block';
76

7+
import VoteBlockTemplate from './VoteBlockTemplate';
8+
89
interface VoteBlockProps {
910
id: string;
11+
registerable: boolean;
1012
}
1113

12-
function VoteBlock({ id }: VoteBlockProps) {
14+
function VoteBlock({ id, registerable }: VoteBlockProps) {
1315
const { momSocket: socket } = useSocketContext();
1416

15-
const [voteMode, setVoteMode] = useState<VoteMode>(VoteMode.CREATE);
17+
const [voteMode, setVoteMode] = useState<VoteMode>(
18+
registerable ? VoteMode.REGISTERING : VoteMode.CREATE,
19+
);
1620
const initialOption: Option[] = [{ id: 1, text: '', count: 0 }];
1721
const [options, setOptions] = useState<Option[]>(initialOption);
1822

1923
useEffect(() => {
20-
socket.on(BLOCK_EVENT.CREATE_VOTE, (options) => {
24+
socket.on(`${BLOCK_EVENT.REGISTER_VOTE}-${id}`, (options) => {
2125
setVoteMode(VoteMode.REGISTERED as VoteMode);
2226
setOptions(options);
2327
});

client/src/components/Block/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ function Block({ id, index, onHandleBlock, registerRef }: BlockProps) {
6565
/>
6666
);
6767
case BlockType.VOTE:
68-
return <VoteBlock id={id} />;
68+
return <VoteBlock id={id} registerable={localUpdateFlagRef.current} />;
6969
case BlockType.QUESTION:
7070
return <QuestionBlock id={id} />;
7171
default:

client/src/components/Mom/index.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,8 @@ function Mom() {
148148
setBlocks(spreadCRDT());
149149
});
150150

151-
socket.on(BLOCK_EVENT.INIT, (id, crdt) => {
152-
ee.emit(`${BLOCK_EVENT.INIT}-${id}`, crdt);
151+
socket.on(BLOCK_EVENT.INIT_TEXT, (id, crdt) => {
152+
ee.emit(`${BLOCK_EVENT.INIT_TEXT}-${id}`, crdt);
153153
});
154154

155155
socket.on(BLOCK_EVENT.INSERT_TEXT, (id, op) => {
@@ -175,7 +175,7 @@ function Mom() {
175175
MOM_EVENT.UPDATED,
176176
MOM_EVENT.INSERT_BLOCK,
177177
MOM_EVENT.DELETE_BLOCK,
178-
BLOCK_EVENT.INIT,
178+
BLOCK_EVENT.INIT_TEXT,
179179
BLOCK_EVENT.INSERT_TEXT,
180180
BLOCK_EVENT.DELETE_TEXT,
181181
BLOCK_EVENT.UPDATE_TYPE,

client/src/constants/block.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export const BLOCKS_TYPE = [
3939

4040
export enum VoteMode {
4141
CREATE,
42+
REGISTERING,
4243
REGISTERED,
4344
END,
4445
}

server/apis/mom/block/model.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import mongoose from '@db';
22
import { Schema } from 'mongoose';
33
import { BlockType } from '@wabinar/api-types/block';
44

5-
import { Vote } from '../vote/service';
5+
import { Vote } from './vote/service';
66
import { Question } from './question/service';
77

8-
interface Block {
8+
export interface Block {
99
id: string;
1010
type: BlockType;
1111
head: Object;

server/apis/mom/block/service.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import LinkedList from '@wabinar/crdt/linked-list';
22
import { BlockType } from '@wabinar/api-types/block';
33
import blockModel from './model';
44
import { Question } from './question/service';
5+
import { Vote } from './vote/service';
56

67
export const getBlock = async (id: string) => {
78
const block = await blockModel.findOne({ id });
@@ -27,7 +28,7 @@ export const putBlockType = async (id: string, type: BlockType) => {
2728
export const putBlock = async (
2829
id: string,
2930
type: BlockType,
30-
data: LinkedList | Question[],
31+
data: LinkedList | Question[] | Vote,
3132
) => {
3233
switch (type) {
3334
case BlockType.H1:
@@ -37,6 +38,7 @@ export const putBlock = async (
3738
await putTextBlock(id, data as LinkedList);
3839
break;
3940
case BlockType.VOTE:
41+
await putVoteBlock(id, data as Vote);
4042
break;
4143
case BlockType.QUESTION:
4244
await putQuestionBlock(id, data as Question[]);
@@ -53,6 +55,20 @@ const putTextBlock = async (id: string, data: LinkedList) => {
5355
);
5456
};
5557

58+
const putVoteBlock = async (id: string, vote: Vote) => {
59+
await blockModel.updateOne({ id }, { voteProperties: vote });
60+
};
61+
62+
export const putVoteBlockStatus = async (id: string, isDoing: boolean) => {
63+
const block = await blockModel.findOneAndUpdate(
64+
{ id },
65+
{ $set: { 'voteProperties.isDoing': isDoing } },
66+
{ new: true },
67+
);
68+
69+
return block;
70+
};
71+
5672
const putQuestionBlock = async (id: string, questions: Question[]) => {
5773
await blockModel.updateOne({ id }, { questionProperties: questions });
5874
};

0 commit comments

Comments
 (0)