Skip to content

Commit

Permalink
Merge pull request #185 from boostcampwm-2021/feature/182
Browse files Browse the repository at this point in the history
프로필 페이지 무한 스크롤 적용
  • Loading branch information
jyh0521 authored Nov 25, 2021
2 parents e4f3de7 + 2c6a4e8 commit bb83204
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 50 deletions.
14 changes: 11 additions & 3 deletions back-end/api-server/database/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,17 @@ export const insertIntoTable = (table, into, values) => {
return connectionQuery(queryLine);
};

export const innerJoinTable = async (column, tableA, tableB, on = null, condition = null) => {
let queryLine = `SELECT ${column} FROM ${tableA} INNER JOIN ${tableB} ON ${on} `;
queryLine += condition ? `WHERE ${condition}` : ``;
export const innerJoinTable = async (
column,
tableA,
tableB,
on = null,
condition = null,
limit = null
) => {
let queryLine = `SELECT ${column} FROM ${tableA} INNER JOIN ${tableB} ON ${on}`;
queryLine += condition ? ` WHERE ${condition}` : ``;
queryLine += limit ? ` LIMIT ${limit}` : ``;
return connectionQuery(queryLine);
};

Expand Down
1 change: 0 additions & 1 deletion back-end/api-server/routes/gameRecord.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ const GameRecordRouter = express.Router();

GameRecordRouter.post('/', async (req, res, next) => {
const { game, players } = req.body;
console.log('hello', req.body);
const insertGameInfoResult = await insertGameInfo(game);
const insertPlayerInfoResult = await insertPlayerInfo(game.game_id, players);
if (insertGameInfoResult && insertPlayerInfoResult) {
Expand Down
21 changes: 17 additions & 4 deletions back-end/api-server/routes/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,21 @@ ProfileRouter.post('/total', async (req, res, next) => {
try {
const [{ oauth_id }] = await getOauthId(req.body.nickname);
const totalList = await getTotalInDB(oauth_id);
const recentList = await getRecentInDB(oauth_id);
const [total, win] = totalList;
res.status(200).json({ total, win, recentList });
const data = { ...total[0], ...win[0] };
res.status(200).json(data);
} catch (error) {
console.log(error);
res.status(401).json({ error: '잘못된 인증입니다.' });
}
});

ProfileRouter.post('/recent', async (req, res, next) => {
try {
const { nickname, offset, limit } = req.body;
const [{ oauth_id }] = await getOauthId(nickname);
const data = await getRecentInDB(oauth_id, offset, limit);
res.status(200).json(data);
} catch (error) {
console.log(error);
res.status(401).json({ error: '잘못된 인증입니다.' });
Expand Down Expand Up @@ -81,13 +93,14 @@ const getTotalInDB = async (id) => {
]);
};

const getRecentInDB = (id) => {
const getRecentInDB = (id, offset, limit) => {
return innerJoinTable(
'game_date, game_mode, ranking, play_time, attack_cnt, attacked_cnt',
'PLAY',
'GAME_INFO',
'PLAY.game_id = GAME_INFO.game_id',
`oauth_id='${id}'`
`oauth_id='${id}'`,
`${offset}, ${limit}`
);
};

Expand Down
92 changes: 92 additions & 0 deletions front-end/src/components/InfiniteScroll/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import React, { useCallback, useEffect, useRef, useState } from 'react';
import './style.scss';

const drawRecent = (list: Array<any>) => {
if (list.length === 0) return;
return (
<>
{list.map((value) => (
<div className="recent__list" key={value.game_date}>
<div>{value.game_date.slice(0, 10)}</div>
<div>{value.game_mode === 'normal' ? '일반전' : '1 vs 1'}</div>
<div>{value.ranking}</div>
<div>{value.play_time}</div>
<div>{value.attack_cnt}</div>
<div>{value.attacked_cnt}</div>
</div>
))}
</>
);
};

export default function InfiniteScroll({
nickname,
MAX_ROWS,
fetchURL,
type,
}: {
nickname: string | undefined;
MAX_ROWS: number;
fetchURL: string;
type: string;
}) {
const [pageNum, setPageNum] = useState(0);
const [loading, setLoading] = useState(false);

const rootRef = useRef(null);
const observerRef = useRef<any>(null);

const [list, setList] = useState<any>([]);
const [hasMore, setHasMore] = useState(false);

useEffect(() => {
setLoading(true);
fetch(fetchURL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ nickname, limit: MAX_ROWS, offset: pageNum }),
})
.then((res) => res.json())
.then((data) => {
setList((prev: any) => {
return [...prev, ...data];
});
setHasMore(data.length > 0);
setLoading(false);
})
.catch((error) => {
console.log('error:', error);
});
}, [pageNum]);

const targetRef = useCallback(
(node) => {
if (loading) return;
let options = {
root: rootRef.current,
rootMargin: '50px',
threshold: 0,
};

if (observerRef.current) observerRef.current.disconnect();
observerRef.current = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting && hasMore) {
setPageNum((prev) => prev + MAX_ROWS);
}
}, options);
if (node) observerRef.current.observe(node);
},
[loading, hasMore]
);

return (
<div
className={`fancy__scroll ${type === 'profile' ? 'recent__list--scroll' : ''}`}
ref={rootRef}
>
{type === 'profile' && drawRecent(list)}
<div ref={targetRef}></div>
<>{loading && <div className="loading">로딩중</div>}</>
</div>
);
}
15 changes: 15 additions & 0 deletions front-end/src/components/InfiniteScroll/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
@import 'common/styles/base';

.recent__list--scroll {
height: 250px;
overflow-y: scroll;

.recent__list {
display: grid;
grid-template-columns: repeat(6, 1fr);
text-align: center;
font-size: 17px;
padding: 15px 40px;
box-sizing: border-box;
}
}
41 changes: 13 additions & 28 deletions front-end/src/pages/ProfilePage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useNavigate, useParams } from 'react-router-dom';
import { useAppDispatch } from '../../app/hooks';
import { updateNickname } from '../../features/user/userSlice';
import { useSocket } from '../../context/SocketContext';
import InfiniteScroll from '../../components/InfiniteScroll';

export default function Profile() {
const { nickname } = useParams();
Expand All @@ -18,12 +19,14 @@ export default function Profile() {
const translations = [
['total_game_cnt', '총 게임 수'],
['total_play_time', '총 플레이 시간'],
['single_player_win', '1vs1 승리 횟수'],
['single_player_win', '1 vs 1 승리 횟수'],
['multi_player_win', '일반전 승리 횟수'],
['total_attack_cnt', '총 공격 횟수'],
];

const [recentList, setRecentList] = useState<string[][]>([]);
const [statsticsState, setStatsticsState] = useState({});

const [editMode, setEditMode] = useState(false);
const [userState, setUserState] = useState({
id: authProfile.id,
Expand All @@ -49,24 +52,6 @@ export default function Profile() {
);
};

const drawRecent = (recentList: Array<any>) => {
if (recentList.length === 0) return;
return (
<>
{recentList.map((value) => (
<div className="recent-list" key={value.game_date}>
<div>{value.game_date.slice(0, 10)}</div>
<div>{value.game_mode === 'normal' ? '일반전' : '1 vs 1'}</div>
<div>{value.ranking}</div>
<div>{value.play_time}</div>
<div>{value.attack_cnt}</div>
<div>{value.attacked_cnt}</div>
</div>
))}
</>
);
};

const changeTextArea = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
if (!e.target) return;
setUserState({ ...userState, stateMessage: e.target.value });
Expand Down Expand Up @@ -98,8 +83,6 @@ export default function Profile() {
};

useEffect(() => {
setUserState({ ...userState, nickname });

fetch('/api/profile/stateMessage', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
Expand All @@ -121,15 +104,13 @@ export default function Profile() {
})
.then((res) => res.json())
.then((data) => {
setStatsticsState({ ...statsticsState, ...data.total[0], ...data.win[0] });
setRecentList([...data.recentList]);
setStatsticsState({ ...statsticsState, ...data });
})
.catch((error) => {
navigate('/error/unauthorize', { replace: true });
console.log('error:', error);
});
return () => {};
}, [nickname]);
}, []);

return (
<AppbarLayout>
Expand Down Expand Up @@ -164,7 +145,7 @@ export default function Profile() {
)}
</div>
<div className="total-section">
<div className="statistics-section ">
<div className="statistics-section">
<div className="absolute_border_bottom statistics-section__header">
<SectionTitle>통계</SectionTitle>
</div>
Expand All @@ -179,8 +160,12 @@ export default function Profile() {
<div key={value}>{value}</div>
))}
</div>

<div className="recent-list__scroll fancy__scroll">{drawRecent(recentList)}</div>
<InfiniteScroll
nickname={userState.nickname}
MAX_ROWS={5}
fetchURL="/api/profile/recent"
type="profile"
/>
</div>
</div>
</div>
Expand Down
14 changes: 0 additions & 14 deletions front-end/src/pages/ProfilePage/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -124,20 +124,6 @@
margin: 20px 30px 0px;
box-sizing: border-box;
}

.recent-list__scroll {
height: 250px;
overflow: auto;

.recent-list {
display: grid;
grid-template-columns: repeat(6, 1fr);
text-align: center;
font-size: 17px;
padding: 15px 40px;
box-sizing: border-box;
}
}
}
}
}

0 comments on commit bb83204

Please sign in to comment.