Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions src/components/icons/Help.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,26 @@ export default function DTHelp({
}: IconProps) {
return (
<svg
viewBox="0 0 45 44"
viewBox="0 0 31 32"
fill="none"
className={`
aspect-[45/44]
aspect-[31/32]
${className}
`}
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M22.7693 38C31.6058 38 38.7693 30.8366 38.7693 22C38.7693 13.1634 31.6058 6 22.7693 6C13.9327 6 6.76929 13.1634 6.76929 22C6.76929 30.8366 13.9327 38 22.7693 38Z"
d="M15.7693 29.8182C23.4009 29.8182 29.5875 23.6316 29.5875 16C29.5875 8.36843 23.4009 2.18182 15.7693 2.18182C8.13771 2.18182 1.9511 8.36843 1.9511 16C1.9511 23.6316 8.13771 29.8182 15.7693 29.8182Z"
stroke={color}
strokeWidth="4.21053"
strokeWidth="2.7"
strokeMiterlimit="10"
/>
<path
d="M20.6414 25.3928C20.6414 22.2696 21.4862 21.3224 22.9966 20.3752C24.0718 19.684 24.891 18.916 24.891 17.764C24.891 16.484 23.9182 15.6904 22.6894 15.6648C21.6319 15.6883 20.6391 16.3374 20.424 17.5726C20.3906 17.7642 20.233 17.9176 20.0385 17.9176H17.2264C17.0213 17.9176 16.8552 17.7481 16.8709 17.5436C17.1194 14.2902 19.6565 12.7217 22.7406 12.7464C26.2222 12.7208 28.6798 14.5384 28.6798 17.6104C28.6798 19.684 27.6302 20.9896 25.9662 21.9624C24.5838 22.8072 23.9438 23.6264 23.9182 25.3928C23.9182 25.5342 23.8036 25.6488 23.6622 25.6488H20.8974C20.756 25.6488 20.6414 25.5342 20.6414 25.3928ZM20.283 29.1816C20.2318 28.0552 21.2046 27.108 22.3822 27.1336C23.483 27.108 24.4558 28.0552 24.4814 29.1816C24.4558 30.3592 23.483 31.3064 22.3822 31.2808C21.2046 31.3064 20.2318 30.3592 20.283 29.1816Z"
d="M15.4339 20.4836H15.4358C16.3579 20.4621 17.176 21.2572 17.1975 22.2033C17.1749 23.1934 16.3565 23.9864 15.4358 23.965H15.4339C14.5061 23.9852 13.7318 23.2873 13.6741 22.3869L13.6721 22.2043V22.2004C13.632 21.3177 14.3449 20.5617 15.2503 20.4885L15.4339 20.4836ZM15.7444 8.0578H15.7454C17.2403 8.04689 18.5103 8.43236 19.4055 9.14471C20.2996 9.8562 20.8235 10.8973 20.8235 12.2092C20.8235 13.0954 20.5998 13.8153 20.1995 14.4172C19.7988 15.0196 19.2193 15.5065 18.5051 15.924L18.5042 15.925C17.9047 16.2913 17.4605 16.6552 17.1643 17.1232C16.8678 17.5921 16.7223 18.1611 16.7112 18.9299L16.6985 18.9963C16.6727 19.058 16.6114 19.1008 16.5403 19.1008H14.1526C14.0581 19.1007 13.9817 19.0243 13.9817 18.9299C13.9817 17.5851 14.1634 16.7157 14.5032 16.0822C14.8002 15.5286 15.2205 15.151 15.7561 14.7922L15.9924 14.6389C16.9222 14.0412 17.6515 13.3649 17.6516 12.342C17.6516 11.7757 17.4357 11.3145 17.0823 10.9924C16.7294 10.6708 16.2423 10.49 15.7014 10.4787H15.6995C14.7663 10.4994 13.8851 11.0742 13.6946 12.1682C13.6691 12.3133 13.5503 12.424 13.4104 12.424H10.9827C10.833 12.424 10.7139 12.3012 10.7249 12.1555C10.8312 10.7641 11.426 9.73588 12.3215 9.0578C13.2182 8.37884 14.4207 8.04721 15.7444 8.0578Z"
fill={color}
stroke={color}
strokeWidth="0.1"
/>
</svg>
);
Expand Down
8 changes: 4 additions & 4 deletions src/components/icons/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@ export default function DTHome({
}: IconProps) {
return (
<svg
viewBox="0 0 45 44"
viewBox="0 0 31 32"
fill="none"
className={`
aspect-[45/44]
aspect-[31/32]
${className}
`}
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M6.42554 21.7278V36.2518C6.42554 37.2196 7.18999 38 8.13791 38H16.868C17.8159 38 18.5803 37.2196 18.5803 36.2518V29.3137C18.5803 28.346 19.3448 27.5655 20.2927 27.5655H24.5584C25.5063 27.5655 26.2707 28.346 26.2707 29.3137V36.2518C26.2707 37.2196 27.0352 38 27.9831 38H36.7132C37.6611 38 38.4255 37.2196 38.4255 36.2518V21.7278C38.4255 21.2518 38.2344 20.7991 37.8981 20.4635L23.6104 6.48582C22.9454 5.83806 21.9057 5.83806 21.2406 6.48582L6.95301 20.4713C6.61665 20.7991 6.42554 21.2596 6.42554 21.7356V21.7278Z"
d="M1.60736 15.7649V28.3084C1.60736 29.1442 2.26756 29.8182 3.08622 29.8182H10.6258C11.4445 29.8182 12.1047 29.1442 12.1047 28.3084V22.3164C12.1047 21.4806 12.7649 20.8066 13.5836 20.8066H17.2675C18.0862 20.8066 18.7464 21.4806 18.7464 22.3164V28.3084C18.7464 29.1442 19.4066 29.8182 20.2253 29.8182H27.7648C28.5835 29.8182 29.2437 29.1442 29.2437 28.3084V15.7649C29.2437 15.3538 29.0787 14.9629 28.7882 14.673L16.4489 2.6014C15.8745 2.04197 14.9766 2.04197 14.4022 2.6014L2.0629 14.6798C1.77241 14.9629 1.60736 15.3605 1.60736 15.7717V15.7649Z"
stroke={color}
strokeWidth="4.21053"
strokeWidth="2.7"
strokeMiterlimit="10"
/>
</svg>
Expand Down
20 changes: 10 additions & 10 deletions src/components/icons/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,40 @@ export default function DTLogin({
}: IconProps) {
return (
<svg
viewBox="0 0 45 44"
viewBox="0 0 28 30"
fill="none"
className={`
aspect-[45/44]
aspect-[28/30]
${className}
`}
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M33.7567 32.464V35.681C33.7567 36.7666 33.1532 37.6466 32.4007 37.6466H9.78112C9.03607 37.6466 8.42514 36.7666 8.42514 35.681V8.31899C8.42514 7.23339 9.02862 6.35339 9.78112 6.35339H32.4081C33.1532 6.35339 33.7641 7.23339 33.7641 8.31899V11.536"
d="M24.2115 24.0371V26.8155C24.2115 27.753 23.6903 28.513 23.0404 28.513H3.50533C2.86189 28.513 2.33426 27.753 2.33426 26.8155V3.18459C2.33426 2.24702 2.85545 1.48703 3.50533 1.48703H23.0468C23.6903 1.48703 24.2179 2.24702 24.2179 3.18459V5.96294"
stroke={color}
strokeWidth="4.11753"
strokeWidth="2.7"
strokeMiterlimit="10"
strokeLinecap="round"
/>
<path
d="M36.4256 22.0014L30.0853 26.9688"
d="M26.5165 15.0012L21.0407 19.2913"
stroke={color}
strokeWidth="4.11753"
strokeWidth="2.7"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M36.4256 21.9977L30.0853 17.0385"
d="M26.5165 14.998L21.0407 10.715"
stroke={color}
strokeWidth="4.11753"
strokeWidth="2.7"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M16.4389 22.0014H34.2827"
d="M9.25525 15.0012H24.6658"
stroke={color}
strokeWidth="4.11753"
strokeWidth="2.7"
strokeMiterlimit="10"
strokeLinecap="round"
/>
Expand Down
105 changes: 105 additions & 0 deletions src/hooks/useFullscreen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { useCallback, useLayoutEffect, useState } from 'react';
import {
DocumentWithFullscreen,
HTMLElementWithFullscreen,
} from '../type/fullscreen';

// 헬퍼 함수: 현재 전체 화면 요소가 무엇인지 반환 (없으면 null)
const getFullscreenElement = (): Element | null => {
const doc = document as DocumentWithFullscreen;
return (
doc.fullscreenElement ||
doc.webkitFullscreenElement ||
doc.mozFullScreenElement ||
doc.msFullscreenElement ||
null
);
};

// 헬퍼 함수: 전체 화면 진입
const enterFullscreen = async (element: HTMLElementWithFullscreen) => {
try {
if (element.requestFullscreen) {
await element.requestFullscreen();
} else if (element.webkitRequestFullscreen) {
await element.webkitRequestFullscreen(); // Safari
} else if (element.mozRequestFullScreen) {
await element.mozRequestFullScreen(); // Firefox
} else if (element.msRequestFullscreen) {
await element.msRequestFullscreen(); // IE11
}
} catch (error) {
console.error('# Failed to enter fullscreen mode:', error);
}
};

// 헬퍼 함수: 전체 화면 해제
const exitFullscreen = async () => {
const doc = document as DocumentWithFullscreen;
try {
if (doc.exitFullscreen) {
await doc.exitFullscreen();
} else if (doc.webkitExitFullscreen) {
await doc.webkitExitFullscreen(); // Safari
} else if (doc.mozCancelFullScreen) {
await doc.mozCancelFullScreen(); // Firefox
} else if (doc.msExitFullscreen) {
await doc.msExitFullscreen(); // IE11
}
} catch (error) {
console.error('# Failed to exit fullscreen mode:', error);
}
};

export default function useFullscreen() {
// 전체 화면 여부를 묘사하는 변수
const [isFullscreen, setIsFullscreen] = useState(!!getFullscreenElement());
const handleFullscreenChange = useCallback(() => {
setIsFullscreen(!!getFullscreenElement());
}, []);

// 이벤트 리스너 등록
useLayoutEffect(() => {
const EVENTS = [
'fullscreenchange',
'webkitfullscreenchange',
'mozfullscreenchange',
'MSFullscreenChange',
];

EVENTS.forEach((event) => {
document.addEventListener(event, handleFullscreenChange);
});

return () => {
EVENTS.forEach((event) => {
document.removeEventListener(event, handleFullscreenChange);
});
};
}, [handleFullscreenChange]);

// 토글 함수
const toggleFullscreen = useCallback(async () => {
const element = document.documentElement as HTMLElementWithFullscreen;

if (isFullscreen) {
await exitFullscreen();
} else {
await enterFullscreen(element);
}
}, [isFullscreen]);

// 값을 직접 입력하는 함수
const setFullscreen = useCallback(async (value: boolean) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

다른 곳에서 setFullscreen을 보고 state 값을 설정한다고 느껴졌어요!! 실제 역할은 단순 상태 변경이 아니고 set함수를 넘긴 것도 아니고 true/false에 따라 전체 화면을 켜고 끄는 함수 호출이니 의도를 담은 함수명이 좋을 것 같은데 어떻게 생각하시나요? 지금 당장 생각나는 것은 handleFullscreen과 같은 ,, ?!!?

const element = document.documentElement as HTMLElementWithFullscreen;
const isCurrentlyFullscreen = !!getFullscreenElement();

if (value && !isCurrentlyFullscreen) {
await enterFullscreen(element);
} else if (!value && isCurrentlyFullscreen) {
await exitFullscreen();
}
}, []);

return { isFullscreen, toggleFullscreen, setFullscreen };
}
18 changes: 15 additions & 3 deletions src/layout/components/header/StickyTriSectionHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { useModal } from '../../../hooks/useModal';
import DialogModal from '../../../components/DialogModal/DialogModal';
import DTHome from '../../../components/icons/Home';
import DTLogin from '../../../components/icons/Login';
import useFullscreen from '../../../hooks/useFullscreen';

// The type of header icons will be declared here.
type HeaderIcons = 'home' | 'auth';
Expand Down Expand Up @@ -50,6 +51,7 @@ StickyTriSectionHeader.Right = function Right(props: PropsWithChildren) {
const navigate = useNavigate();
const { mutate: logoutMutate } = useLogout(() => navigate('/home'));
const { openModal, closeModal, ModalWrapper } = useModal({});
const { isFullscreen, setFullscreen } = useFullscreen();
const defaultIcons: HeaderIcons[] = ['home', 'auth'];

const handleLoginStart = (keepData: boolean) => {
Expand All @@ -64,7 +66,7 @@ StickyTriSectionHeader.Right = function Right(props: PropsWithChildren) {
{isGuestFlow() && (
<>
{/* Guest mode indicator */}
<div className="animate-pulse rounded-full bg-neutral-300 px-4 py-2 font-semibold">
<div className="animate-pulse whitespace-nowrap rounded-full bg-neutral-300 px-4 py-2 font-semibold">
비회원 모드
</div>

Expand All @@ -82,9 +84,14 @@ StickyTriSectionHeader.Right = function Right(props: PropsWithChildren) {
case 'home':
return (
<button
className="flex h-full items-center justify-center"
className="flex h-full items-center justify-center p-[4px]"
key={`${iconName}-${index}`}
onClick={() => {
// 전체 화면 상태에서 홈으로 이동하는 경우, 전체 화면 비활성화
if (isFullscreen) {
setFullscreen(false);
}

if (isGuestFlow()) {
deleteSessionCustomizeTableData();
}
Expand All @@ -97,9 +104,14 @@ StickyTriSectionHeader.Right = function Right(props: PropsWithChildren) {
case 'auth':
return (
<button
className="flex h-full items-center justify-center"
className="flex h-full items-center justify-center p-[4px]"
key={`${iconName}-${index}`}
onClick={() => {
// 전체 화면 상태에서 홈으로 이동하는 경우, 전체 화면 비활성화
if (isFullscreen) {
setFullscreen(false);
}

if (isLoggedIn()) {
logoutMutate();
} else {
Expand Down
39 changes: 35 additions & 4 deletions src/page/TimerPage/TimerPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import DTHelp from '../../components/icons/Help';
import clsx from 'clsx';
import ErrorIndicator from '../../components/ErrorIndicator/ErrorIndicator';
import LoadingIndicator from '../../components/LoadingIndicator/LoadingIndicator';
import { RiFullscreenFill, RiFullscreenExitFill } from 'react-icons/ri';

export default function TimerPage() {
const pathParams = useParams();
Expand All @@ -30,7 +31,18 @@ export default function TimerPage() {
const state = useTimerPageState(tableId);

useTimerHotkey(state);
const { data, bg, index, goToOtherItem, isLoading, isError, refetch } = state;
const {
data,
bg,
index,
goToOtherItem,
isLoading,
isError,
refetch,
isFullscreen,
setFullscreen,
toggleFullscreen,
} = state;

// If error, print error message and let user be able to retry
if (isError) {
Expand Down Expand Up @@ -72,10 +84,22 @@ export default function TimerPage() {
</DefaultLayout.Header.Center>
<DefaultLayout.Header.Right>
<button
className="flex h-full items-center justify-center"
className="flex h-full items-center justify-center p-[4px]"
title="도움말"
onClick={openUseTooltipModal}
>
<DTHelp className="size-full" />
<DTHelp className="h-full" />
</button>
<button
className="flex aspect-square h-full items-center justify-center p-[4px]"
title="전체 화면"
onClick={toggleFullscreen}
>
{isFullscreen ? (
<RiFullscreenExitFill className="h-full w-full" />
) : (
<RiFullscreenFill className="h-full w-full" />
)}
</button>
</DefaultLayout.Header.Right>
</DefaultLayout.Header>
Expand All @@ -98,7 +122,14 @@ export default function TimerPage() {
table={data.table}
index={index}
goToOtherItem={goToOtherItem}
openDoneModal={openLoginAndStoreModalOrGoToDebateEndPage}
openDoneModal={() => {
// 전체 화면 상태에서 토론을 끝낼 경우, 전체 화면을 비활성화
if (isFullscreen) {
setFullscreen(false);
}

openLoginAndStoreModalOrGoToDebateEndPage();
}}
className="absolute bottom-[66px] left-1/2 -translate-x-1/2"
/>
)}
Expand Down
21 changes: 21 additions & 0 deletions src/page/TimerPage/components/FirstUseToolTip.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { PropsWithChildren } from 'react';
import { LuKeyboard } from 'react-icons/lu';
import { MdOutlineTimer } from 'react-icons/md';
import { RiFullscreenExitFill, RiFullscreenFill } from 'react-icons/ri';

// z-index
// - 30: Tooltip
Expand Down Expand Up @@ -68,6 +69,26 @@ export default function FirstUseToolTip({ onClose }: FirstUseToolTipProps) {
</div>
</div>

<div className="flex flex-col space-y-1 text-slate-50">
<div className="mb-2 flex flex-row items-center space-x-4">
<RiFullscreenFill size={18} />
<h1 className="text-xl font-bold">전체 화면</h1>
</div>

<div className="text-m flex flex-col space-y-1 md:text-lg">
<ListItem>
화면 우측 상단 헤더의 전체 화면 버튼
<RiFullscreenFill className="mx-[2px] self-center" />
으로 활성화
</ListItem>
<ListItem>
화면 우측 상단 헤더의 전체 화면 닫기 버튼
<RiFullscreenExitFill className="mx-[2px] self-center" />
또는 ESC 키를 눌러 전체 화면 비활성화
</ListItem>
</div>
</div>

<div className="flex justify-end">
<button
data-testid="tooltip-button"
Expand Down
Loading