Skip to content

Commit

Permalink
feat: 새로고침 버튼 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
FourwingsY committed Nov 22, 2024
1 parent cf39d13 commit 5647a54
Show file tree
Hide file tree
Showing 13 changed files with 458 additions and 92 deletions.
2 changes: 1 addition & 1 deletion app/(private)/quest/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default function QuestDetail() {
useEffect(() => {
const interval = setInterval(() => {
queryClient.invalidateQueries({ queryKey: ["@quests", id] })
}, 5 * 1000)
}, 10 * 1000)
return () => clearInterval(interval)
}, [quest?.id])

Expand Down
11 changes: 1 addition & 10 deletions app/(public)/public/quest/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,11 @@ export default function QuestDetail() {
const queryClient = useQueryClient()
const [map, setMap] = useState<kakao.maps.Map>()
const intialized = useRef(false)

// 10초마다 퀘스트 진행 상황을 갱신합니다.
useEffect(() => {
const interval = setInterval(() => {
queryClient.invalidateQueries({ queryKey: ["@quests", id] })
}, 5 * 1000)
}, 10 * 1000)
return () => clearInterval(interval)
}, [quest?.id])

Expand All @@ -50,14 +49,6 @@ export default function QuestDetail() {
openGuide()
}, [])

// 10초마다 퀘스트 진행 상황을 갱신합니다.
useEffect(() => {
const interval = setInterval(() => {
queryClient.invalidateQueries({ queryKey: ["@quests", id] })
}, 10 * 1000)
return () => clearInterval(interval)
}, [quest?.id])

function getCenterOf(buildings: QuestBuilding[]) {
const coords = buildings.map((b) => b.location)
const center = {
Expand Down
14 changes: 14 additions & 0 deletions app/icons/Reload.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
interface IconProps {
size?: number
color?: string
}
export default function Reload({ size, color = "black" }: IconProps) {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width={size} height={size} fill="none">
<path
fill={color}
d="M31.5 4.47a27.3 27.3 0 0 0-14.7 4.28L11.05 3v16.11h16.11l-6.4-6.4A21.83 21.83 0 0 1 31.5 9.9a22.1 22.1 0 0 1 22.07 22.08c0 2.9-.57 5.66-1.6 8.2l4.74 2.76A27.53 27.53 0 0 0 31.5 4.46ZM42.23 51.22a21.82 21.82 0 0 1-10.73 2.82A22.1 22.1 0 0 1 9.43 31.97c0-2.74.52-5.35 1.44-7.77l-4.73-2.85A27.53 27.53 0 0 0 31.5 59.47c5.4 0 10.43-1.59 14.7-4.29l5.75 5.76V44.83H35.84l6.39 6.39Z"
/>
</svg>
)
}
46 changes: 35 additions & 11 deletions app/modals/BuildingDetailSheet/BuildingDetailSheet.desktop.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { BasicModalProps } from "@reactleaf/modal"
import { useQueryClient } from "@tanstack/react-query"
import { useMemo } from "react"

import { QuestBuilding } from "@/lib/models/quest"
import { useQuestBuilding } from "@/lib/apis/api"
import { QuestBuilding, QuestPlace } from "@/lib/models/quest"

import Reload from "@/icons/Reload"

import RightSheet from "../_template/RightSheet"
import * as S from "./BuildingDetailSheet.style"
import PlaceCard from "./PlaceCard"

interface Props extends BasicModalProps {
Expand All @@ -11,24 +17,42 @@ interface Props extends BasicModalProps {
}

export const defaultOverlayOptions = { closeDelay: 200, dim: false }
export default function BuildingDetailSheet({ building, questId, visible, close }: Props) {
const conquered = building.places.filter((place) => place.isConquered || place.isClosed || place.isNotAccessible)
const notConquered = building.places.filter(
(place) => !place.isConquered && !place.isClosed && !place.isNotAccessible,
)
export default function BuildingDetailSheet({ building: initialData, questId, visible, close }: Props) {
const { data: building } = useQuestBuilding({ questId, buildingId: initialData.buildingId })
const queryClient = useQueryClient()

const isConquered = (place: QuestPlace) => place.isConquered || place.isNotAccessible || place.isClosed

function reloadQuest() {
queryClient.invalidateQueries({ queryKey: ["@quests", questId] })
}

// 활동 중 장소 순서가 바뀌는 것을 막습니다.
const sortedPlaces = useMemo(() => {
if (!building) return []
const conquered = initialData.places.filter(isConquered)
const notConquered = initialData.places.filter((p) => !isConquered(p))
return [...notConquered, ...conquered].map((p) => building.places.find((b) => b.placeId === p.placeId) || p)
}, [initialData, building])

if (!building) return null

const conquered = building.places.filter(isConquered)
const title = (
<>
{building.name}
<br />
<S.CustomTitle>
<h5>{building.name}</h5>
<small>
정복 완료 {conquered.length} / {building.places.length}
</small>
</>
<S.ReloadButton onClick={reloadQuest}>
<Reload size={24} />
</S.ReloadButton>
</S.CustomTitle>
)

return (
<RightSheet visible={visible} close={close} title={title} style={{ width: "360px" }}>
{[...notConquered, ...conquered].map((place) => (
{sortedPlaces.map((place) => (
<PlaceCard place={place} questId={questId} key={place.placeId} />
))}
</RightSheet>
Expand Down
43 changes: 33 additions & 10 deletions app/modals/BuildingDetailSheet/BuildingDetailSheet.mobile.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { BasicModalProps } from "@reactleaf/modal"
import { useQueryClient } from "@tanstack/react-query"
import { useAtom } from "jotai"
import { useEffect } from "react"
import { useEffect, useMemo } from "react"

import { useQuestBuilding } from "@/lib/apis/api"
import { AppState } from "@/lib/globalAtoms"
import { QuestBuilding } from "@/lib/models/quest"
import { QuestBuilding, QuestPlace } from "@/lib/models/quest"

import Reload from "@/icons/Reload"
import BottomSheet from "@/modals/_template/BottomSheet"

import * as S from "./BuildingDetailSheet.style"
import PlaceCard from "./PlaceCard"

interface Props extends BasicModalProps {
Expand All @@ -15,8 +19,10 @@ interface Props extends BasicModalProps {
}

export const defaultOverlayOptions = { closeDelay: 200, dim: false }
export default function BuildingDetailSheet({ building, questId, visible, close }: Props) {
export default function BuildingDetailSheet({ building: initialData, questId, visible, close }: Props) {
const { data: building } = useQuestBuilding({ questId, buildingId: initialData.buildingId })
const [appState, setAppState] = useAtom(AppState)
const queryClient = useQueryClient()

useEffect(() => {
setAppState((prev) => ({ ...prev, isHeaderHidden: true }))
Expand All @@ -25,21 +31,38 @@ export default function BuildingDetailSheet({ building, questId, visible, close
}
}, [])

const conquered = building.places.filter((place) => place.isConquered)
const notConquered = building.places.filter((place) => !place.isConquered)
function reloadQuest() {
queryClient.invalidateQueries({ queryKey: ["@quests", questId] })
}

const isConquered = (place: QuestPlace) => place.isConquered || place.isNotAccessible || place.isClosed

// 활동 중 장소 순서가 바뀌는 것을 막습니다.
const sortedPlaces = useMemo(() => {
if (!building) return []
const conquered = initialData.places.filter(isConquered)
const notConquered = initialData.places.filter((p) => !isConquered(p))
return [...notConquered, ...conquered].map((p) => building.places.find((b) => b.placeId === p.placeId) || p)
}, [initialData, building])

if (!building) return null

const conquered = building.places.filter(isConquered)
const title = (
<>
{building.name}
<br />
<S.CustomTitle>
<h5>{building.name}</h5>
<small>
정복 완료 {conquered.length} / {building.places.length}
</small>
</>
<S.ReloadButton onClick={reloadQuest}>
<Reload size={24} />
</S.ReloadButton>
</S.CustomTitle>
)

return (
<BottomSheet visible={visible} close={close} title={title} style={{ height: "calc(100vh - 300px)" }}>
{[...notConquered, ...conquered].map((place) => (
{sortedPlaces.map((place) => (
<PlaceCard place={place} questId={questId} key={place.placeId} />
))}
</BottomSheet>
Expand Down
36 changes: 36 additions & 0 deletions app/modals/BuildingDetailSheet/BuildingDetailSheet.style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { styled } from "@/styles/jsx"

export const CustomTitle = styled("div", {
base: {
position: "relative",
display: "flex",
flexFlow: "column",
alignItems: "center",
justifyContent: "center",
width: "full",
gap: 4,
fontSize: 18,
fontWeight: 700,
textAlign: "center",
"& small": {
fontSize: 14,
fontWeight: 500,
},
},
})

export const ReloadButton = styled("button", {
base: {
position: "absolute",
top: "50%",
right: 20,
transform: "translateY(-50%)",
padding: 4,
backgroundColor: "transparent",
border: "none",
cursor: "pointer",
"&:hover": {
backgroundColor: "rgba(0, 0, 0, 0.1)",
},
},
})
4 changes: 2 additions & 2 deletions app/modals/BuildingDetailSheet/PlaceCard.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ export const Header = styled("div", {

export const PlaceName = styled("h3", {
base: {
fontSize: 18,
fontWeight: 700,
fontSize: 16,
fontWeight: 600,
},
})

Expand Down
10 changes: 7 additions & 3 deletions app/modals/BuildingDetailSheet/PlaceCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import * as S from "./PlaceCard.style"
interface Props {
place: QuestPlace
questId: string
onUpdate?: (place: QuestPlace) => void
}
export default function PlaceCard({ place, questId }: Props) {
export default function PlaceCard({ place, questId, onUpdate }: Props) {
const [isClosed, setClosed] = useState(place.isClosed)
const [isNotAccessible, setNotAccessible] = useState(place.isNotAccessible)
const noInfo = !place.isConquered && !isClosed && !isNotAccessible
const visited = place.isConquered || isClosed || isNotAccessible
const isReversible = !place.isConquered && (isClosed || isNotAccessible)

Expand Down Expand Up @@ -53,6 +53,7 @@ export default function PlaceCard({ place, questId }: Props) {
placeId: place.placeId,
isClosed,
})
onUpdate?.({ ...place, isClosed })
setClosed(isClosed)
}

Expand All @@ -63,6 +64,7 @@ export default function PlaceCard({ place, questId }: Props) {
placeId: place.placeId,
isNotAccessible,
})
onUpdate?.({ ...place, isNotAccessible })
setNotAccessible(isNotAccessible)
}

Expand All @@ -81,7 +83,9 @@ export default function PlaceCard({ place, questId }: Props) {
<S.PlaceName>
{place.name}
{place.isConquered && <S.PlaceStatusBadge status="good">정복</S.PlaceStatusBadge>}
{!isClosed && place.isClosedExpected && <S.PlaceStatusBadge status="warn">폐업추정</S.PlaceStatusBadge>}
{!place.isConquered && !isClosed && place.isClosedExpected && (
<S.PlaceStatusBadge status="warn">폐업추정</S.PlaceStatusBadge>
)}
{isClosed && <S.PlaceStatusBadge status="bad">폐업확인</S.PlaceStatusBadge>}
{isNotAccessible && <S.PlaceStatusBadge status="bad">접근불가</S.PlaceStatusBadge>}
</S.PlaceName>
Expand Down
1 change: 1 addition & 0 deletions app/modals/_template/BottomSheet/BottomSheet.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export const SheetTitle = styled("h5", {
export const CloseButton = styled("button", {
base: {
position: "absolute",
zIndex: 1,
top: "50%",
transform: "translateY(-50%)",
left: 16,
Expand Down
2 changes: 1 addition & 1 deletion app/modals/_template/BottomSheet/BottomSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default function BottomSheet({ title, actionButton, style, children, visi
<S.CloseButton onClick={close}>
<Close size={28} color="black" />
</S.CloseButton>
<S.SheetTitle>{title}</S.SheetTitle>
{typeof title == "string" ? <S.SheetTitle>{title}</S.SheetTitle> : title}
<S.ActionButtonWrapper>{actionButton}</S.ActionButtonWrapper>
</S.BottomSheetHeader>
)}
Expand Down
26 changes: 16 additions & 10 deletions lib/apis/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,17 @@ export function useQuest({ id }: { id: string }) {
queryKey: ["@quests", id],
queryFn: ({ queryKey }) =>
http(`/admin/clubQuests/${queryKey[1]}`).then((res) => res.json() as Promise<QuestDetail>),
staleTime: 10 * 1000,
})
}

// 특정 빌딩 정보만 가져오기
export function useQuestBuilding({ questId, buildingId }: { questId: string; buildingId: string }) {
const { data, ...others } = useQuest({ id: questId })
const building = data?.buildings.find((b) => b.buildingId === buildingId)
return { data: building, ...others }
}

type UpdateQuestStatusParams = {
questId: string
placeId: string
Expand Down Expand Up @@ -183,28 +191,26 @@ export function crawlChunk({ boundary }: { boundary: LatLng[] }) {
})
}


export type ImageUploadPurposeType = 'BANNER';
export type ImageUploadPurposeType = "BANNER"
export function getImageUploadUrls({
purposeType,
count,
filenameExtension,
} : {
purposeType: ImageUploadPurposeType,
count: number,
filenameExtension: string,
}: {
purposeType: ImageUploadPurposeType
count: number
filenameExtension: string
}): Promise<GetImageUploadUrlsResult> {
return http("/admin/image-upload-urls", {
method: "POST",
body: JSON.stringify({
purposeType,
count,
filenameExtension,
})
}),
}).then((res) => {
return res.json() as Promise<GetImageUploadUrlsResult>
})
.then((res) => {
return res.json() as Promise<GetImageUploadUrlsResult>
})
}
export interface GetImageUploadUrlsResult {
urls: ImageUploadUrl[]
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
"react-responsive": "^9.0.2",
"react-select": "^5.8.0",
"react-toastify": "^9.1.3",
"return-fetch": "^0.4.5"
"return-fetch": "^0.4.5",
"sharp": "^0.33.5"
},
"devDependencies": {
"@pandacss/dev": "^0.23.0",
Expand Down
Loading

0 comments on commit 5647a54

Please sign in to comment.