Skip to content

Commit

Permalink
Merge pull request #276 from WePlanPlans/dev
Browse files Browse the repository at this point in the history
Refator: 소켓 최적화(소켓새로고침 최적화, 디바운싱, 쓰로틀링), 커서공유 구현
  • Loading branch information
LeHiHo authored Jan 26, 2024
2 parents c5bd8f2 + 600c7cd commit cfb4260
Show file tree
Hide file tree
Showing 17 changed files with 364 additions and 70 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@
"@types/react-beautiful-dnd": "^13.1.8",
"axios": "^1.6.2",
"date-fns": "^3.1.0",
"lodash": "^4.17.21",
"msw": "0.36.3",
"path": "^0.12.7",
"react": "^18.2.0",
"react-beautiful-dnd": "^13.1.1",
"react-dom": "^18.2.0",
"react-hook-form": "^7.49.2",
"react-icons": "^5.0.1",
"react-infinite-scroller": "^1.2.6",
"react-kakao-maps-sdk": "^1.1.24",
"react-modal": "^3.16.1",
Expand All @@ -52,6 +54,7 @@
"websocket": "^1.0.34"
},
"devDependencies": {
"@types/lodash": "^4.14.202",
"@types/react": "^18.2.43",
"@types/react-date-range": "^1.4.9",
"@types/react-dom": "^18.2.17",
Expand Down
21 changes: 21 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions src/@types/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,27 @@ export type subBudgetRes = {
} | null;
};

export type subCursorRes = {
status: number;
message: string;
data: {
color: string;
tripId: string;
visitDate: string;
memberId: number;
name: string;
x: number;
y: number;
} | null;
};

export type SocketContextType = {
tripInfo: subInfoRes | null;
tripItem: subItemRes | null;
tripPath: subPathRes | null;
tripMember: subMemberRes | null;
tripBudget: subBudgetRes | null;
tripCursor: subCursorRes | null;
tripId: string;
callBackPub: (callback: () => void) => void;
};
Expand Down
21 changes: 21 additions & 0 deletions src/@types/socket.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,20 @@ type subBudgetMessage = (response: {
};
}) => void;

type subCursorMessage = (response: {
status: number;
message: string;
data: {
color: string;
tripId: string;
visitDate: string;
memberId: number;
name: string;
x: number;
y: number;
};
}) => void;

interface pubInfo {
startDate: string;
endDate: string;
Expand Down Expand Up @@ -138,3 +152,10 @@ interface pubGetPathAndItems {
interface pubUpdateBudget {
budget: number;
}

interface pubCursor {
token: string;
visitDate: string;
x: number;
y: number;
}
21 changes: 21 additions & 0 deletions src/api/socket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,18 @@ export const subBudget = (
});
};

// 커서 공유
export const subCursor = (
tripId: string,
visitDate: string,
subCursorMessage: subCursorMessage,
) => {
socketClient.subscribe(`/sub/${tripId}/cursor/${visitDate}`, (message) => {
const res = JSON.parse(message.body);
subCursorMessage(res);
});
};

// 소켓 전송
// 여정 기본 정보 변경 이벤트 발생시
export const pubInfo = (pubInfo: pubInfo, tripId: string) => {
Expand Down Expand Up @@ -101,6 +113,7 @@ export const pubUpdateTripItem = (
destination: `/pub/trips/${tripId}/updateTripItemOrder`,
body: JSON.stringify(pubUpdateTripItem),
});
console.log('실행');
};

// 여행 날짜별 교통 수단 변경 이벤트 발생시 (01/16 업데이트)
Expand Down Expand Up @@ -187,3 +200,11 @@ export const pubUpdateBudget = (
body: JSON.stringify(pubUpdateBudget),
});
};

// 커서공유
export const pubCursor = (pubCursor: pubCursor, tripId: string) => {
socketClient.publish({
destination: `/pub/trips/${tripId}/cursor`,
body: JSON.stringify(pubCursor),
});
};
2 changes: 1 addition & 1 deletion src/components/DetailSectionTop/DetailAddSchedule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ const DetailAddSchedule = () => {
key={index}
className="flex w-[99px] items-start">
<button
className={`relative flex h-10 flex-shrink-0 flex-grow-0 items-center justify-center gap-1 rounded-[168px] border-[1.25px] border-solid ${
className={`relative flex h-10 w-[99px] flex-shrink-0 flex-grow-0 items-center justify-center gap-1 rounded-[168px] border-[1.25px] border-solid ${
index === selectedButton
? 'border-main2'
: 'border-gray3'
Expand Down
114 changes: 114 additions & 0 deletions src/components/Plan/PlanCursor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { useEffect, useState, useContext, RefObject } from 'react';
import { BsFillCursorFill } from 'react-icons/bs';
import { pubCursor } from '@api/socket';
import { socketContext } from '@hooks/useSocket';
import { useGetTripsAuthority } from '@hooks/useGetTripsAuthority';
import { useRecoilValue } from 'recoil';
import { visitDateState } from '@recoil/socket';
import { throttle } from 'lodash';

type PlanCursorProps = {
props: RefObject<HTMLDivElement>;
};

const PlanCursor = ({ props }: PlanCursorProps) => {
const visitDate = useRecoilValue(visitDateState);
const token = localStorage.getItem('accessToken');
const { memberId } = useGetTripsAuthority();
const { tripId, tripMember } = useContext(socketContext);
const [position, setPosition] = useState({ myX: 0, myY: 0 });

const myName = tripMember?.data?.tripMembers.find(
(member) => member.memberId === memberId,
);

const throttledPubCursor = throttle((x, y) => {
if (token && visitDate) {
pubCursor(
{
token: token,
visitDate: visitDate.visitDate,
x,
y,
},
tripId,
);
}
}, 50);

useEffect(() => {
const handleMouseMove = (e: MouseEvent) => {
const myX = e.clientX;
const myY = e.clientY;
setPosition({ myX, myY });

const x = (e.clientX + window.scrollX) / window.innerWidth;
const y = (e.clientY + window.scrollY) / window.innerHeight;

if (token && myName && visitDate && tripId) {
throttledPubCursor(x, y);
}
};

const handleMouseEnter = () => {
cursorStyle('none');
};

const handleMouseLeave = () => {
cursorStyle('auto');
};

const cursorArea = props.current;

const cursorStyle = (style: string): void => {
document.querySelectorAll('*').forEach((el) => {
const element = el as HTMLElement;
element.style.cursor = style;
});
};

if (cursorArea) {
cursorArea.addEventListener('mousemove', handleMouseMove);
cursorArea.addEventListener('mouseenter', handleMouseEnter);
cursorArea.addEventListener('mouseleave', handleMouseLeave);
}

return () => {
if (cursorArea) {
cursorStyle('auto');
cursorArea.removeEventListener('mousemove', handleMouseMove);
cursorArea.removeEventListener('mouseenter', handleMouseEnter);
cursorArea.removeEventListener('mouseleave', handleMouseLeave);
}
};
}, [token, myName, visitDate, tripId]);

const getColorByMemberId = (memberId: number | null) => {
if (memberId == null) {
return 'black';
}

const colors = ['#FF2167', '#7932FF', '#29DDF6', '#FFAC16', '#16E7A9'];
const remainder = memberId % 5;
return colors[remainder];
};

return (
<div
className="pointer-events-none fixed z-50 w-full -translate-x-2 -translate-y-2 transform"
style={{ left: `${position.myX}px`, top: `${position.myY}px` }}>
<BsFillCursorFill
size={15}
color={getColorByMemberId(memberId)}
className="scale-x-[-1]"
/>
<div
className="text-bold absolute left-1 top-2 p-1 text-center text-xs"
style={{ color: getColorByMemberId(memberId) }}>
{myName?.name}
</div>
</div>
);
};

export default PlanCursor;
26 changes: 14 additions & 12 deletions src/components/Plan/PlanEditItemBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@ import {
} from 'react-beautiful-dnd';
import { useState, useEffect } from 'react';
import { pubUpdateTripItem, pubDeleteItem } from '@api/socket';
import { useContext } from 'react';
import { socketContext } from '@hooks/useSocket';
import { pubUpdateTripItemReq } from '@/@types/service';
import Alert from '@components/common/alert/Alert';
import ToastPopUp from '@components/common/toastpopup/ToastPopUp';
import PlanMoveItem from './PlanMoveItem';
import { useRecoilState } from 'recoil';
import { isEditState } from '@recoil/socket';
import { debounce } from 'lodash';

type PlanItemBoxProps = {
item: TripItem[];
Expand All @@ -34,7 +33,6 @@ const PlanEditItemBox = ({
return <div>Missing data</div>;
}

const { callBackPub } = useContext(socketContext);
const [, setIsEdit] = useRecoilState(isEditState);
const [items, setItems] = useState(item);
const [newData, setNewData] = useState<pubUpdateTripItemReq | null>(null);
Expand Down Expand Up @@ -63,17 +61,19 @@ const PlanEditItemBox = ({
});
};

const debouncedPubUpdateTripItem = debounce((newData, tripId) => {
pubUpdateTripItem(newData, tripId);
}, 1000);

useEffect(() => {
if (newData && tripId) {
callBackPub(() => pubUpdateTripItem(newData, tripId));
debouncedPubUpdateTripItem(newData, tripId);
}
}, [newData]);

const handleConfirm = () => {
if (tripId && visitDate && selectedItemId) {
callBackPub(() =>
pubDeleteItem({ tripId: tripId, visitDate: visitDate }, selectedItemId),
);
pubDeleteItem({ tripId: tripId, visitDate: visitDate }, selectedItemId);
}
setToastPopUp(() => ({
isPopUp: true,
Expand Down Expand Up @@ -132,22 +132,24 @@ const PlanEditItemBox = ({
checked={selectedItemId === item.tripItemId}></input>
</div>
<div className="flex w-full flex-col">
<div className="mb-[8px] flex h-[87.5px] rounded-lg border border-solid border-[#ededed] bg-white">
<div className="mb-[8px] flex h-[88.5px] rounded-lg border border-solid border-[#ededed] bg-white">
<img
className="h-[87px] w-[93px] rounded-bl-lg rounded-tl-lg "
src={item.thumbnailUrl}
alt="img"
/>
<div className="flex w-full flex-col p-[10px]">
<div className="flex h-[88px] w-full flex-col px-[10px] py-[8px]">
<div className="flex text-left text-[14px] font-medium text-black">
{item.name.length > 17
? item.name.slice(0, 17) + '...'
: item.name}
</div>
<div className="mt-[3px] flex h-fit w-fit items-center justify-center gap-2 rounded-[3px] bg-[#ededed] p-[4px] text-center text-[11px] text-black">
{item.category}
<div className="mb-[11px] mt-[4px] flex h-[16px] w-fit items-center justify-center rounded-[3px] bg-[#ededed] px-[4px] py-[8px] text-center text-[11px] text-black">
<div className="flex h-[13px] items-center justify-center text-center">
{item.category}
</div>
</div>
<div className="mt-[15px] text-sm font-bold text-black">
<div className="flex justify-between text-sm font-bold text-black">
{item.price}
</div>
</div>
Expand Down
Loading

0 comments on commit cfb4260

Please sign in to comment.