Skip to content

Commit

Permalink
Merge pull request #139 from NEM-NE/dev
Browse files Browse the repository at this point in the history
์ฑ„ํŒ…๋ฐฉ ๋ฒ„๊ทธ ์ด์Šˆ ๋ฐ ํ”„๋กœํ•„ ์‚ฌ์ง„ ๋ณ€๊ฒฝ ๋ชจ๋‹ฌ UI ์ˆ˜์ •
  • Loading branch information
HanCiHu authored Nov 26, 2021
2 parents def3bf2 + 9162aff commit f42829e
Show file tree
Hide file tree
Showing 13 changed files with 479 additions and 66 deletions.
10 changes: 6 additions & 4 deletions client/src/components/chat/chat-new-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ const DoneBtn = ({ selectedUserList }: any) => {
pathname: `/chat-rooms/${res.chatDocumentId}`,
state: { participantsInfo: selectedUserList },
});
chatSocket.emit('chat:makeChat', {
chatDocumentId: res.chatDocumentId,
participantsInfo: [...selectedUserList, { userDocumentId: user.userDocumentId, userName: user.userName, profileUrl: user.profileUrl }],
});
if (res.isNew) {
chatSocket.emit('chat:makeChat', {
chatDocumentId: res.chatDocumentId,
participantsInfo: [...selectedUserList, { userDocumentId: user.userDocumentId, userName: user.userName, profileUrl: user.profileUrl }],
});
}
});
};

Expand Down
20 changes: 13 additions & 7 deletions client/src/components/profile/change-profile-image-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ const PreviewProfileImage = styled.img`
width: 150px;
height: 150px;
min-height: 150px;
object-fit: contain;
margin: 20px;
object-fit: cover;
border-radius: 30px;
border: 0.1px solid #b8b8b8;
Expand All @@ -48,9 +47,13 @@ const ButtonsLayout = styled.div`
`;

const CustomInput = styled.input`
padding:0;
margin-bottom: 20px;
width : 200px;
display: none;
& + label {
width: 200x;
height: 200px;
cursor: pointer;
}
`;

function ShareModal() {
Expand Down Expand Up @@ -95,9 +98,12 @@ function ShareModal() {
/>
<ModalBox>
<ChangePofileImageLayout>
<PreviewProfileImage src={previewImageURL} />
<h2>Change your photo</h2>
<CustomForm>
<CustomInput type="file" accept="image/gif, image/jpeg, image/png" onChange={inputOnChange} />
<CustomInput id="c2" type="file" accept="image/gif, image/jpeg, image/png" onChange={inputOnChange} />
<label htmlFor={'c2' as string}>
<PreviewProfileImage src={previewImageURL} />
</label>
<ButtonsLayout>
<DefaultButton buttonType="secondary" size="medium" onClick={() => changeImageHandler()}>Change</DefaultButton>
<DefaultButton buttonType="thirdly" size="medium" onClick={() => setIsOpenChangeProfileImageModalState(!isOpenChangeProfileImageModal)}>
Expand Down
1 change: 0 additions & 1 deletion client/src/recoil/selectors/index.ts

This file was deleted.

1 change: 0 additions & 1 deletion client/src/tests/index.tsx

This file was deleted.

1 change: 0 additions & 1 deletion client/src/utils/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import { Link } from 'react-router-dom';

import { IconAndLink } from '@interfaces/index';
Expand Down
51 changes: 3 additions & 48 deletions client/src/views/chat-rooms-new-view.tsx
Original file line number Diff line number Diff line change
@@ -1,62 +1,17 @@
/* eslint-disable no-underscore-dangle */
import React, { useEffect, useState, useRef } from 'react';
import { useRecoilValue } from 'recoil';
import React, { useRef } from 'react';

import NewChatRoomHeader from '@src/components/chat/chat-new-header';
import { ChatRoomsLayout, NewChatRoomBody } from '@components/chat/style';
import UserCardList from '@components/common/user-card-list';
import { findUsersByIdList } from '@api/index';
import followState from '@atoms/following-list';
import {
SelectDiv, SelectInputBar, SelectedUserDiv, SelectUserComponent,
} from '@common/select';
import useSelectUser from '@hooks/useSelectUser';

function ChatRoomsNewView() {
const followingList = useRecoilValue(followState);
const [selectedUsers, setSelectedUsers] = useState<any>([]);
const [allUserList, setAllUserList] = useState([]);
const [filteredUserList, setFilteredUserList] = useState([]);
const inputBarRef = useRef<HTMLInputElement>(null);

const addSelectedUser = (e: any) => {
const userCardDiv = e.target.closest('.userCard');
const profileUrl = userCardDiv.querySelector('img');
if (!userCardDiv || !profileUrl) return;
const userName = userCardDiv?.getAttribute('data-username');
inputBarRef.current!.value = '';
setSelectedUsers([...selectedUsers,
{
userDocumentId: userCardDiv?.getAttribute('data-id'),
userName,
profileUrl: profileUrl.getAttribute('src'),
}]);
};

const deleteUser = (e: any) => {
setSelectedUsers(selectedUsers.filter((user: any) => user.userDocumentId !== e.target.getAttribute('data-id')));
inputBarRef.current!.value = '';
};

const searchUser = () => {
const searchWord = inputBarRef.current!.value;
const selectedUserIds = selectedUsers.map((user: any) => user.userDocumentId);
setFilteredUserList(allUserList
.filter((user: any) => (user.userId.indexOf(searchWord) > -1 || user.userName.indexOf(searchWord) > -1)
&& selectedUserIds.indexOf(user._id) === -1));
};

useEffect(() => {
findUsersByIdList(followingList).then((res: any) => {
const selectedUserIds = selectedUsers.map((user: any) => user.userDocumentId);
setAllUserList(res.userList);
setFilteredUserList(res.userList.filter((user: any) => selectedUserIds.indexOf(user._id) === -1));
});
}, [followingList]);

useEffect(() => {
const selectedUserIds = selectedUsers.map((user: any) => user.userDocumentId);
setFilteredUserList(allUserList.filter((user: any) => selectedUserIds.indexOf(user._id) === -1));
}, [selectedUsers]);
const [selectedUsers, filteredUserList, searchUser, deleteUser, addSelectedUser] = useSelectUser(inputBarRef);

return (
<ChatRoomsLayout>
Expand Down
4 changes: 2 additions & 2 deletions server/src/api/routes/chats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ export default (app: Router) => {
try {
const { participants } = req.body;
participants.sort();
const { chatDocumentId } = await chatService.makeChatRoom(participants);
const { chatDocumentId, isNew } = await chatService.makeChatRoom(participants);

res.status(200).json({ chatDocumentId });
res.status(200).json({ chatDocumentId, isNew });
} catch (error) {
res.json({ ok: false });
}
Expand Down
101 changes: 101 additions & 0 deletions server/src/services/user/activity-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/* eslint-disable no-underscore-dangle */
import Users, { IActivity } from '@models/users';
import Events from '@models/events';
import { activeUser } from '@src/sockets/user';
import { userNamespace } from '@src/sockets';

let instance: any = null;
class UserService {
constructor() {
if (instance) return instance;
instance = this;
}

async getActivityList(userDocumentId: string, count: number) {
const user = await Users.findById(userDocumentId, ['activity']);
const newActivityList = await Promise.all(user!.activity.slice(count, count + 10).map(async (activity: IActivity) => {
const detailFrom = await this.findUserByDocumentId(activity.from);
const newFrom = { userId: detailFrom!.userId, userName: detailFrom!.userName, profileUrl: detailFrom!.profileUrl };
return { ...activity, from: newFrom };
}));
await Users.findByIdAndUpdate(userDocumentId, { activity: user!.activity.map((el) => ({ ...el, isChecked: true })) });
return newActivityList;
}

async isActivityChecked(userDocumentId: string) {
try {
const { activity } : any = await Users.findOne({ _id: userDocumentId }, ['activity']);
if (!activity) return false;
return activity.some((item: IActivity) => !item.isChecked);
} catch (e) {
return false;
}
}

async addActivityTypeFollow(userDocumentId: string, targetUserDocumentId: string) {
try {
const newActivity = {
type: 'follow',
clickDocumentId: userDocumentId,
from: userDocumentId,
date: new Date(),
isChecked: false,
};
await Users.findByIdAndUpdate(targetUserDocumentId, { $push: { activity: { $each: [newActivity], $position: 0 } } });
this.emitToUserGetActivity(targetUserDocumentId);
return true;
} catch (e) {
return false;
}
}

async addActivityTypeRoom(userDocumentId: string, roomDocumentId: string) {
try {
const user = await Users.findById(userDocumentId, ['followers']);
const newActivity = {
type: 'room',
clickDocumentId: String(roomDocumentId),
from: userDocumentId,
date: new Date(),
isChecked: false,
};
await Promise.all(user!.followers.map(async (userDocId: string) => {
await Users.findByIdAndUpdate(userDocId, { $push: { activity: { $each: [newActivity], $position: 0 } } });
this.emitToUserGetActivity(userDocId);
return true;
}));
return true;
} catch (e) {
return false;
}
}

async addActivityTypeEvent(userDocumentId: string, eventDocumentId: string) {
try {
const event = await Events.findById(eventDocumentId, ['participants']);
const newActivity = {
type: 'event',
clickDocumentId: String(eventDocumentId),
from: userDocumentId,
date: new Date(),
isChecked: false,
};
await Promise.all(event!.participants.map(async (userId: string) => {
const user = await Users.findOneAndUpdate({ userId }, { $push: { activity: { $each: [newActivity], $position: 0 } } });
const userDocId = user!._id;
this.emitToUserGetActivity(String(userDocId));
return true;
}));
return true;
} catch (e) {
return false;
}
}

emitToUserGetActivity(userDocumentId: string) {
const socketUser = activeUser.get(userDocumentId);
if (socketUser) userNamespace.to(socketUser.socketId).emit('user:getActivity');
}
}

export default new UserService();
120 changes: 120 additions & 0 deletions server/src/services/user/auth-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/* eslint-disable no-underscore-dangle */
import jwt from 'jsonwebtoken';

import Users from '@models/users';
import RefreshTokens from '@models/refresh-token';
import jwtUtils from '@utils/jwt-util';

interface ISignupUserInfo {
loginType: string,
userId: string,
password: string,
userName: string,
userEmail: string,
}

let instance: any = null;
class AuthService {
constructor() {
if (instance) return instance;
instance = this;
}

async signIn(email: string, password: string) {
const user = await Users.findOne({ userEmail: email });

if (!user) {
return { ok: false, msg: 'there is no user' };
}

const isMatch = user.checkPassword(password);

if (isMatch) {
const accessToken = jwtUtils.sign(user._id, user.userEmail);
const refreshToken = jwtUtils.refresh();

const existingRefreshToken = await RefreshTokens.findOneAndUpdate(
{ userDocumentId: user._id },
{ token: refreshToken },
);

if (!existingRefreshToken) {
const usersRefreshTokens = new RefreshTokens({
userDocumentId: user._id,
token: refreshToken,
});
await usersRefreshTokens.save();
}

return {
accessToken,
ok: true,
msg: 'ok',
};
}
return { ok: false, msg: 'wrong password' };
}

async signup(info: ISignupUserInfo) {
try {
await Users.insertMany([info]);
} catch (error) {
console.error(error);
}
}

async verifyAccessToken(token: string) {
const result = jwtUtils.verify(token);
if (!result.ok) {
const newToken = await this.tokenRefresh(token);
return newToken;
}
return result;
}

async getRefreshTokens(userDocumentId: string) {
const refreshToken = await RefreshTokens.findOne({ userDocumentId });
if (!refreshToken) {
return null;
}
return refreshToken.token;
}

async tokenRefresh(accessToken: string) {
const accessResult = jwtUtils.verify(accessToken);
const decoded = jwt.decode(accessToken) as jwt.JwtPayload;

if (decoded === null) {
return {
ok: false,
msg: 'No authorized!',
};
}

const refreshToken = (await this.getRefreshTokens(decoded.id)) as string;
const refreshResult = await jwtUtils.refreshVerify(refreshToken);

if (accessResult.ok === false && accessResult.message === 'jwt expired') {
if (refreshResult === false) {
return {
ok: false,
msg: 'No authorized!',
};
}
const newAccessToken = jwtUtils.sign(decoded.id, decoded.email);

return {
ok: true,
accessToken: newAccessToken,
userDocumentId: decoded.id,
};
}

return {
ok: true,
msg: 'Acess token is not expired!',
};
}
}

export default new AuthService();
Loading

0 comments on commit f42829e

Please sign in to comment.