-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feat] 예외 케이스 : 타이머 상세 페이지 [미션 중일 때 앱을 껐다 킬 경우] 대응 #297
Changes from all commits
eddc9bf
942fcb3
f77cb3a
fc895c4
4380351
ead9e13
51ce3db
3ae2b8a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
import { useEffect } from 'react'; | ||
import { useCallback, useEffect } from 'react'; | ||
import { EVENT_LOG_CATEGORY, EVENT_LOG_NAME } from '@/constants/eventLog'; | ||
import { STORAGE_KEY } from '@/constants/storage'; | ||
import useInterval from '@/hooks/useInterval'; | ||
|
@@ -12,29 +12,29 @@ export const useGetCategory = () => { | |
}; | ||
|
||
export function useUnloadAction(time: number) { | ||
const onSaveTime = () => { | ||
const onSaveTime = useCallback(() => { | ||
eventLogger.logEvent(EVENT_LOG_NAME.STOPWATCH.MID_SAVE, EVENT_LOG_CATEGORY.STOPWATCH, { time }); | ||
localStorage.setItem(STORAGE_KEY.STOPWATCH.TIME, String(time)); | ||
}; | ||
}, [time]); | ||
|
||
useVisibilityState(onSaveTime); | ||
} | ||
|
||
function useVisibilityState(onAction: VoidFunction) { | ||
useEffect(() => { | ||
const onVisibilityChange = () => { | ||
if (document.visibilityState === 'hidden') { | ||
onAction(); | ||
} | ||
}; | ||
const onVisibilityChange = useCallback(() => { | ||
if (document.visibilityState === 'hidden') { | ||
onAction(); | ||
} | ||
}, [onAction]); | ||
|
||
useEffect(() => { | ||
document.addEventListener('visibilitychange', onVisibilityChange); | ||
|
||
// 컴포넌트가 언마운트될 때 이벤트 리스너를 제거합니다. | ||
return () => { | ||
document.removeEventListener('visibilitychange', onVisibilityChange); | ||
}; | ||
}, []); // 빈 의존성 배열을 전달하여 이 훅이 컴포넌트가 마운트되거나 언마운트될 때만 실행되도록 합니다. | ||
}, [onVisibilityChange]); // 빈 의존성 배열을 전달하여 이 훅이 컴포넌트가 마운트되거나 언마운트될 때만 실행되도록 합니다. | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 의존성 배열에 onVisibilityChange 값이 들어가게된 이유가 궁금해요 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 들어가지 않는다면, 이러한 이유로 의존성 배열에 추가하였어요 |
||
|
||
export function useRecordMidTime(time: number) { | ||
|
@@ -43,7 +43,6 @@ export function useRecordMidTime(time: number) { | |
localStorage.setItem(STORAGE_KEY.STOPWATCH.TIME_2, String(time)); | ||
}; | ||
|
||
// 카운터 속도 증가 | ||
useInterval(() => { | ||
onSaveTime(); | ||
}, 10000); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,7 +32,7 @@ export default function StopwatchPage() { | |
const category = useGetCategory(); | ||
|
||
const { step, prevStep, stepLabel, onNextStep } = useStopwatchStatus(); | ||
const { seconds, minutes, stepper, isFinished } = useStopwatch(step); | ||
const { seconds, minutes, stepper, isFinished, isPending: isStopwatchPending } = useStopwatch(step); | ||
|
||
const time = Number(minutes) * 60 + Number(seconds); | ||
const logData = { | ||
|
@@ -46,15 +46,24 @@ export default function StopwatchPage() { | |
|
||
useCustomBack(openMidOutModal); | ||
|
||
useUnloadAction(time); | ||
useRecordMidTime(time); | ||
useUnloadAction(time); | ||
|
||
const resetStopwatchStorage = () => { | ||
localStorage.removeItem(STORAGE_KEY.STOPWATCH.MISSION_ID); | ||
localStorage.removeItem(STORAGE_KEY.STOPWATCH.TIME); | ||
localStorage.removeItem(STORAGE_KEY.STOPWATCH.TIME_2); | ||
localStorage.removeItem(STORAGE_KEY.STOPWATCH.START_TIME); | ||
}; | ||
|
||
// isError 처리 어떻게 할것인지? | ||
const { mutate, isPending: isSubmitLoading } = useRecordTime({ | ||
onSuccess: (response) => { | ||
const missionRecordId = String(response.missionId); | ||
router.replace(ROUTER.RECORD.CREATE(missionRecordId)); | ||
eventLogger.logEvent('api/record-time', 'stopwatch', { missionRecordId }); | ||
|
||
resetStopwatchStorage(); | ||
}, | ||
onError: (error) => { | ||
// TODO | ||
|
@@ -101,6 +110,7 @@ export default function StopwatchPage() { | |
// 뒤로가기 버튼 눌렀을 때 | ||
const onExit = () => { | ||
router.push(ROUTER.MISSION.DETAIL(missionId)); | ||
resetStopwatchStorage(); | ||
}; | ||
|
||
const onFinish = () => { | ||
|
@@ -134,9 +144,16 @@ export default function StopwatchPage() { | |
}; | ||
|
||
const onStart = () => { | ||
eventLogger.logEvent(EVENT_LOG_NAME.STOPWATCH.CLICK_START, EVENT_LOG_CATEGORY.STOPWATCH, { category }); | ||
onNextStep('progress'); | ||
// 중도 재시작 | ||
if (time > 0) { | ||
eventLogger.logEvent(EVENT_LOG_NAME.STOPWATCH.CLICK_RESTART, EVENT_LOG_CATEGORY.STOPWATCH); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 중도 재시작시에는 로그만 쏜다 가 쉽게 이해가 안가서 궁금해요! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 위에 시작하는 로직인
|
||
return; | ||
} | ||
// 초기시작 | ||
eventLogger.logEvent(EVENT_LOG_NAME.STOPWATCH.CLICK_START, EVENT_LOG_CATEGORY.STOPWATCH); | ||
const startTime = new Date().toISOString(); | ||
localStorage.setItem(STORAGE_KEY.STOPWATCH.MISSION_ID, missionId); | ||
localStorage.setItem(STORAGE_KEY.STOPWATCH.START_TIME, startTime); | ||
}; | ||
|
||
|
@@ -166,7 +183,7 @@ export default function StopwatchPage() { | |
</section> | ||
<section className={buttonContainerCss}> | ||
{step === 'ready' && ( | ||
<Button variant="cta" size="large" type="button" onClick={onStart}> | ||
<Button variant="cta" size="large" type="button" onClick={onStart} disabled={isStopwatchPending}> | ||
시작 | ||
</Button> | ||
)} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
import { useEffect, useState } from 'react'; | ||
import { STORAGE_KEY } from '@/constants/storage'; | ||
import { formatMMSS } from '@/utils/time'; | ||
|
||
import { type StepType } from './useStopwatchStatus'; | ||
|
@@ -7,13 +8,12 @@ const INIT_SECONDS = 0; | |
const MAX_SECONDS = 60 * 60; // max 1 hour | ||
|
||
const DEFAULT_MS = 1000; | ||
const TEST_MS = 1; | ||
const TEST_MS = 50; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pooling을 위한 시간으로 이해했는데 맞을까요?! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵 맞아요 라이브로 갈때는 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. #308 |
||
|
||
// 좀 더 의미론적.... useStopwatch | ||
export default function useStopwatch(status: StepType) { | ||
const [second, setSecond] = useState(INIT_SECONDS); // 남은 시간 (단위: 초) | ||
const [isPending, setIsPending] = useState(true); | ||
const [isFinished, setIsFinished] = useState(false); | ||
|
||
const { formattedMinutes, formattedSeconds } = formatMMSS(second); | ||
|
||
const stepper = second < 60 ? 0 : Math.floor(second / 60 / 10); | ||
|
@@ -29,12 +29,22 @@ export default function useStopwatch(status: StepType) { | |
|
||
if (status === 'progress') { | ||
timer = setInterval(() => { | ||
setSecond((prev) => prev + 1); | ||
setSecond((prev) => (prev >= MAX_SECONDS ? prev : prev + 1)); | ||
}, TEST_MS); | ||
} | ||
|
||
return () => clearInterval(timer); | ||
}, [second, status]); | ||
|
||
return { minutes: formattedMinutes, seconds: formattedSeconds, stepper, isFinished }; | ||
useEffect(() => { | ||
// init time setting | ||
const initSecondString = localStorage.getItem(STORAGE_KEY.STOPWATCH.TIME); | ||
const initSeconds = Number(initSecondString); | ||
if (initSeconds && initSeconds < MAX_SECONDS) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. initSeconds가 NaN으로 나오거나 0으로 나오면 실행이 안될걸로 이해했는데 다르게 이해한 것일지, 의도된 것일지 궁금해요! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵 맞습니다 실행이 되지 않으면 second가 초기값 0으로 유지되기 때문에 문제 없습니당 |
||
setSecond(initSeconds); | ||
} | ||
setIsPending(false); | ||
}, []); | ||
|
||
return { minutes: formattedMinutes, seconds: formattedSeconds, stepper, isFinished, isPending }; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 친구 테스트용이라면 제거되어도 될것 같은데 맞게 이해한 것일지 궁금하빈다~
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
음 아직은 있는게 좋을 것 같아요!
일단 잘 되는지 확인 테스트는, 나중에나 가능할 것 같아서요!