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: 12 additions & 0 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,17 @@ const config: StorybookConfig = {
core: {
builder: '@storybook/builder-vite',
},
async viteFinal(config, { configType }) {
console.log(`# configType = ${configType}`);

// Storybook의 실행 모드를 테스트 모드로 고정하여
// msw가 잘 동작할 수 있게 준비
config.define = {
...config.define,
'import.meta.env.MODE': JSON.stringify('test'),
};

return config;
},
};
export default config;
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "debate-timer-fe-win",
"private": true,
"version": "1.0.1",
"version": "1.0.2",
"homepage": "./",
"main": "./dist/main/main.js",
"scripts": {
Expand Down
3 changes: 2 additions & 1 deletion src/page/TimerPage/TimerPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import HeaderTableInfo from '../../components/HeaderTableInfo/HeaderTableInfo';
import HeaderTitle from '../../components/HeaderTitle/HeaderTitle';
import IconButton from '../../components/IconButton/IconButton';
import { IoHelpCircle } from 'react-icons/io5';
import { bgColorMap, useTimerPageState } from './hooks/useTimerPageState';
import { useTimerPageState } from './hooks/useTimerPageState';
import { useTimerHotkey } from './hooks/useTimerHotkey';
import RoundControlRow from './components/RoundControlRow';
import TimerView from './components/TimerView';
Expand All @@ -13,6 +13,7 @@ import { FirstUseToolTipModal } from './components/FirstUseToolTipModal';
import { isUUID } from '../../util/type_guard';
import LoadingIndicator from '../../components/async/LoadingIndicator';
import ErrorIndicator from '../../components/async/ErrorIndicator';
import { bgColorMap } from '../../type/type';

export default function TimerPage() {
const { id } = useParams();
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 @@ -90,7 +90,7 @@ export default function NormalTimer({
{/* Speaker's number, if necessary */}
<div className="my-[12px] h-[25px] lg:my-[17px] lg:h-[30px] xl:my-[20px] xl:h-[40px]">
<div className="flex w-full flex-row items-center justify-center space-x-2 text-neutral-900">
{item.stance !== 'NEUTRAL' && isAdditionalTimerOn && (
{item.stance !== 'NEUTRAL' && !isAdditionalTimerOn && (
<>
<MdRecordVoiceOver className="size-[30px] lg:size-[35px] xl:size-[40px]" />
<h3 className="text-[18px] font-semibold lg:text-[24px] xl:text-[28px]">
Expand Down
4 changes: 2 additions & 2 deletions src/page/TimerPage/components/TimerView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export default function TimerView({ state }: { state: TimerPageLogics }) {
isRunning: timer1.isRunning,
startTimer: timer1.startTimer,
pauseTimer: timer1.pauseTimer,
resetCurrentTimer: timer1.resetCurrentTimer,
resetCurrentTimer: () => timer1.resetCurrentTimer(timer2.isDone),
}}
isSelected={prosConsSelected === 'PROS'}
onActivate={() => handleActivateTeam('PROS')}
Expand All @@ -76,7 +76,7 @@ export default function TimerView({ state }: { state: TimerPageLogics }) {
isRunning: timer2.isRunning,
startTimer: timer2.startTimer,
pauseTimer: timer2.pauseTimer,
resetCurrentTimer: timer2.resetCurrentTimer,
resetCurrentTimer: () => timer2.resetCurrentTimer(timer1.isDone),
}}
isSelected={prosConsSelected === 'CONS'}
onActivate={() => handleActivateTeam('CONS')}
Expand Down
3 changes: 2 additions & 1 deletion src/page/TimerPage/hooks/useBellSound.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ export function useBellSound({
prevTime !== null &&
prevTime > warningTime &&
currentTime === warningTime &&
defaultTime !== warningTime
((defaultTime !== null && prevTime > defaultTime) ||
defaultTime !== warningTime)
);
}

Expand Down
42 changes: 36 additions & 6 deletions src/page/TimerPage/hooks/useNormalTimer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ import {
* "일반 타이머" 기능을 제공하는 커스텀 훅
* - 한 명(한 팀)만 시간을 쓰는 단일 타이머 상황에서 사용
*/
export function useNormalTimer() {
export function useNormalTimer(): NormalTimerLogics {
// 타이머에 표시할 '남은 시간'(초)
const [timer, setTimer] = useState<number | null>(null);
const intervalRef = useRef<NodeJS.Timeout | null>(null);

// 타이머가 초기화될 때 사용하는 '기본 시간값' (reset 시 사용)
const [defaultTimer, setDefaultTimer] = useState(0);

Expand All @@ -25,17 +26,44 @@ export function useNormalTimer() {
const [isAdditionalTimerOn, setIsAdditionalTimerOn] = useState(false);
const [savedTimer, setSavedTimer] = useState(0);

// 실제 시간 계산용 레퍼런스
const targetTimeRef = useRef<number | null>(null);

/**
* 타이머를 1초마다 1씩 감소시키며 시작
* 이미 동작중이면 재시작하지 않음
*/
const startTimer = useCallback(() => {
if (intervalRef.current !== null) return;
intervalRef.current = setInterval(() => {
setTimer((prev) => (prev === null ? null : prev - 1));
}, 1000);
if (intervalRef.current !== null || timer === null) return;

// 목표 시각을 실제 시각 기반으로 계산
// 예를 들어, 현재 시각이 오후 13시 00분 30초인데, 1회당 발언 시간이 30초라면,
// 1회당 발언 시간이 모두 끝나는 시간은 13시 01분 00초이므로,
// 해당 시간을 목표 시간으로 두는 식임
const startTime = Date.now();
targetTimeRef.current = startTime + timer * 1000;

// isRunning 상태를 true로 바꿔주고 인터벌 처리
setIsRunning(true);
}, []);

// 목표 시각에 기반하여 타이머 계산
intervalRef.current = setInterval(() => {
// 목표 시각 레퍼런스의 유효성 확인
if (targetTimeRef.current === null) {
return;
}

// 현재 시각 확인
const now = Date.now();

// 목표 시각까지 얼마나 더 필요한지, 남은 시간을 초 단위로 계산
const remainingTotal = targetTimeRef.current - now;
const remainingSeconds = Math.ceil(remainingTotal / 1000);

// 계산한 남은 시간을 타이머에 반영
setTimer(remainingSeconds);
}, 200);
}, [timer]);

/**
* 타이머 일시정지 (setInterval 해제)
Expand Down Expand Up @@ -96,6 +124,7 @@ export function useNormalTimer() {
const handleCloseAdditionalTimer = useCallback(() => {
setIsAdditionalTimerOn(false);
}, []);

//작전시간 종료 시, 자동으로 타이머 변경
useEffect(() => {
if (isAdditionalTimerOn && timer === 0 && isRunning) {
Expand All @@ -112,6 +141,7 @@ export function useNormalTimer() {
setTimer,
isRunning,
]);

useEffect(() => () => pauseTimer(), [pauseTimer]);

return {
Expand Down
Loading