Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
266dc85
feat: 전체 화면 로직을 useTimerPageState 훅에 추가
i-meant-to-be Sep 13, 2025
7e11110
design: 코인 토스 모달의 토론 정보 수정 버튼 배경 색 변경
i-meant-to-be Sep 13, 2025
a5407fb
design: 홈으로 돌아가기 버튼 스타일 통일
i-meant-to-be Sep 13, 2025
dc82289
fix: 피드백 타이머 시간 추가/감소 시 전체 시간 기준으로 초기화되게 변경
i-meant-to-be Sep 13, 2025
fec80cc
design: 헤더 아이콘 벡터 써니가 두께 줄인 것으로 변경
i-meant-to-be Sep 14, 2025
b0b4559
feat: 도움말에 전체 화면 관련 내용 추가
i-meant-to-be Sep 14, 2025
4d35c35
design: 아이콘에 Figma 시안대로 패딩 추가
i-meant-to-be Sep 14, 2025
8cf96cc
fix: 텍스트가 2줄 이상 늘어나는 문제 수정
i-meant-to-be Sep 23, 2025
33e5dba
fix: 누락된 의존성 배열 추가
i-meant-to-be Sep 23, 2025
b936bc2
fix: 아이콘 크기 문제 수정
i-meant-to-be Sep 23, 2025
76c6cc1
fix: 아이콘 벡터에서 빠진 색상 매개변수 추가
i-meant-to-be Sep 23, 2025
277725a
refactor: 전체화면 토글 함수 useCallback 적용하여 개선
i-meant-to-be Sep 23, 2025
534a001
fix: 전체화면 아이콘 너비 오류 수정
i-meant-to-be Sep 23, 2025
397f7c0
feat: 발언 유형 기본값에 교차 조사 추가
i-meant-to-be Sep 23, 2025
ddc8661
refactor: 헤더 버튼에 접근성 정보 추가
i-meant-to-be Sep 30, 2025
1722c9f
fix: 로고가 원형으로 잘리는 문제 수정
i-meant-to-be Sep 30, 2025
46a949a
fix: 시간 총량제 타이머 현재 시간 적응형 문제 수정
i-meant-to-be Sep 30, 2025
2b8fa07
design: 피드백 타이머 및 토론 종료 페이지 가운데 정렬
i-meant-to-be Sep 30, 2025
8311a08
fix: 순서 전환 시 프로그레스 바 튕기는 문제 수정
i-meant-to-be Sep 30, 2025
b00c45f
fix: 고정을 위한 태그를 absolute에서 fixed로 변경
i-meant-to-be Oct 1, 2025
cf91488
refactor: 전체화면 인터페이스 별도 파일로 분리
i-meant-to-be Oct 4, 2025
6659e78
refactor: 전체 화면 로직 별도 훅으로 분리
i-meant-to-be Oct 4, 2025
ae356a0
feat: 홈, 로그아웃 버튼 클릭 시 전체 화면 끄도록 구현
i-meant-to-be Oct 4, 2025
a3cc47a
feat: 토론 종료 시 전체 화면 끄도록 구현
i-meant-to-be Oct 4, 2025
8a52ffa
chore: 불필요한 주석 제거
i-meant-to-be Oct 4, 2025
86ec3c0
chore: 불필요한 태그 제거
i-meant-to-be Oct 4, 2025
a87031e
feat: 피드백 타이머가 전체 시간이 아니라 현재 시간 기준으로 재조정되게 변경
i-meant-to-be Oct 4, 2025
c38d776
fix: `timer` 값이 null일 경우의 로직 수정
i-meant-to-be Oct 4, 2025
0393991
Merge branch 'develop' into fix/#369
i-meant-to-be Oct 10, 2025
3c8d60c
Revert "Merge branch 'develop' into fix/#369"
i-meant-to-be Oct 10, 2025
f4f44b5
Merge branch 'feat/#378' into fix/#369
i-meant-to-be Oct 10, 2025
28456a1
fix: 사라진 패딩 복구
i-meant-to-be Oct 10, 2025
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
Binary file modified src/assets/template_logo/kondae_time.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions src/components/GoToHomeButton/GoToHomeButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ export default function GoToHomeButton() {
type="button"
aria-label="홈으로 돌아가기"
onClick={handleClick}
className="flex h-[72px] w-[492px] items-center justify-center gap-[12px] rounded-full border-[2px] border-default-disabled/hover bg-default-white px-[16px] py-[11px] font-semibold text-default-black opacity-80 transition-colors duration-200 hover:bg-default-disabled/hover"
className="button enabled neutral flex w-[492px] flex-row space-x-[12px] rounded-full p-[24px]"
>
<span>홈으로 돌아가기 →</span>
홈으로 돌아가기 →
</button>
);
}
6 changes: 5 additions & 1 deletion src/layout/components/header/StickyTriSectionHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ StickyTriSectionHeader.Right = function Right(props: PropsWithChildren) {
return (
<button
className="flex h-full items-center justify-center p-[4px]"
aria-label="홈으로 이동"
title="홈으로 이동"
key={`${iconName}-${index}`}
onClick={() => {
// 전체 화면 상태에서 홈으로 이동하는 경우, 전체 화면 비활성화
Expand All @@ -105,6 +107,8 @@ StickyTriSectionHeader.Right = function Right(props: PropsWithChildren) {
return (
<button
className="flex h-full items-center justify-center p-[4px]"
aria-label={isLoggedIn() ? '로그아웃' : '로그인'}
title={isLoggedIn() ? '로그아웃' : '로그인'}
key={`${iconName}-${index}`}
onClick={() => {
// 전체 화면 상태에서 홈으로 이동하는 경우, 전체 화면 비활성화
Expand All @@ -119,7 +123,7 @@ StickyTriSectionHeader.Right = function Right(props: PropsWithChildren) {
}
}}
>
<DTLogin className="h-full" />
<DTLogin className="h-full w-full" />
</button>
);
default:
Expand Down
7 changes: 4 additions & 3 deletions src/page/DebateEndPage/DebateEndPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default function DebateEndPage() {
return (
<div
style={backgroundStyle}
className="flex min-h-screen flex-col items-center justify-center p-4 font-pretendard"
className="relative flex h-screen w-full flex-col items-center justify-center p-4"
>
<div className="mb-12 flex items-center justify-center gap-4 text-center lg:mb-16 xl:mb-24">
<h1 className="text-3xl font-semibold text-default-black md:text-4xl lg:text-5xl xl:text-display-raw">
Expand Down Expand Up @@ -56,7 +56,7 @@ export default function DebateEndPage() {
{/* 승패투표 카드 */}
<button
onClick={handleFeedbackClick}
className="flex h-[280px] w-[280px] flex-col items-center justify-center gap-6 rounded-[34px] border-2 border-default-disabled/hover bg-[#e3e3e3] text-default-disabled/hover md:h-[300px] md:w-[300px] lg:h-[340px] lg:w-[340px] xl:h-[370px] xl:w-[370px] xl:gap-[30px]"
className="flex h-[280px] w-[280px] flex-col items-center justify-center gap-6 rounded-[34px] border-2 border-default-disabled/hover bg-[#e3e3e3] text-default-disabled/hover transition-all duration-300 md:h-[300px] md:w-[300px] lg:h-[340px] lg:w-[340px] xl:h-[370px] xl:w-[370px] xl:gap-[30px]"
disabled={true}
>
<img
Expand All @@ -79,7 +79,8 @@ export default function DebateEndPage() {
</p>
</button>
</div>
<div className="mt-12 lg:mt-16 xl:mt-[74px]">

<div className="fixed bottom-[8%] xl:bottom-[12%]">
<GoToHomeButton />
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/page/LandingPage/components/TemplateCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default function TemplateCard({
<img
src={logoSrc}
alt={`${title} 로고`}
className="h-12 w-12 shrink-0 rounded-full object-contain"
className="h-12 w-12 shrink-0 object-contain"
/>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,20 @@ type TimerCreationOption =
| 'TIME_NORMAL'
| 'BELL';

type SpeechType = 'OPENING' | 'REBUTTAL' | 'TIMEOUT' | 'CLOSING' | 'CUSTOM';
type SpeechType =
| 'OPENING'
| 'REBUTTAL'
| 'TIMEOUT'
| 'CLOSING'
| 'CROSS_EXAM'
| 'CUSTOM';

const SPEECH_TYPE_RECORD: Record<SpeechType, string> = {
OPENING: '입론',
CLOSING: '최종 발언',
CUSTOM: '직접 입력',
REBUTTAL: '반론',
CROSS_EXAM: '교차 조사',
TIMEOUT: '작전 시간',
} as const;

Expand Down Expand Up @@ -122,6 +129,9 @@ export default function TimerCreationContent({
case '작전시간':
case '작전 시간':
return 'TIMEOUT';
case '교차조사':
case '교차 조사':
return 'CROSS_EXAM';
default:
return 'CUSTOM';
}
Expand Down Expand Up @@ -234,6 +244,7 @@ export default function TimerCreationContent({
{ value: 'OPENING', label: SPEECH_TYPE_RECORD['OPENING'] },
{ value: 'REBUTTAL', label: SPEECH_TYPE_RECORD['REBUTTAL'] },
{ value: 'TIMEOUT', label: SPEECH_TYPE_RECORD['TIMEOUT'] },
{ value: 'CROSS_EXAM', label: SPEECH_TYPE_RECORD['CROSS_EXAM'] },
{ value: 'CLOSING', label: SPEECH_TYPE_RECORD['CLOSING'] },
{ value: 'CUSTOM', label: SPEECH_TYPE_RECORD['CUSTOM'] },
] as const;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ export default function TeamSelectionModal({
{(coinState === 'front' || coinState === 'back') && (
<div className="flex">
<button
className="sm:text-lg sm:py-4 w-full border-[2px] border-default-disabled/hover py-3 text-lg font-semibold hover:bg-default-disabled/hover md:py-5 md:text-xl lg:py-[21px] lg:text-[22px]"
className="sm:text-lg sm:py-4 w-full border-[2px] border-default-disabled/hover bg-default-white py-3 text-lg font-semibold hover:bg-default-disabled/hover md:py-5 md:text-xl lg:py-[21px] lg:text-[22px]"
Copy link
Contributor

Choose a reason for hiding this comment

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

이것과는 다른 이야기 일 수 있으나, 제 기억에는 DialogModal을 구현해주신 것으로 알고 있는데 현재 디자인과 유사한 지점이 있는 것 같습니다. 차이점은 오른쪽 버튼의 색상에 차이가 있는데 해당 DialogModal을 변경해서 이용 가능한지 궁금하네요

Copy link
Contributor

Choose a reason for hiding this comment

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

저도 DialogModal을 사용해서 현재 구현하고 있는데 버튼 색상을 변경할 수 있는 props를 추가할지 고민하고 있습니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

주신 의견대로 DialogModal로 대체를 시도해 봤는데요. 일단 DialogModal은 왼쪽 버튼과 오른쪽 버튼인 leftright를 인자로 받고 있기에, TeamSelectionModal의 '동전 던지기' 버튼처럼 모달 버튼이 1개인 시나리오는 대응 불가능합니다. 또한 말씀해주신 대로 버튼 색상에 대한 변경 사항도 반영이 필요합니다.

따라서 현 단계에서는 작업해야 할 것이 꽤 있어 리팩터링을 시도하진 않겠습니다. 다만, 추후 DialogModal을 더 확장성 있게 개선하는 방안은 고려 가능할 것 같습니다. 예를 들어, 하단 버튼이 들어가는 영역에 대한 정보를 leftright로 제공받지 않고, 그냥 JSX 컴포넌트를 그대로 인수로 받는 식으로요.

onClick={handleEdit}
>
토론 정보 수정하기
Expand Down
7 changes: 5 additions & 2 deletions src/page/TimerPage/FeedbackTimerPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@ export default function FeedbackTimerPage() {
return (
<DefaultLayout>
<DefaultLayout.ContentContainer>
<div className="relative flex h-full w-full flex-col items-center justify-center space-y-[54px] pb-[66px] xl:space-y-[60px]">
<div className="relative flex h-screen w-full flex-col items-center justify-center space-y-[54px] pb-[66px] xl:space-y-[60px]">
<FeedbackTimer feedbackTimerInstance={feedbackTimerInstance} />
<GoToHomeButton />

<div className="fixed bottom-[8%] xl:bottom-[12%]">
<GoToHomeButton />
</div>
</div>
</DefaultLayout.ContentContainer>
</DefaultLayout>
Expand Down
2 changes: 2 additions & 0 deletions src/page/TimerPage/TimerPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,13 @@ export default function TimerPage() {
<DefaultLayout.Header.Right>
<button
className="flex h-full items-center justify-center p-[4px]"
aria-label="도움말"
title="도움말"
onClick={openUseTooltipModal}
>
<DTHelp className="h-full" />
</button>

<button
className="flex aspect-square h-full items-center justify-center p-[4px]"
title="전체 화면"
Expand Down
2 changes: 1 addition & 1 deletion src/page/TimerPage/components/FeedbackTimer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export default function FeedbackTimer({
timer !== null && defaultTimer > 0
? ((defaultTimer - timer) / defaultTimer) * 100
: 0;
const progressMotionValue = useCircularTimerAnimation(rawProgress);
const progressMotionValue = useCircularTimerAnimation(rawProgress, isRunning);
const breakpoint = useBreakpoint();

const getStrokeWidth = () => {
Expand Down
2 changes: 1 addition & 1 deletion src/page/TimerPage/components/NormalTimer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export default function NormalTimer({
const titleText = item.speechType;
const rawProgress =
timer !== null && item.time ? ((item.time - timer) / item.time) * 100 : 0;
const progressMotionValue = useCircularTimerAnimation(rawProgress);
const progressMotionValue = useCircularTimerAnimation(rawProgress, isRunning);
const breakpoint = useBreakpoint();
const getStrokeWidth = () => {
switch (breakpoint) {
Expand Down
68 changes: 28 additions & 40 deletions src/page/TimerPage/components/TimeBasedTimer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,13 @@ type TimeBasedTimerInstance = {
totalTimer: number | null;
speakingTimer: number | null;
isRunning: boolean;
startTimer: (isOpponentDone: boolean) => void;
startTimer: () => void;
pauseTimer: () => void;
resetCurrentTimer: () => void;
denominator: number;
};

interface TimeBasedTimerProps {
timeBasedTimerInstance: TimeBasedTimerInstance;
isOpponentDone: boolean;
isSelected: boolean;
onActivate?: () => void;
prosCons: TimeBasedStance;
Expand All @@ -30,7 +28,6 @@ interface TimeBasedTimerProps {

export default function TimeBasedTimer({
timeBasedTimerInstance,
isOpponentDone,
isSelected,
prosCons,
teamName,
Expand All @@ -43,7 +40,6 @@ export default function TimeBasedTimer({
startTimer,
pauseTimer,
resetCurrentTimer,
denominator,
} = timeBasedTimerInstance;

const minute = Formatting.formatTwoDigits(
Expand All @@ -59,44 +55,36 @@ export default function TimeBasedTimer({
);

const initRawProgress = (): number => {
if (isOpponentDone) {
if (speakingTimer === null) {
// 1회당 발언 시간 X일 때...
return ((denominator - (totalTimer ?? 0)) / denominator) * 100;
if (speakingTimer === null) {
// 1회당 발언 시간 X일 때...
if (item.timePerTeam && totalTimer && item.timePerTeam > 0) {
// 팀당 발언 시간 타이머가 정상 동작 중이고 남은 시간이 있을 경우, 진행도를 계산
if (totalTimer <= 0) {
return 100;
}
return ((item.timePerTeam - totalTimer) / item.timePerTeam) * 100;
} else {
// 1회당 발언 시간 O일 때...
return ((denominator - (speakingTimer ?? 0)) / denominator) * 100;
// 팀당 발언 시간 타이머가 멈추거나 완료된 경우,
// 완료(100%)에 해당하는 진행도를 반환
return 100;
}
} else {
if (speakingTimer === null) {
// 1회당 발언 시간 X일 때...
if (item.timePerTeam && totalTimer && item.timePerTeam > 0) {
// 팀당 발언 시간 타이머가 정상 동작 중이고 남은 시간이 있을 경우, 진행도를 계산
if (totalTimer <= 0) {
return 100;
}
return ((item.timePerTeam - totalTimer) / denominator) * 100;
} else {
// 팀당 발언 시간 타이머가 멈추거나 완료된 경우,
// 완료(100%)에 해당하는 진행도를 반환
return 100;
}
// 1회당 발언 시간 O일 때...
if (item.timePerSpeaking && speakingTimer && item.timePerSpeaking > 0) {
// 1회당 발언 시간 타이머가 정상 동작 중이고 남은 시간이 있을 경우, 진행도를 계산
return (
((item.timePerSpeaking - speakingTimer) / item.timePerSpeaking) * 100
);
} else {
// 1회당 발언 시간 O일 때...
if (item.timePerSpeaking && speakingTimer && item.timePerSpeaking > 0) {
// 1회당 발언 시간 타이머가 정상 동작 중이고 남은 시간이 있을 경우, 진행도를 계산
return ((item.timePerSpeaking - speakingTimer) / denominator) * 100;
} else {
// 1회당 발언 시간 타이머가 멈추거나 완료된 경우,
// 완료(100%)에 해당하는 진행도를 반환
return 100;
}
// 1회당 발언 시간 타이머가 멈추거나 완료된 경우,
// 완료(100%)에 해당하는 진행도를 반환
return 100;
}
}
};

const rawProgress = initRawProgress();
const progressMotionValue = useCircularTimerAnimation(rawProgress);
const progressMotionValue = useCircularTimerAnimation(rawProgress, isRunning);

const breakpoint = useBreakpoint();
const getStrokeWidth = () => {
Expand Down Expand Up @@ -153,11 +141,11 @@ export default function TimeBasedTimer({
전체 시간
</h1>
<span className="flex flex-row text-[56px] font-semibold tabular-nums text-default-black xl:text-[72px]">
<p className="flex w-[80px] items-center justify-center xl:w-[120px]">
<p className="flex w-[80px] items-center justify-center xl:w-[100px]">
{minute}
</p>
<p className="flex items-center justify-center">:</p>
<p className="flex w-[80px] items-center justify-center xl:w-[120px]">
<p className="flex w-[80px] items-center justify-center xl:w-[100px]">
{second}
</p>
</span>
Expand All @@ -173,12 +161,12 @@ export default function TimeBasedTimer({
>
현재 시간
</h1>
<span className="flex flex-row text-[70px] font-bold tabular-nums text-default-black lg:text-[90px] xl:text-[110px]">
<p className="flex w-[108px] items-center justify-center xl:w-[180px]">
<span className="flex flex-row text-[80px] font-bold tabular-nums text-default-black xl:text-[110px]">
<p className="flex w-[120px] items-center justify-center xl:w-[180px]">
{speakingMinute}
</p>
<p className="flex items-center justify-center">:</p>
<p className="flex w-[108px] items-center justify-center xl:w-[180px]">
<p className="flex w-[120px] items-center justify-center xl:w-[180px]">
{speakingSecond}
</p>
</span>
Expand All @@ -189,7 +177,7 @@ export default function TimeBasedTimer({
{/* 조작부 */}
<TimerController
isRunning={isRunning}
onStart={() => startTimer(isOpponentDone)}
onStart={startTimer}
onPause={pauseTimer}
onReset={resetCurrentTimer}
stance={prosCons}
Expand Down
4 changes: 0 additions & 4 deletions src/page/TimerPage/components/TimerView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,9 @@ export default function TimerView({ state }: { state: TimerPageLogics }) {
isRunning: timer1.isRunning,
startTimer: timer1.startTimer,
pauseTimer: timer1.pauseTimer,
denominator: timer1.denominator,
resetCurrentTimer: () => timer1.resetCurrentTimer(timer2.isDone),
}}
item={data.table[index]}
isOpponentDone={timer2.isDone}
isSelected={prosConsSelected === 'PROS'}
onActivate={() => handleActivateTeam('PROS')}
prosCons="PROS"
Expand All @@ -81,11 +79,9 @@ export default function TimerView({ state }: { state: TimerPageLogics }) {
isRunning: timer2.isRunning,
startTimer: timer2.startTimer,
pauseTimer: timer2.pauseTimer,
denominator: timer2.denominator,
resetCurrentTimer: () => timer2.resetCurrentTimer(timer1.isDone),
}}
item={data.table[index]}
isOpponentDone={timer1.isDone}
isSelected={prosConsSelected === 'CONS'}
onActivate={() => handleActivateTeam('CONS')}
prosCons="CONS"
Expand Down
9 changes: 6 additions & 3 deletions src/page/TimerPage/hooks/useCircularTimerAnimation.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import { animate, clamp, useMotionValue } from 'framer-motion';
import { useEffect } from 'react';

export default function useCircularTimerAnimation(rawProgress: number) {
export default function useCircularTimerAnimation(
rawProgress: number,
isRunning: boolean,
) {
const progress = clamp(0, 100, rawProgress);
const progressMotionValue = useMotionValue(0);

useEffect(() => {
const controls = animate(progressMotionValue, progress, {
duration: 0.7,
duration: isRunning ? 0.7 : 0,
ease: 'easeOut',
});

return () => controls.stop();
}, [progress, progressMotionValue]);
}, [progress, progressMotionValue, isRunning]);

return progressMotionValue;
}
13 changes: 8 additions & 5 deletions src/page/TimerPage/hooks/useFeedbackTimer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,16 +82,19 @@ export function useFeedbackTimer(): FeedbackTimerLogics {
*/
const adjustTime = useCallback(
(amount: number) => {
if (isRunning) return;
if (isRunning) {
return;
}

let newTime: number;

setTimer((prevTimer) => {
const newTime = (prevTimer ?? 0) + amount;
newTime = (prevTimer ?? 0) + amount;
return newTime < 0 ? 0 : newTime;
});

setDefaultTimer((prevDefault) => {
const newDefault = prevDefault + amount;
return newDefault < 0 ? 0 : newDefault;
setDefaultTimer(() => {
return newTime < 0 ? 0 : newTime;
});
},
[isRunning],
Expand Down
Loading