Skip to content

Commit

Permalink
Merge pull request #295 from boostcampwm2023/feature/update-member-st…
Browse files Browse the repository at this point in the history
…atus

fix: 처음 접속 시 서버에서 보내준 상태 반영, 여러 탭 사용 시 수동 자리비움 상태 변경 동작 모두 적용
  • Loading branch information
surinkwon authored Jun 18, 2024
2 parents 2b3793c + 5f16cb2 commit 207ff28
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 74 deletions.
13 changes: 8 additions & 5 deletions frontend/src/components/landing/member/LandingMember.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import useUpdateUserStatus from "../../../hooks/common/member/useUpdateUserStatu
import { DEFAULT_MEMBER } from "../../../constants/projects";
import { memberResponse } from "../../../types/DTO/authDTO";
import emitMemberStatusUpdate from "../../../utils/emitMemberStatusUpdate";
import { STORAGE_KEY } from "../../../constants/storageKey";

interface LandingMemberProps {
projectTitle: string;
Expand Down Expand Up @@ -56,18 +57,20 @@ const LandingMember = ({ projectTitle }: LandingMemberProps) => {
};

function selectStatusOption(option: string) {
emitMemberStatusUpdate(socket, {
...myInfo,
status: USER_WORD_STATUS[option],
});

if (option === USER_STATUS_WORD.away || option === USER_STATUS_WORD.off) {
handleCanAddStatusEventListener(false);
localStorage.setItem(STORAGE_KEY.UPDATE_STATUS_WITH_INTENTION, "true");
removeUserStatusEventListener();
} else {
handleCanAddStatusEventListener(true);
localStorage.removeItem(STORAGE_KEY.UPDATE_STATUS_WITH_INTENTION);
addUserStatusEventListener();
}

emitMemberStatusUpdate(socket, {
...myInfo,
status: USER_WORD_STATUS[option],
});
}

return (
Expand Down
18 changes: 8 additions & 10 deletions frontend/src/components/landing/member/UserBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,17 @@ const UserStateDisplay = ({ status }: { status: "on" | "off" | "away" }) => {
return (
<div className="flex gap-2 items-center w-[4.0625rem]">
<div className={`w-3 h-3 rounded-full ${bgColor}`} />
<p className="text-xxxs font-semibold">{text}</p>
<p className="font-semibold text-xxxs">{text}</p>
</div>
);
};

const UserBlock = ({ imageUrl, username, status }: UserBlockProps) => {
return (
<div className="w-full flex justify-between items-center bg-white rounded-lg p-3 shadow-box">
<ProfileImage imageUrl={imageUrl} pxSize={40} />
<p className="text-xs font-bold text-middle-green">{username}</p>
<UserStateDisplay status={status} />
</div>
);
};
const UserBlock = ({ imageUrl, username, status }: UserBlockProps) => (
<div className="flex items-center justify-between w-full p-3 bg-white rounded-lg shadow-box">
<ProfileImage imageUrl={imageUrl} pxSize={40} />
<p className="text-xs font-bold text-middle-green">{username}</p>
<UserStateDisplay status={status} />
</div>
);

export default UserBlock;
1 change: 1 addition & 0 deletions frontend/src/constants/storageKey.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export const STORAGE_KEY = {
MEMBER: "member",
REDIRECT: "redirect",
UPDATE_STATUS_WITH_INTENTION: "updateStatusWithIntention",
};
8 changes: 8 additions & 0 deletions frontend/src/hooks/common/member/useAwayUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Socket } from "socket.io-client";
import useMemberStore from "../../../stores/useMemberStore";
import useThrottle from "../throttle/useThrottle";
import emitMemberStatusUpdate from "../../../utils/emitMemberStatusUpdate";
import { STORAGE_KEY } from "../../../constants/storageKey";

const useAwayUser = (socket: Socket) => {
const timerRef = useRef<NodeJS.Timeout | null>(null);
Expand Down Expand Up @@ -70,6 +71,13 @@ const useAwayUser = (socket: Socket) => {
addUserStatusEventListener();
}

if (
localStorage.getItem(STORAGE_KEY.UPDATE_STATUS_WITH_INTENTION) === "true"
) {
removeUserStatusEventListener();
return;
}

return () => {
removeUserStatusEventListener();
};
Expand Down
27 changes: 15 additions & 12 deletions frontend/src/hooks/common/member/useUpdateUserStatus.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useEffect, useRef } from "react";
import { Socket } from "socket.io-client";
import {
LandingSocketData,
Expand All @@ -6,8 +7,8 @@ import {
} from "../../../types/common/landing";
import { LandingDTO, LandingMemberDTO } from "../../../types/DTO/landingDTO";
import useMemberStore from "../../../stores/useMemberStore";
import { useEffect, useRef } from "react";
import { USER_STATUS_WORD } from "../../../constants/landing";
import sortMemberByStatus from "../../../utils/sortMemberByStatus";

const useUpdateUserStatus = (
socket: Socket,
Expand All @@ -22,11 +23,11 @@ const useUpdateUserStatus = (
addMember,
} = useMemberStore();
const inviteLinkIdRef = useRef<string>("");

const handleInitEvent = (content: LandingDTO) => {
const { myInfo, member: memberList, inviteLinkId } = content;
updateMyInfo(myInfo);
updateMemberList(memberList);
handleChangeStatus(USER_STATUS_WORD[myInfo.status]);
updateMemberList(memberList.sort(sortMemberByStatus));
inviteLinkIdRef.current = inviteLinkId;
};

Expand All @@ -51,16 +52,18 @@ const useUpdateUserStatus = (
}

updateMemberList(
memberList.map((member) => {
if (member.id === content.id) {
return {
...member,
status: (content as LandingMemberDTO).status,
};
}
memberList
.map((member) => {
if (member.id === content.id) {
return {
...member,
status: (content as LandingMemberDTO).status,
};
}

return member;
})
return member;
})
.sort(sortMemberByStatus)
);

break;
Expand Down
50 changes: 3 additions & 47 deletions frontend/src/hooks/common/socket/useLandingSocket.ts
Original file line number Diff line number Diff line change
@@ -1,68 +1,27 @@
import { Socket } from "socket.io-client";
import { useEffect, useState } from "react";
import {
LandingDTO,
LandingLinkDTO,
LandingMemoDTO,
LandingProjectDTO,
LandingSprintDTO,
} from "../../../types/DTO/landingDTO";
import { LandingDTO, LandingProjectDTO } from "../../../types/DTO/landingDTO";
import { DEFAULT_VALUE } from "../../../constants/landing";
import {
LandingSocketData,
LandingSocketDomain,
LandingSocketMemoAction,
} from "../../../types/common/landing";

const useLandingSocket = (socket: Socket) => {
const [project, setProject] = useState<LandingProjectDTO>(
DEFAULT_VALUE.PROJECT
);
const [sprint, setSprint] = useState<LandingSprintDTO | null>(null);
const [memoList, setMemoList] = useState<LandingMemoDTO[]>([]);
const [link, setLink] = useState<LandingLinkDTO[]>([]);

const handleInitEvent = (content: LandingDTO) => {
const { project, sprint, memoList, link } = content as LandingDTO;
const { project } = content as LandingDTO;
setProject(project);
setSprint(sprint);
setMemoList(memoList);
setLink(link);
};

const handleMemoEvent = (
action: LandingSocketMemoAction,
content: LandingMemoDTO
) => {
switch (action) {
case LandingSocketMemoAction.CREATE:
setMemoList((memoList: LandingMemoDTO[]) => [content, ...memoList]);
break;
case LandingSocketMemoAction.DELETE:
setMemoList((memoList: LandingMemoDTO[]) =>
memoList.filter((memo: LandingMemoDTO) => memo.id !== content.id)
);
break;
case LandingSocketMemoAction.COLOR_UPDATE:
setMemoList((memoList: LandingMemoDTO[]) =>
memoList.map((memo: LandingMemoDTO) => {
if (memo.id !== content.id) {
return memo;
}
return { ...memo, color: content.color };
})
);
}
};

const handleOnLanding = ({ domain, action, content }: LandingSocketData) => {
const handleOnLanding = ({ domain, content }: LandingSocketData) => {
switch (domain) {
case LandingSocketDomain.INIT:
handleInitEvent(content);
break;
case LandingSocketDomain.MEMO:
handleMemoEvent(action, content);
break;
}
};

Expand All @@ -77,9 +36,6 @@ const useLandingSocket = (socket: Socket) => {

return {
project,
sprint,
memoList,
link,
};
};

Expand Down
20 changes: 20 additions & 0 deletions frontend/src/test/sortMemberByStatus.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { LandingMemberDTO } from "../types/DTO/landingDTO";
import sortMemberByStatus from "../utils/sortMemberByStatus";

describe("sortMemberByStatus test", () => {
it("on, away, off 순 정렬 테스트", () => {
const memberList: LandingMemberDTO[] = [
{ id: 1, username: "", imageUrl: "", status: "off" },
{ id: 2, username: "", imageUrl: "", status: "on" },
{ id: 3, username: "", imageUrl: "", status: "away" },
{ id: 4, username: "", imageUrl: "", status: "on" },
{ id: 5, username: "", imageUrl: "", status: "off" },
{ id: 6, username: "", imageUrl: "", status: "away" },
];

const sortedMemberIdList = memberList
.sort(sortMemberByStatus)
.map(({ id }) => id);
expect(sortedMemberIdList).toStrictEqual([2, 4, 3, 6, 1, 5]);
});
});
32 changes: 32 additions & 0 deletions frontend/src/utils/sortMemberByStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { LandingMemberDTO, MemberStatus } from "../types/DTO/landingDTO";

const getStatusOrder = (status: MemberStatus) => {
if (status === "on") {
return 2;
}

if (status === "away") {
return 1;
}

return 0;
};

const sortMemberByStatus = (
member1: LandingMemberDTO,
member2: LandingMemberDTO
) => {
const member1Status = getStatusOrder(member1.status);
const member2Status = getStatusOrder(member2.status);
if (member1Status > member2Status) {
return -1;
}

if (member1Status < member2Status) {
return 1;
}

return 0;
};

export default sortMemberByStatus;

0 comments on commit 207ff28

Please sign in to comment.