From 65ee2b5c9a66075d8b128009e109d19032e117cc Mon Sep 17 00:00:00 2001 From: kyoohyun Date: Tue, 21 Feb 2023 15:08:53 +0900 Subject: [PATCH] =?UTF-8?q?GM-267=20=EC=A4=91=EA=B3=A0=20=EA=B1=B0?= =?UTF-8?q?=EB=9E=98=20=EC=95=84=EC=9D=B4=ED=85=9C=20=EB=AA=A9=EB=A1=9D=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 15 ++++++ package.json | 1 + src/api/usedItemApi.ts | 32 +++++++++++++ src/component/main/home/UsedItem.tsx | 17 ++++++- src/component/main/home/UsedItemList.tsx | 59 +++++++++++++++++++----- src/model/usedItemPost.ts | 25 ++++++++++ src/store/index.ts | 7 ++- src/store/usedItem.ts | 24 ++++++++++ src/util/CurrentTime.ts | 7 +++ src/util/Dummy.ts | 24 ++-------- src/util/LocalDateTimeConverter.ts | 2 +- 11 files changed, 179 insertions(+), 34 deletions(-) create mode 100644 src/api/usedItemApi.ts create mode 100644 src/model/usedItemPost.ts create mode 100644 src/store/usedItem.ts create mode 100644 src/util/CurrentTime.ts diff --git a/package-lock.json b/package-lock.json index 9ca699e..71d082c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "react-bootstrap": "^2.7.2", "react-daum-postcode": "^3.1.1", "react-dom": "^18.2.0", + "react-intersection-observer": "^9.4.2", "react-redux": "^8.0.5", "react-router-dom": "^6.8.1", "redux": "^4.2.1", @@ -1779,6 +1780,14 @@ "react": "^18.2.0" } }, + "node_modules/react-intersection-observer": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.4.2.tgz", + "integrity": "sha512-AdK+ryzZ7U9ZJYttDUZ8q2Am3nqE0exg5Ryl5Y124KeVsix/1hGZPbdu58EqA98TwnzwDNWHxg/kwNawmIiUig==", + "peerDependencies": { + "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", @@ -3349,6 +3358,12 @@ "scheduler": "^0.23.0" } }, + "react-intersection-observer": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.4.2.tgz", + "integrity": "sha512-AdK+ryzZ7U9ZJYttDUZ8q2Am3nqE0exg5Ryl5Y124KeVsix/1hGZPbdu58EqA98TwnzwDNWHxg/kwNawmIiUig==", + "requires": {} + }, "react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", diff --git a/package.json b/package.json index 4d64104..2912cda 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "react-bootstrap": "^2.7.2", "react-daum-postcode": "^3.1.1", "react-dom": "^18.2.0", + "react-intersection-observer": "^9.4.2", "react-redux": "^8.0.5", "react-router-dom": "^6.8.1", "redux": "^4.2.1", diff --git a/src/api/usedItemApi.ts b/src/api/usedItemApi.ts new file mode 100644 index 0000000..48ec308 --- /dev/null +++ b/src/api/usedItemApi.ts @@ -0,0 +1,32 @@ +import {createApi, fetchBaseQuery} from "@reduxjs/toolkit/query/react"; +import {BASE_URL, USED_ITEM_POST_LIST} from "../util/Api"; +import {getCookie} from "../util/Cookie"; +import {UsedItemPost, UsedItemPostListArgs} from "../model/usedItemPost"; + +export const usedItemApi = createApi({ + reducerPath: 'usedItemApi', + baseQuery: fetchBaseQuery({ + baseUrl: BASE_URL, + prepareHeaders: (headers, { getState, endpoint, type, forced }) => { + headers.set("Authorization", 'Bearer ' + getCookie("access_token")); + + const townToken = getCookie("X-TOWN-TOKEN"); + if(townToken) + headers.set("X-TOWN-TOKEN", townToken); + return headers + } + }), + endpoints: (builder) => ({ + getUsedItem: builder.query({ + query: (UsedItemPostListArgs) => ({ + url: USED_ITEM_POST_LIST, + method : `GET`, + params: {pageNum : UsedItemPostListArgs.pageNum, + requestTime : UsedItemPostListArgs.requestTime} + }) + }) + + }) + }) + + diff --git a/src/component/main/home/UsedItem.tsx b/src/component/main/home/UsedItem.tsx index bf8753c..3e4ca39 100644 --- a/src/component/main/home/UsedItem.tsx +++ b/src/component/main/home/UsedItem.tsx @@ -4,6 +4,7 @@ import styled from "styled-components"; import theme from "../../../theme"; import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; import {compareLocalDateTimeToNow} from "../../../util/LocalDateTimeConverter"; +import {faBox} from "@fortawesome/free-solid-svg-icons"; const UsedItemWrapper = styled.div` display: flex; @@ -17,6 +18,10 @@ const UsedItemImageWrapper = styled.div` border-radius: 10px; border: 1px solid ${theme.color.gray}; margin-right: 25px; + display: flex; + justify-content: center; + align-items: center; + background-color: rgba(0,0,0,0.1); ` const UsedItemImage = styled.img` @@ -60,6 +65,11 @@ const Divider = styled.hr` background-color: ${theme.color.gray}; ` +const BlankImage = styled(FontAwesomeIcon)` + font-size: 40px; + color: rgba(0,0,0,0.6); +` + interface UsedItemProps { postId: string; imgUrl: string; @@ -71,11 +81,16 @@ interface UsedItemProps { } function UsedItem({postId, imgUrl, title,address, createdAt, price, interestCount}: UsedItemProps) { + return ( <> - + {imgUrl !== null ? : + <> + + + } {title.length > 24 ? `${title.substring(0,24)}...` : title} diff --git a/src/component/main/home/UsedItemList.tsx b/src/component/main/home/UsedItemList.tsx index f593d0e..7a0ffee 100644 --- a/src/component/main/home/UsedItemList.tsx +++ b/src/component/main/home/UsedItemList.tsx @@ -1,7 +1,12 @@ import styled from "styled-components"; import UsedItem from "./UsedItem"; -import {useState} from "react"; -import {createDummyUsedItemData, UsedItemPostList} from "../../../util/Dummy"; +import {createDummyUsedItemData} from "../../../util/Dummy"; +import {usedItemApi} from "../../../api/usedItemApi"; +import {useEffect, useRef, useState} from "react"; +import {currentTime} from "../../../util/CurrentTime"; +import {useInView} from "react-intersection-observer"; +import {ADD, useUsedItemSelector} from "../../../store/usedItem"; +import {useDispatch} from "react-redux"; const UsedItemListWrapper = styled.div` margin: 20px; @@ -9,7 +14,35 @@ const UsedItemListWrapper = styled.div` function UsedItemList() { - const dummy_data = createDummyUsedItemData(); + + + const [pageNum ,setPageNum] = useState(0) + const [ref, inView] = useInView(); + const usedItem = useUsedItemSelector(); + const dispatch = useDispatch(); + + + const [now,setNow]=useState(currentTime()); + let query = usedItemApi.useGetUsedItemQuery({ + pageNum, + requestTime : now + }); + useEffect(() => { + if (inView && !query.isLoading && query.data !== null) { + setPageNum(prev => prev+1); + } + }, [inView]) + + + + + useEffect(()=>{ + if (query.isSuccess) { + dispatch(ADD(query.data)); + } + },[query.data ,pageNum]) + + if(query.isLoading) return <>Now Loading... return ( @@ -17,19 +50,21 @@ function UsedItemList() { { - dummy_data.map(d => { + + usedItem.map(d => { return }) } +

diff --git a/src/model/usedItemPost.ts b/src/model/usedItemPost.ts new file mode 100644 index 0000000..156201e --- /dev/null +++ b/src/model/usedItemPost.ts @@ -0,0 +1,25 @@ +export interface UsedItemPost{ + previewPost : PreviewPost + previewPostCount : PreviewPostCount +} + + +export interface PreviewPost{ + postId : string; + representPictureUrl : string; + title : string; + address : string; + createdAt : string; + price : number; + tradeStatus:string, + isHide: boolean, +} +export interface PreviewPostCount { + interestCount : number; + viewCount : number; +} + +export interface UsedItemPostListArgs{ + pageNum: number, + requestTime: string, +} \ No newline at end of file diff --git a/src/store/index.ts b/src/store/index.ts index 696c8b0..48a7ec8 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -2,14 +2,19 @@ import {configureStore} from "@reduxjs/toolkit"; import {modeSlice} from "./mode"; import {townApi} from "../api/townApi"; import {townTokenSlice} from "./towntoken"; +import {usedItemApi} from "../api/usedItemApi"; +import {usedItemSlice} from "./usedItem"; export const store = configureStore({ reducer: { mode : modeSlice.reducer, townToken: townTokenSlice.reducer, - [townApi.reducerPath]: townApi.reducer + usedItem: usedItemSlice.reducer, + [townApi.reducerPath]: townApi.reducer, + [usedItemApi.reducerPath]: usedItemApi.reducer }, middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(townApi.middleware) + .concat(usedItemApi.middleware) }) export type RootState = ReturnType diff --git a/src/store/usedItem.ts b/src/store/usedItem.ts new file mode 100644 index 0000000..9f1e688 --- /dev/null +++ b/src/store/usedItem.ts @@ -0,0 +1,24 @@ +import {createSlice} from "@reduxjs/toolkit"; +import {useSelector} from "react-redux"; +import {RootState} from "./index"; +import {UsedItemPost} from "../model/usedItemPost"; + +const initialState: UsedItemPost[] = []; + +export const usedItemSlice = createSlice({ + name: 'usedItem', + initialState, + reducers: { + ADD: (state,action) => { + Array.prototype.push.apply(state, action.payload); + return state; + }, + CLEAR: () => { + return initialState; + } + + }, +}) +export const useUsedItemSelector = () => + useSelector((state: RootState) => state.usedItem) +export const {ADD,CLEAR } = usedItemSlice.actions; \ No newline at end of file diff --git a/src/util/CurrentTime.ts b/src/util/CurrentTime.ts new file mode 100644 index 0000000..c55fa81 --- /dev/null +++ b/src/util/CurrentTime.ts @@ -0,0 +1,7 @@ +export function currentTime() { + let cur = new Date(); + return `${cur.getFullYear()}-${cur.getMonth() + 1 < 10 ? "0"+(cur.getMonth() + 1) + :cur.getMonth() + 1}-${cur.getDate()<10?"0"+cur.getDate():cur.getDate()}T${cur.getHours() + <10? "0"+cur.getHours():cur.getHours()}:${cur.getMinutes()<10? + "0"+cur.getMinutes():cur.getMinutes()}:${cur.getSeconds()<10?"0"+cur.getSeconds():cur.getSeconds()}` +} \ No newline at end of file diff --git a/src/util/Dummy.ts b/src/util/Dummy.ts index 75a1169..548e615 100644 --- a/src/util/Dummy.ts +++ b/src/util/Dummy.ts @@ -1,21 +1,5 @@ +import {UsedItemPostList} from "../model/usedItemPost"; -interface UsedItemPost{ - previewPost : { - postId : string; - representPictureUrl : string; - title : string; - address : string; - createdAt : string; - price : number; - } - previewPostCount : { - interestCount : number; - viewCount : number; - } -} -interface UsedItemPostList{ - postListRetirveResponse : UsedItemPost -} const createDummyUsedItemData = () => { const dummy:UsedItemPostList[] = []; @@ -28,7 +12,9 @@ const createDummyUsedItemData = () => { title : `테스트 ${i+1}`, address : `서초 ${i+1}동`, createdAt : `2023-02-15T22:52:49.954301600`, - price : 30000 + price : 30000, + tradeStatus: "예약중", + isHide:false, }, previewPostCount : { interestCount : 10, @@ -42,4 +28,4 @@ const createDummyUsedItemData = () => { } export {createDummyUsedItemData}; -export type { UsedItemPostList }; + diff --git a/src/util/LocalDateTimeConverter.ts b/src/util/LocalDateTimeConverter.ts index 13fe14d..fecf2a0 100644 --- a/src/util/LocalDateTimeConverter.ts +++ b/src/util/LocalDateTimeConverter.ts @@ -15,7 +15,7 @@ function compareLocalDateTimeToNow(localDateTime:string) : string{ if(now.getTime() < target.getTime()) console.error("현재보다 더 이후에 생성된 게시글이 존재할 수 없습니다.") if(gap < 60) - return `${gap}초 전` + return `${Math.floor(gap)}초 전` if(gap < 3600) return `${Math.floor(gap/60)}분 전` if(gap < 3600 * 24)