Skip to content

Commit 60a4060

Browse files
authored
Merge pull request #186 from boostcamp-2020/develop
Release Browse Page
2 parents 99720a5 + c76909f commit 60a4060

File tree

25 files changed

+305
-63
lines changed

25 files changed

+305
-63
lines changed

client/.eslintrc.json

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
}
4141
],
4242
"linebreak-style": 0,
43+
"no-case-declarations": 0,
4344
"no-use-before-define": "off",
4445
"import/prefer-default-export": "off",
4546
"import/no-unresolved": "off",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { JOIN_CHATROOM, joinChatroomState } from '@socket/types/chatroom-types';
2+
import socket from '../socketIO';
3+
4+
export const joinChatroom = (chatroomId: joinChatroomState) => {
5+
socket.emit(JOIN_CHATROOM, { chatroomId });
6+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export const JOIN_CHATROOM = 'join chatroom';
2+
3+
export interface joinChatroomState {
4+
chatroomId: number;
5+
}
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1-
import { INIT_CHANNELS_ASYNC } from '../types/channel-types';
1+
import { INIT_CHANNELS_ASYNC, LOAD_NEXT_CHANNELS_ASYNC, JOIN_CHANNEL_ASYNC } from '../types/channel-types';
22

33
export const initChannels = () => ({ type: INIT_CHANNELS_ASYNC });
4+
export const loadNextChannels = (payload: any) => ({ type: LOAD_NEXT_CHANNELS_ASYNC, payload });
5+
export const joinChannel = (payload: any) => ({ type: JOIN_CHANNEL_ASYNC, payload });

client/src/common/store/reducers/channel-reducer.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { channelsState, ChannelTypes, INIT_CHANNELS } from '../types/channel-types';
1+
import { channelState, channelsState, ChannelTypes, INIT_CHANNELS, LOAD_NEXT_CHANNELS, JOIN_CHANNEL } from '../types/channel-types';
22

33
const initialState: channelsState = {
44
channelCount: 0,
@@ -12,6 +12,18 @@ export default function channelReducer(state = initialState, action: ChannelType
1212
channelCount: action.payload.channelCount,
1313
channels: action.payload.channels
1414
};
15+
case LOAD_NEXT_CHANNELS:
16+
return {
17+
...state,
18+
channels: [...state.channels, ...action.payload.channels]
19+
};
20+
case JOIN_CHANNEL:
21+
const { chatroomId } = action.payload;
22+
const channels = state.channels.map((channel: channelState) => {
23+
if (channel.chatroomId === chatroomId) return { ...channel, isJoined: true };
24+
return channel;
25+
});
26+
return { ...state, channels };
1527
default:
1628
return state;
1729
}
+38-3
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,52 @@
11
import { call, put, takeEvery } from 'redux-saga/effects';
22
import API from '@utils/api';
3-
import { INIT_CHANNELS, INIT_CHANNELS_ASYNC } from '../types/channel-types';
3+
import { joinChatroom } from '@socket/emits/chatroom';
4+
import {
5+
INIT_CHANNELS,
6+
INIT_CHANNELS_ASYNC,
7+
JOIN_CHANNEL,
8+
JOIN_CHANNEL_ASYNC,
9+
LOAD_NEXT_CHANNELS,
10+
LOAD_NEXT_CHANNELS_ASYNC
11+
} from '../types/channel-types';
12+
import { ADD_CHANNEL } from '../types/chatroom-types';
413

514
function* initChannelsSaga() {
615
try {
7-
const channelCount = 0;
8-
const channels = yield call(API.getChannels);
16+
const { channels, channelCount } = yield call(API.getChannels);
917
yield put({ type: INIT_CHANNELS, payload: { channelCount, channels } });
1018
} catch (e) {
1119
console.log(e);
1220
}
1321
}
1422

23+
function* loadNextChannels(action: any) {
24+
try {
25+
const { title } = action.payload;
26+
const nextChannels = yield call(API.getNextChannels, title);
27+
yield put({ type: LOAD_NEXT_CHANNELS, payload: { channels: nextChannels } });
28+
} catch (e) {
29+
console.log(e);
30+
}
31+
}
32+
33+
function* joinChannel(action: any) {
34+
try {
35+
const { chatroomId } = action.payload;
36+
yield call(API.joinChannel, chatroomId);
37+
yield put({ type: JOIN_CHANNEL, payload: { chatroomId } });
38+
const chatroom = yield call(API.getChatroom, chatroomId);
39+
const { chatType, isPrivate, title } = chatroom;
40+
const payload = { chatroomId, chatType, isPrivate, title };
41+
joinChatroom(chatroomId);
42+
yield put({ type: ADD_CHANNEL, payload });
43+
} catch (e) {
44+
console.log(e);
45+
}
46+
}
47+
1548
export function* channelSaga() {
1649
yield takeEvery(INIT_CHANNELS_ASYNC, initChannelsSaga);
50+
yield takeEvery(LOAD_NEXT_CHANNELS_ASYNC, loadNextChannels);
51+
yield takeEvery(JOIN_CHANNEL_ASYNC, joinChannel);
1752
}

client/src/common/store/sagas/chatroom-saga.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { call, put, takeEvery } from 'redux-saga/effects';
22
import API from '@utils/api';
3+
import { joinChatroom } from '@socket/emits/chatroom';
34
import {
45
LOAD,
56
LOAD_ASYNC,
@@ -53,6 +54,7 @@ function* addChannel(action: any) {
5354
try {
5455
const chatroomId = yield call(API.createChannel, action.payload.title, action.payload.description, action.payload.isPrivate);
5556
const payload = { chatroomId, chatType: 'Channel', isPrivate: action.payload.isPrivate, title: action.payload.title };
57+
joinChatroom(chatroomId);
5658
yield put({ type: ADD_CHANNEL, payload });
5759
} catch (e) {
5860
alert('같은 이름의 채널이 존재합니다.');
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
export const INIT_CHANNELS = 'INIT_CHANNELS';
22
export const INIT_CHANNELS_ASYNC = 'INIT_CHANNELS_ASYNC';
3+
export const LOAD_NEXT_CHANNELS = 'LOAD_NEXT_CHANNELS';
4+
export const LOAD_NEXT_CHANNELS_ASYNC = 'LOAD_NEXT_CHANNELS_ASYNC';
5+
export const JOIN_CHANNEL = 'JOIN_CHANNEL';
6+
export const JOIN_CHANNEL_ASYNC = 'JOIN_CHANNEL_ASYNC';
37

48
export interface channelState {
5-
channelId: number;
9+
chatroomId: number;
610
title: string;
7-
description: string;
11+
description?: string;
812
isPrivate: boolean;
913
members: number;
1014
isJoined: boolean;
@@ -15,9 +19,23 @@ export interface channelsState {
1519
channels: Array<channelState>;
1620
}
1721

22+
export interface joinChannelState {
23+
chatroomId: number;
24+
}
25+
1826
interface InitChannelsAction {
1927
type: typeof INIT_CHANNELS;
2028
payload: channelsState;
2129
}
2230

23-
export type ChannelTypes = InitChannelsAction;
31+
interface LoadNextChannels {
32+
type: typeof LOAD_NEXT_CHANNELS;
33+
payload: channelsState;
34+
}
35+
36+
interface JoinChannel {
37+
type: typeof JOIN_CHANNEL;
38+
payload: joinChannelState;
39+
}
40+
41+
export type ChannelTypes = InitChannelsAction | LoadNextChannels | JoinChannel;

client/src/common/utils/api.ts

+11
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,17 @@ export default {
7171

7272
getChannels: async () => {
7373
const response = await axios.get(`api/chatrooms`);
74+
const channelCount = response.headers['x-total-count'];
75+
return { channels: response.data, channelCount };
76+
},
77+
78+
getNextChannels: async (title: string) => {
79+
const response = await axios.get(`api/chatrooms?offsetTitle=${title}`);
80+
return response.data;
81+
},
82+
83+
joinChannel: async (chatroomId: number) => {
84+
const response = await axios.post(`api/user-chatrooms`, { chatroomId });
7485
return response.data;
7586
}
7687
};

client/src/components/atoms/Button/Button.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@ interface ButtonProps {
99
fontColor: string;
1010
isBold?: boolean;
1111
hoverColor?: string;
12+
width?: string;
13+
height?: string;
1214
onClick?: () => void;
1315
}
1416

1517
const StyledButton = styled.button<any>`
1618
display: flex;
1719
align-items: center;
20+
justify-content: center;
1821
background-color: ${(props) => props.backgroundColor};
1922
border: 2px solid ${(props) => props.borderColor};
2023
color: ${(props) => props.fontColor};
@@ -24,6 +27,8 @@ const StyledButton = styled.button<any>`
2427
cursor: pointer;
2528
font-weight: ${(props) => (props.isBold ? 'bold' : null)};
2629
${(props) => (props.hoverColor ? `&:hover { background-color: ${color.hover_primary}}` : '')}
30+
${(props) => (props.width ? `width: ${props.width}}` : '')}
31+
${(props) => (props.height ? `height: ${props.height}}` : '')}
2732
`;
2833

2934
const Button: React.FC<ButtonProps> = ({ children, backgroundColor, borderColor, fontColor, isBold, hoverColor, ...props }) => {

client/src/components/molecules/BrowsePageChannelBody/BrowsePageChannelBody.stories.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ const Template: Story<BrowsePageChannelBodyProps> = (args) => <BrowsePageChannel
1212
export const BlackBrowsePageChannelBody = Template.bind({});
1313
BlackBrowsePageChannelBody.args = {
1414
isJoined: true,
15-
memberCount: 4,
15+
members: 4,
1616
description: '공지사항을 안내하는 채널'
1717
};

client/src/components/molecules/BrowsePageChannelBody/BrowsePageChannelBody.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { color } from '@theme/index';
55

66
interface BrowsePageChannelBodyProps {
77
isJoined?: boolean;
8-
memberCount: number;
8+
members: number;
99
description?: string;
1010
}
1111

@@ -16,7 +16,7 @@ const BrowsePageChannelBodyWrap = styled.div<any>`
1616
}
1717
`;
1818

19-
const BrowsePageChannelBody: React.FC<BrowsePageChannelBodyProps> = ({ isJoined, memberCount, description, ...props }) => {
19+
const BrowsePageChannelBody: React.FC<BrowsePageChannelBodyProps> = ({ isJoined, members, description, ...props }) => {
2020
return (
2121
<BrowsePageChannelBodyWrap {...props}>
2222
{isJoined && (
@@ -25,7 +25,7 @@ const BrowsePageChannelBody: React.FC<BrowsePageChannelBodyProps> = ({ isJoined,
2525
</Text>
2626
)}
2727
<Text fontColor={color.text_tertiary} size="superSmall">
28-
{`${memberCount} members`}
28+
{`${members} members`}
2929
</Text>
3030
{description && (
3131
<Text fontColor={color.text_tertiary} size="superSmall">

client/src/components/molecules/BrowsePageChannelButton/BrowsePageChannelButton.tsx

+8-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,13 @@ const BrowsePageChannelButton: React.FC<BrowsePageChannelButtonProps> = ({ isJoi
1212
return (
1313
<>
1414
{isJoined ? (
15-
<Button onClick={handlingLeaveButton} backgroundColor={color.tertiary} borderColor={color.secondary} fontColor={color.primary} {...props}>
15+
<Button
16+
onClick={handlingLeaveButton}
17+
backgroundColor={color.tertiary}
18+
borderColor={color.secondary}
19+
fontColor={color.primary}
20+
width="5rem"
21+
{...props}>
1622
Leave
1723
</Button>
1824
) : (
@@ -21,6 +27,7 @@ const BrowsePageChannelButton: React.FC<BrowsePageChannelButtonProps> = ({ isJoi
2127
backgroundColor={color.button_secondary}
2228
borderColor={color.button_secondary}
2329
fontColor={color.text_secondary}
30+
width="5rem"
2431
{...props}>
2532
Join
2633
</Button>

client/src/components/molecules/BrowsePageChannelHeader/BrowsePageChannelHeader.stories.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ const Template: Story<BrowsePageChannelHeaderProps> = (args) => <BrowsePageChann
1111

1212
export const BlackBrowsePageChannelHeader = Template.bind({});
1313
BlackBrowsePageChannelHeader.args = {
14-
name: 'notice',
14+
title: 'notice',
1515
isPrivate: true
1616
};

client/src/components/molecules/BrowsePageChannelHeader/BrowsePageChannelHeader.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import LockIcon from '@imgs/lock-icon.png';
66
import { color } from '@theme/index';
77

88
interface BrowsePageChannelHeaderProps {
9-
name: string;
9+
title: string;
1010
isPrivate?: boolean;
1111
}
1212

@@ -17,12 +17,12 @@ const BrowsePageChannelHeaderWrap = styled.div<any>`
1717
}
1818
`;
1919

20-
const BrowsePageChannelHeader: React.FC<BrowsePageChannelHeaderProps> = ({ name, isPrivate, ...props }) => {
20+
const BrowsePageChannelHeader: React.FC<BrowsePageChannelHeaderProps> = ({ title, isPrivate, ...props }) => {
2121
return (
2222
<BrowsePageChannelHeaderWrap {...props}>
2323
<Icon size="small" src={isPrivate ? LockIcon : ChannelIcon} isHover={false} />
2424
<Text fontColor={color.primary} size="small" isBold={true}>
25-
{name}
25+
{title}
2626
</Text>
2727
</BrowsePageChannelHeaderWrap>
2828
);

client/src/components/molecules/BrowsePageControls/BrowsePageControls.tsx

+5-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ const BrowsePageControlsWrap = styled.div<any>`
2424
display: flex;
2525
justify-content: space-between;
2626
align-items: center;
27-
padding: 0.4rem 1.5rem;
27+
padding-top: 0.4rem;
28+
padding-bottom: 1rem;
29+
margin-left: 1.5rem;
30+
margin-right: 2.5rem;
31+
border-bottom: 0.5px solid ${color.border_secondary};
2832
`;
2933

3034
const BrowsePageControlsButtonWrap = styled.div<any>`

client/src/components/molecules/BrowsePageSearchBar/BrowsePageSearchBar.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const StyledInput = styled.input<any>`
2020
border: 0 none;
2121
outline: none;
2222
width: fill-available;
23-
font-size: 0.8rem;
23+
font-size: 0.9rem;
2424
`;
2525

2626
const InputHintWrap = styled.div<any>`

client/src/components/organisms/BrowsePageChannel/BrowsePageChannel.stories.tsx

+4-8
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,12 @@ export default {
99

1010
const Template: Story<BrowsePageChannelProps> = (args) => <BrowsePageChannel {...args} />;
1111

12-
const handlingJoinButton = () => {};
13-
const handlingLeaveButton = () => {};
14-
1512
export const BlackBrowsePageChannel = Template.bind({});
1613
BlackBrowsePageChannel.args = {
17-
name: 'notice',
14+
chatroomId: 1,
15+
title: 'notice',
1816
isJoined: true,
19-
memberCount: 4,
17+
members: 4,
2018
description: '공지사항을 안내하는 채널',
21-
isPrivate: true,
22-
handlingJoinButton,
23-
handlingLeaveButton
19+
isPrivate: true
2420
};

0 commit comments

Comments
 (0)