-
Notifications
You must be signed in to change notification settings - Fork 0
fix: 위시리스트 뒤로가기 시 스크롤 위치 복원 #401
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
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
b2dbb36
feat: 스크롤 컨테이너 식별용 ID 추가
kanghaeun ba1767c
feat: 위시리스트 스크롤 위치 복원 훅 추가
kanghaeun 3ee1b3b
feat: 위시리스트 뒤로가기 시 스크롤 위치 복원 기능 적용
kanghaeun b5f8803
fix: 위시리스트 복귀 시 router.back 사용으로 스크롤 복원
kanghaeun 1e57666
fix: 스크롤 복원 판별을 popstate에서 history entry 기준으로 변경
kanghaeun 891d337
Merge branch 'dev' into fix/392-wishlist-scroll-restoration
kanghaeun 45cb351
refactor: 위시리스트 스크롤 복원을 클릭 시점·상품 기준으로 변경
kanghaeun 62481bc
fix: 새 탭으로 여는 클릭에서는 스크롤 앵커를 저장하지 않음
kanghaeun 3c83ed0
refactor: 스크롤 저장소 키 네이밍 규칙 통일
kanghaeun cefcf66
refactor: 삭제 모드 z-index를 Z_INDEX 상수로 변경
kanghaeun 176d9c5
Merge branch 'dev' into fix/392-wishlist-scroll-restoration
kanghaeun File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
48 changes: 48 additions & 0 deletions
48
apps/web/src/app/archive/wish/_hooks/useScrollRestoration.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| import { useLayoutEffect } from 'react'; | ||
|
|
||
| import { | ||
| WISH_CARD_ID_ATTR, | ||
| clearWishScroll, | ||
| getCardOffset, | ||
| getScrollContainer, | ||
| readWishScroll, | ||
| } from '../_utils/wishScroll'; | ||
|
|
||
| const RESTORE_DEADLINE_MS = 1000; | ||
| const TOLERANCE_PX = 1; | ||
|
|
||
| export const useScrollRestoration = () => { | ||
| useLayoutEffect(() => { | ||
| const container = getScrollContainer(); | ||
| if (!container) return; | ||
|
|
||
| const anchor = readWishScroll(); | ||
| if (!anchor) return; | ||
|
|
||
| let frame = 0; | ||
| const deadline = performance.now() + RESTORE_DEADLINE_MS; | ||
|
|
||
| /** 카드 렌더링이 끝나기 전에는 한 번에 복원되지 않을 수 있어 deadline까지 스크롤 복원 재시도 */ | ||
| const restore = () => { | ||
| const card = container.querySelector<HTMLElement>( | ||
| `[${WISH_CARD_ID_ATTR}="${anchor.wishId}"]` | ||
| ); | ||
|
|
||
| if (card) { | ||
| const delta = getCardOffset(container, card) - anchor.offset; | ||
| if (Math.abs(delta) < TOLERANCE_PX) { | ||
| clearWishScroll(); | ||
| return; | ||
| } | ||
|
|
||
| container.scrollTop += delta; | ||
| } | ||
|
|
||
| if (performance.now() < deadline) frame = requestAnimationFrame(restore); | ||
| }; | ||
|
|
||
| restore(); | ||
|
|
||
| return () => cancelAnimationFrame(frame); | ||
| }, []); | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| import { SCROLL_CONTAINER_ID } from '@/consts/layout'; | ||
|
|
||
| const STORAGE_KEY_PREFIX = 'piki:wishScroll:'; | ||
|
|
||
| const HISTORY_STATE_KEY = '__pikiScrollKey'; | ||
|
|
||
| /** 스크롤 복원 기준 카드를 식별하기 위한 속성 */ | ||
| export const WISH_CARD_ID_ATTR = 'data-wish-id'; | ||
|
|
||
| export type WishScrollAnchorT = { | ||
| wishId: number; | ||
| offset: number; | ||
| }; | ||
|
|
||
| const getStorageKey = () => { | ||
| const state: Record<string, unknown> = window.history.state ?? {}; | ||
| const key = state[HISTORY_STATE_KEY]; | ||
| if (typeof key === 'string') return `${STORAGE_KEY_PREFIX}${key}`; | ||
|
|
||
| const newKey = Math.random().toString(36).slice(2, 8); | ||
| window.history.replaceState({ ...state, [HISTORY_STATE_KEY]: newKey }, ''); | ||
|
|
||
| return `${STORAGE_KEY_PREFIX}${newKey}`; | ||
| }; | ||
|
|
||
| export const getScrollContainer = () => document.getElementById(SCROLL_CONTAINER_ID); | ||
|
|
||
| export const getCardOffset = (container: HTMLElement, card: HTMLElement) => | ||
| card.getBoundingClientRect().top - container.getBoundingClientRect().top; | ||
|
|
||
| export const saveWishScroll = (card: HTMLElement, wishId: number) => { | ||
| const container = getScrollContainer(); | ||
| if (!container) return; | ||
|
|
||
| const anchor: WishScrollAnchorT = { wishId, offset: getCardOffset(container, card) }; | ||
|
|
||
| try { | ||
| sessionStorage.setItem(getStorageKey(), JSON.stringify(anchor)); | ||
| } catch { | ||
| /** 스크롤 복원 실패는 치명적이지 않으므로 무시 */ | ||
| } | ||
| }; | ||
|
|
||
| export const readWishScroll = (): WishScrollAnchorT | null => { | ||
| try { | ||
| const raw = sessionStorage.getItem(getStorageKey()); | ||
| if (!raw) return null; | ||
|
|
||
| const { wishId, offset }: Record<string, unknown> = JSON.parse(raw); | ||
| if (typeof wishId !== 'number' || typeof offset !== 'number') return null; | ||
|
|
||
| return { wishId, offset }; | ||
| } catch { | ||
| return null; | ||
| } | ||
| }; | ||
|
|
||
| export const clearWishScroll = () => { | ||
| try { | ||
| sessionStorage.removeItem(getStorageKey()); | ||
| } catch { | ||
| /** 값이 남아도 무해하므로 무시 */ | ||
| } | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| /** window가 아닌 루트 스크롤 컨테이너를 조회하기 위한 ID */ | ||
| export const SCROLL_CONTAINER_ID = 'scroll-container'; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.