작성
·
293
0
import fetcher from '@utils/fetcher';
import axios from 'axios';
import React, { VFC, useCallback, useState } from 'react';
import { Redirect, Route, Switch, useParams } from 'react-router';
import useSWR from 'swr';
import {
Header,
ProfileImg,
RightMenu,
WorkspaceWrapper,
WorkspaceName,
Workspaces,
Channels,
Chats,
MenuScroll,
ProfileModal,
LogOutButton,
WorkspaceButton,
AddButton,
WorkspaceModal,
} from './styles';
import gravatar from 'gravatar';
import loadable from '@loadable/component';
import Menu from '@components/Menu';
const Channel = loadable(() => import('@pages/Channel'));
const DirectMessage = loadable(() => import('@pages/DirectMessage'));
import { Link } from 'react-router-dom';
import { IChannel, IUser } from '@typings/db';
import Modal from '@components/Modal';
import { Button, Input, Label } from '@pages/SignUp/styles';
import useInput from '@hooks/useInput';
import { toast } from 'react-toastify';
import CreateChannelModal from '@components/CreateChannelModal';
import InviteWorkspaceModal from '@components/InviteWorkspaceModal';
import InviteChannelModal from '@components/InviteChannelModal';
// import ChannelList from '@components/ChannelList';
import DMList from '@components/DMList';
import ChannelList from '@components/ChannelList';
// FC타입안에 children이 알아서 들어있음
// children 안쓸거면 VFC
const Workspace: VFC = () => {
const [showUserMenu, setShowUserMenu] = useState(false);
const [showCreateWorkspaceModal, setShowCreateWorkspaceModal] = useState(false);
const [showInviteWorkspaceModal, setShowInviteWorkspaceModal] = useState(false);
const [showInviteChannelModal, setShowInviteChannelModal] = useState(false);
const [newWorkspace, onChangeNewWorkspace, setNewWorkspace] = useInput('');
const [newUrl, onChangeNewUrl, setNewUrl] = useInput('');
const [showWorkspaceModal, setShowWorkspaceModal] = useState(false);
const [ShowCreateChannelModal, setShowCreateChannelModal] = useState(false);
const { workspace } = useParams<{ workspace: string }>();
// 사용자 데이터 가져오기
const {
data: userData,
error,
revalidate,
mutate,
} = useSWR<IUser | false>('/api/users', fetcher, {
dedupingInterval: 2000,
});
// channel 데이터 가져오기
const { data: channelData } = useSWR<IChannel[]>(userData ? `/api/workspaces/${workspace}/channels` : null, fetcher);
// 워크스페이스에 있는 멤버 데이터
const { mutate: revalidateMembers } = useSWR<IUser[]>(
userData ? `/api/workspaces/${workspace}/members` : null,
fetcher,
);
// 로그아웃
const onLogout = useCallback(() => {
axios
.post('/api/users/logout', null, {
withCredentials: true,
})
.then((response) => {
mutate(response.data);
// 기존에 받은 데이터를 mutate의 data에 담음
})
.catch((error) => {
console.log(error);
});
}, []);
// 프로필 누르면 메뉴 보이기
const onClickUserProfile = useCallback(() => {
setShowUserMenu((prev) => !prev);
}, []);
// 프로필 닫기
const onCloseUserProfile = useCallback((e) => {
e.stopPropagation();
setShowUserMenu(false);
}, []);
// 워크스페이스 모달 열기
const onClickCreateWorkspace = useCallback(() => {
setShowCreateWorkspaceModal(true);
}, []);
// 워크스페이스 모달 닫기
const onCloseModal = useCallback(() => {
setShowCreateWorkspaceModal(false);
setShowCreateChannelModal(false);
setShowInviteChannelModal(false);
setShowInviteWorkspaceModal(false);
}, []);
// 워크스페이스 생성
const onCreateWorkspace = useCallback(
(e) => {
e.preventDefault();
// trim() 안넣으면 띄어쓰기 넣으면 걍 통과됨
if (!newWorkspace || !newWorkspace.trim()) return;
if (!newUrl || !newUrl.trim()) return;
axios
.post(
'/api/workspaces',
{
workspace: newWorkspace,
url: newUrl,
},
{
withCredentials: true,
},
)
.then(() => {
revalidate();
setShowCreateWorkspaceModal(false);
setNewWorkspace('');
setNewUrl('');
})
.catch((error) => {
console.log(error);
toast.error(error.response?.data, { position: 'bottom-center' });
});
},
[newWorkspace, newUrl],
);
// 워크스페이스 사용자 초대
const onClickInviteWorkspace = useCallback(() => {
setShowInviteWorkspaceModal(true);
}, []);
// Channel
// 토글
const toggleWorkspaceModal = useCallback(() => {
setShowWorkspaceModal((prev) => !prev);
}, []);
// 채널 만들기
const onClickAddChannel = useCallback(() => {
setShowCreateChannelModal(true);
}, []);
if (!userData) {
return <Redirect to="/login" />;
}
return (
<div>
<Header>
<RightMenu>
<span onClick={onClickUserProfile}>
<ProfileImg src={gravatar.url(userData.nickname, { s: '28px', d: 'retro' })} alt={userData.nickname} />
{showUserMenu && (
<Menu style={{ right: 0, top: 38 }} show={showUserMenu} onCloseModal={onCloseUserProfile}>
<ProfileModal>
<img src={gravatar.url(userData.nickname, { s: '36px', d: 'retro' })} alt={userData.nickname} />
<div>
<span id="profile-name">{userData.nickname}</span>
<span id="profile-active">Active</span>
</div>
</ProfileModal>
<LogOutButton onClick={onLogout}>로그아웃</LogOutButton>
</Menu>
)}
</span>
</RightMenu>
</Header>
<WorkspaceWrapper>
<Workspaces>
{userData?.Workspaces.map((ws) => {
return (
<Link key={ws.id} to={`/workspace/${123}/channel/일반`}>
<WorkspaceButton>{ws.name.slice(0, 1).toUpperCase()}</WorkspaceButton>
</Link>
);
})}
<AddButton onClick={onClickCreateWorkspace}>+</AddButton>
</Workspaces>
<Channels>
<WorkspaceName onClick={toggleWorkspaceModal}>Sleact</WorkspaceName>
<MenuScroll>
<Menu show={showWorkspaceModal} onCloseModal={toggleWorkspaceModal} style={{ top: 95, left: 80 }}>
<WorkspaceModal>
<h2>Sleact</h2>
<button onClick={onClickInviteWorkspace}>워크스페이스에 사용자 초대</button>
<button onClick={onClickAddChannel}>채널 만들기</button>
<button onClick={onLogout}>로그아웃</button>
</WorkspaceModal>
</Menu>
<ChannelList />
<DMList />
{/* {channelData?.map((v) => (
<div>{v.name}</div>
))} */}
</MenuScroll>
</Channels>
<Chats>
<Switch>
<Route path="/workspace/:workspace/channel/:channel" component={Channel} />
<Route path="/workspace/:workspace/dm/:id" component={DirectMessage} />
</Switch>
</Chats>
</WorkspaceWrapper>
<Modal show={showCreateWorkspaceModal} onCloseModal={onCloseModal}>
<form onSubmit={onCreateWorkspace}>
<Label id="workspace-label">
<span>워크스페이스 이름</span>
<Input id="workspace" value={newWorkspace} onChange={onChangeNewWorkspace} />
</Label>
<Label id="workspace-url-label">
<span>워크스페이스 url</span>
<Input id="workspace" value={newUrl} onChange={onChangeNewUrl} />
</Label>
<Button type="submit">생성하기</Button>
</form>
</Modal>
<CreateChannelModal
show={ShowCreateChannelModal}
onCloseModal={onCloseModal}
setShowCreateChannelModal={setShowCreateChannelModal}
/>
<InviteWorkspaceModal
show={showInviteWorkspaceModal}
onCloseModal={onCloseModal}
setShowInviteWorkspaceModal={setShowInviteWorkspaceModal}
/>
<InviteChannelModal
show={showInviteChannelModal}
onCloseModal={onCloseModal}
setShowInviteChannelModal={setShowInviteChannelModal}
/>
</div>
);
};
export default Workspace;
이건 제가 작성한 WorkSpace 입니다.
import Modal from '@components/Modal';
import useInput from '@hooks/useInput';
import { Button, Input, Label } from '@pages/SignUp/styles';
import { IUser } from '@typings/db';
import fetcher from '@utils/fetcher';
import axios from 'axios';
import React, { FC, useCallback } from 'react';
import { useParams } from 'react-router';
import { toast } from 'react-toastify';
import useSWR from 'swr';
interface Props {
show: boolean;
onCloseModal: () => void;
setShowInviteChannelModal: (flag: boolean) => void;
}
const InviteChannelModal: FC<Props> = ({ show, onCloseModal, setShowInviteChannelModal }) => {
const { workspace, channel } = useParams<{ workspace: string; channel: string }>();
const [newMember, onChangeNewMember, setNewMember] = useInput('');
const { data: userData } = useSWR<IUser>('/api/users', fetcher);
const { revalidate: revalidateMembers } = useSWR<IUser[]>(
userData ? `/api/workspaces/${workspace}/channels/${channel}/members` : null,
fetcher,
);
console.dir(channel);
const onInviteMember = useCallback(
(e) => {
e.preventDefault();
if (!newMember || !newMember.trim()) {
return;
}
axios
.post(`/api/workspaces/${workspace}/channels/${channel}/members`, {
email: newMember,
})
.then(() => {
revalidateMembers();
setShowInviteChannelModal(false);
setNewMember('');
})
.catch((error) => {
console.dir(error);
toast.error(error.response?.data, { position: 'bottom-center' });
});
},
[channel, newMember, revalidateMembers, setNewMember, setShowInviteChannelModal, workspace],
);
return (
<Modal show={show} onCloseModal={onCloseModal}>
<form onSubmit={onInviteMember}>
<Label id="member-label">
<span>채널 멤버 초대</span>
<Input id="member" value={newMember} onChange={onChangeNewMember} />
</Label>
<Button type="submit">초대하기</Button>
</form>
</Modal>
);
};
export default InviteChannelModal;
이건 제가 작성한 InviteChannelModal입니다.
xhr.js:210 GET http://localhost:3090/api/workspaces/sleact/channels/undefined/members 404 (Not Found)
dispatchXhrRequest @ xhr.js:210
xhrAdapter @ xhr.js:15
dispatchRequest @ dispatchRequest.js:58
request @ Axios.js:108
Axios.<computed> @ Axios.js:129
wrap @ bind.js:9
fetcher @ fetcher.ts:18
eval @ use-swr.js:392
step @ use-swr.js:43
eval @ use-swr.js:24
eval @ use-swr.js:18
__awaiter @ use-swr.js:14
eval @ use-swr.js:344
softRevalidate @ use-swr.js:532
onFocus @ use-swr.js:550
revalidate_1 @ use-swr.js:73
eval @ use-swr.js:77
eval @ web-preset.js:29
그런데 위와 같은 에러가 발생합니다.
콘솔에 channel을 출력해도 undefined가 뜹니다.. 그런데 url을 보면 http://localhost:3090/workspace/sleact/channel/테스트채널 이렇게 채널 명이 표시가 되는데 도대체 뭐 때문에 undefined라고 뜨는지 모르겠습니다... 아무리 찾아봐도 알 수가 없어서 글 남깁니다... 분명 WorkSpace의 route에도 오타가 없고 url도 표시가 잘되는데 왜 undefined일까요...
해결했습니다. 감사합니다!
그런데 WorkSpace에서는 왜 channel params에 접근할 수 없나요?