Skip to content

Commit

Permalink
refactor: update loading, root page and bottom sheet (#117)
Browse files Browse the repository at this point in the history
* refactor: set loading UI globally usin loading.tsx

* fix: add missing dependency of Effect

* fix: update css padding

* fix: add proper loading state

* fix: react error - two children with the same key

same error in production is displayed in production as "Uncaught Error: Minified React error #418"

* chore: unify code style

* refactor: make root as page, not use as middleware

* refactor: make board and enter map code clearer

* refactor: simplify code

* refactor: update root page component to not-found

* refactor: separate scroll outside bottom sheet

- handle scroll inside body component
- fix place-list-bottom sheet gap issue

* refactor: use status i/o undefined value

* chore: update invitation image

* fix: add min-h to image to prevent layout shift
  • Loading branch information
hee-suh authored Sep 2, 2024
1 parent 2db214e commit 8f007ec
Show file tree
Hide file tree
Showing 20 changed files with 105 additions and 136 deletions.
3 changes: 3 additions & 0 deletions next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { withSentryConfig } from '@sentry/nextjs'
/** @type {import('next').NextConfig} */

const nextConfig = {
async redirects() {
return [{ source: '/', destination: '/intro', permanent: true }]
},
compiler: {
removeConsole: process.env.NODE_ENV === 'production',
},
Expand Down
Binary file modified public/images/invitation.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 16 additions & 9 deletions src/app/intro/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ import useFetch from '@/hooks/use-fetch'
import { useIsServer } from '@/hooks/use-is-server'
import useSafeRouter from '@/hooks/use-safe-router'
import type { Token } from '@/models/user'
import { enterMap } from '@/services/invitation'
import { boardMap, getMapInviteInfo } from '@/services/invitation'
import { api } from '@/utils/api'
import { fetchData } from '@/utils/api/route'
import { inviteCodeStorage, onboardingStorage } from '@/utils/storage'
import { getMapId } from '@/services/map-id'

export interface IntroActionDispatch {
goNextStep: VoidFunction
Expand Down Expand Up @@ -76,22 +77,22 @@ const Intro = () => {
setLoading(true)

try {
const data = await enterMap(inviteCode)
const status = await boardMap(inviteCode)
const info = await getMapInviteInfo(inviteCode)

if (!data) {
throw new Error('예상치 못한 오류가 발생했습니다.')
router.push(`/map/${info.mapId}`)
inviteCodeStorage.remove()
if (status === 'success') {
notify.success(`${info.mapName} 지도에 오신 걸 환영합니다!`)
}

router.push(`/map/${data.map.id}`)
notify.success(`${data.map.name} 지도에 오신 걸 환영합니다!`)
} catch (error) {
if (error instanceof Error) {
notify.error(error.message)
}

setLoading(false)
}
}, [inviteCode])
}, [inviteCode, router])

const [step, setStep] = useState<IntroStep>(IntroStep.LOADING)

Expand Down Expand Up @@ -142,7 +143,13 @@ const Intro = () => {

useEffect(() => {
if (initialStep === IntroStep.FORBIDDEN) {
router.replace('/')
try {
const enterMap = async () => {
const mapId = await getMapId()
router.push(`/map/${mapId}`)
}
enterMap()
} catch {}
} else if (nickname && !!inviteCode) {
enterMapWithInviteCode()
} else {
Expand Down
8 changes: 2 additions & 6 deletions src/app/invite/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client'

import { Suspense, useEffect, useState } from 'react'
import { useEffect, useState } from 'react'

import { useSearchParams } from 'next/navigation'

Expand Down Expand Up @@ -79,11 +79,7 @@ const Invite = () => {
}

const InvitePage = () => {
return (
<Suspense fallback={<LoadingIndicator />}>
<Invite />
</Suspense>
)
return <Invite />
}

export default InvitePage
9 changes: 9 additions & 0 deletions src/app/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use client'

import LoadingIndicator from '@/components/common/loading-indicator'

const Loading = () => {
return <LoadingIndicator />
}

export default Loading
10 changes: 5 additions & 5 deletions src/app/map/[mapId]/place-list-bottom-sheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ const PlaceListBottomSheet = ({
}

return (
<>
<div className="sticky left-0 top-[-1px] z-10 h-[38px] bg-[#212124] px-5 pt-[1px] shadow-[rgba(33,33,36,1)_0px_1px_4px_4px]">
<div className="flex flex-col h-full">
<div className="z-10 h-[38px] bg-[#212124] px-5 shadow-[rgba(33,33,36,1)_0px_1px_4px_4px]">
<FilterButton
numOfSelectedFilter={numOfSelectedFilter}
icon={{ type: 'filter' }}
Expand All @@ -105,7 +105,7 @@ const PlaceListBottomSheet = ({
</FilterButton>
</div>
{placeList.length > 0 ? (
<ul className="flex flex-col px-5">
<ul className="flex flex-col px-5 h-dvh no-scrollbar overflow-y-scroll overscroll-contain">
{placeList.map((place) => (
<PlaceListItem
key={`bottom-sheet-${place.place.kakaoPlace.id}`}
Expand All @@ -128,14 +128,14 @@ const PlaceListBottomSheet = ({
handleLike(place)
},
}}
className="first:pt-2"
className="first:pt-0 first:-my-2"
/>
))}
</ul>
) : (
<EmptyPlaceList message="해당 음식점이 없어요" />
)}
</>
</div>
)
}

Expand Down
11 changes: 1 addition & 10 deletions src/app/map/create/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import LoadingIndicator from '@/components/common/loading-indicator'
import Header from '@/components/intro/header'
import { Invite, Mapname, NewMap } from '@/components/intro/steps'
import { IntroStep } from '@/constants/intro'
import { useIsServer } from '@/hooks/use-is-server'

const Step = ({ step, goNextStep }: StepProps) => {
switch (step) {
Expand All @@ -27,8 +26,6 @@ const Step = ({ step, goNextStep }: StepProps) => {
}

const MapCreate = () => {
const isServer = useIsServer()

const [step, setStep] = useState<IntroStep>(IntroStep.NEW_MAP)

const goNextStep = () => {
Expand All @@ -39,13 +36,7 @@ const MapCreate = () => {
return (
<div className="flex h-dvh w-full flex-col justify-between bg-neutral-700">
<Header />
{isServer ? (
<div className="flex flex-1 items-center justify-center text-white">
<LoadingIndicator />
</div>
) : (
<Step step={step} goNextStep={goNextStep} />
)}
<Step step={step} goNextStep={goNextStep} />
</div>
)
}
Expand Down
75 changes: 22 additions & 53 deletions src/app/not-found.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,13 @@
'use client'

import { useEffect, useState } from 'react'

import { usePathname } from 'next/navigation'

import Button from '@/components/common/button'
import LoadingIndicator from '@/components/common/loading-indicator'
import Typography from '@/components/common/typography'
import Header from '@/components/intro/header'
import useSafeRouter from '@/hooks/use-safe-router'
import { getMapId } from '@/services/map-id'

const NotFound = () => {
const router = useSafeRouter()
const pathname = usePathname()
const isRoot = pathname === '/'
const [isLoading, setIsLoading] = useState(isRoot)

useEffect(() => {
const handleRoot = async () => {
try {
const mapId = await getMapId()

if (!mapId) throw new Error('잘못된 접근입니다.')

return router.replace(`/map/${mapId}`)
} catch {
return router.replace('/intro')
}
}
if (isRoot) {
handleRoot()
} else {
setIsLoading(false)
}
}, [isRoot, router])

const handleClick = async () => {
try {
Expand All @@ -52,33 +25,29 @@ const NotFound = () => {
return (
<div className="flex h-dvh w-full flex-col justify-between bg-neutral-700">
<Header />
{isLoading ? (
<LoadingIndicator />
) : (
<>
<div className="flex-1">
<div className="mb-10 space-y-4 px-5 pt-12">
<Typography size="body0-2" color="neutral-000">
앗... 길을 잃어버렸다..!
</Typography>
<Typography
size="body1"
color="neutral-200"
className="whitespace-pre-line"
>
{`페이지가 존재하지 않거나,\n일시적인 오류가 발생했어요.`}
</Typography>
</div>
<img src="/images/404.png" width="100%" alt="길을 잃었어요" />
</div>

<div className="w-full p-5">
<Button colorScheme="orange" onClick={handleClick}>
홈으로 이동
</Button>
<>
<div className="flex-1">
<div className="mb-10 space-y-4 px-5 pt-12">
<Typography size="body0-2" color="neutral-000">
앗... 길을 잃어버렸다..!
</Typography>
<Typography
size="body1"
color="neutral-200"
className="whitespace-pre-line"
>
{`페이지가 존재하지 않거나,\n일시적인 오류가 발생했어요.`}
</Typography>
</div>
</>
)}
<img src="/images/404.png" width="100%" alt="길을 잃었어요" />
</div>

<div className="w-full p-5">
<Button colorScheme="orange" onClick={handleClick}>
홈으로 이동
</Button>
</div>
</>
</div>
)
}
Expand Down
7 changes: 7 additions & 0 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import NotFound from '@/app/not-found'

const Root = () => {
return <NotFound />
}

export default Root
15 changes: 15 additions & 0 deletions src/app/search/[query]/result-search-box.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import cn from '@/utils/cn'
import { formatBoundToRect } from '@/utils/location'
import { getCorners } from '@/utils/map'
import { mapBoundSessionStorage } from '@/utils/storage'
import LoadingIndicator from '@/components/common/loading-indicator'

interface ResultSearchBoxProps extends ClassName {
query: string
Expand All @@ -26,6 +27,8 @@ interface ResultSearchBoxProps extends ClassName {
const ResultSearchBox = ({ query, className }: ResultSearchBoxProps) => {
const [isMapView, setIsMapView] = useState(false)
const [mapId, setMapId] = useState('')
// TODO: useFetch에 status 추가 및 useFetch로 데이터 관리
const [status, setStatus] = useState('pending') // 'pending' | 'fetching' | 'success' | 'error'
const [places, setPlaces] = useState<SearchPlace[]>([])
const [selectedPlace, setSelectedPlace] = useState<SearchPlace | null>(null)
const [center, setCenter] = useState<{ lat: number; lng: number } | null>(
Expand All @@ -46,15 +49,20 @@ const ResultSearchBox = ({ query, className }: ResultSearchBoxProps) => {
if (!mapId) return

try {
setStatus('fetching')

const { data } = await api.search.places.get({
q: query,
rect: formatBoundToRect(mapBound),
mapId,
})
setPlaces(data)

setIsShowCurrentPositionSearch(false)
setStatus('success')
} catch {
notify.error('잘못된 접근입니다.')
setStatus('error')
}
}

Expand Down Expand Up @@ -97,6 +105,8 @@ const ResultSearchBox = ({ query, className }: ResultSearchBoxProps) => {
const bounds = mapBoundSessionStorage.getValueOrNull()

try {
setStatus('fetching')

let validMapId = mapId
if (!validMapId) {
validMapId = (await getMapId()) || ''
Expand All @@ -122,8 +132,11 @@ const ResultSearchBox = ({ query, className }: ResultSearchBoxProps) => {
if (data.length === 0) {
await searchOnKorea(validMapId)
}

setStatus('success')
} catch {
notify.error('잘못된 접근입니다.')
setStatus('error')
}
})()
}, [mapId, query])
Expand Down Expand Up @@ -166,6 +179,8 @@ const ResultSearchBox = ({ query, className }: ResultSearchBoxProps) => {
/>
)}
</>
) : status === 'fetching' ? (
<LoadingIndicator />
) : (
<ResultSearchListBox
mapId={mapId}
Expand Down
15 changes: 1 addition & 14 deletions src/app/search/page.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,9 @@
'use client'

import { Suspense } from 'react'

import SearchBox from './search-box'

import LoadingIndicator from '@/components/common/loading-indicator'
import { useIsServer } from '@/hooks/use-is-server'

const Search = () => {
const isServer = useIsServer()

if (isServer) return <LoadingIndicator />

return (
<Suspense fallback={<LoadingIndicator />}>
<SearchBox />
</Suspense>
)
return <SearchBox />
}

export default Search
Loading

0 comments on commit 8f007ec

Please sign in to comment.