-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ランキングの各アイテムと表示のコンポーネント作成・Supabaseからランキングを取得するフックの作成・一度に取得する件数の制限・さらに読…
…み込む機能 ランキング機能の作成 #26
- Loading branch information
1 parent
7740bc4
commit 3b380c7
Showing
3 changed files
with
128 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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,12 @@ | ||
.ranking-item{ | ||
display: flex; | ||
justify-content: space-between; | ||
align-items: center; | ||
padding: 10px; | ||
margin: 5px 0; | ||
border-radius: 10px; | ||
background-color: #525252; | ||
font-size: 24px; | ||
border: 2px solid #333; | ||
box-shadow: 3px 3px 5px rgba(0, 0, 0, 0.2); | ||
} |
This file contains 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 React from 'react'; | ||
import { useFetchRanking } from '~/hooks/useFetchRanking'; | ||
import '../Ranking.css' | ||
|
||
interface RankingData{ | ||
rank: number | null; | ||
name: string; | ||
score: number | null; | ||
} | ||
|
||
export function RankingItem({ rank, name, score }: RankingData){ | ||
return ( | ||
<div className='ranking-item'> | ||
<span className='rank'>{rank}位</span> | ||
<span className='name'>{name}</span> | ||
<span className='score'>{score}</span> | ||
</div> | ||
); | ||
}; | ||
|
||
export function RankingList(){ | ||
const { ranking, loading, error, loadMore } = useFetchRanking(); | ||
|
||
if(loading) return <div>Loading...</div>; | ||
if(error) return <div>Error!!</div>; | ||
|
||
if(!ranking || ranking.length === 0){ | ||
return <div>No Ranking Data.</div>; | ||
} | ||
|
||
return ( | ||
<div className='ranking-list'> | ||
{ranking.map((item, index) => ( | ||
<RankingItem | ||
key={item.user_id} | ||
rank={item.rank} | ||
name={item.display_name} | ||
score={item.high_score} | ||
/> | ||
))} | ||
{loading ? ( | ||
<p>Loading...</p> | ||
) : ( | ||
<button onClick={loadMore}>Show More</button> | ||
)} | ||
</div> | ||
); | ||
} |
This file contains 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,68 @@ | ||
import { useState, useEffect, useCallback, useRef } from "react"; | ||
import { supabase } from "~/libs/supabase"; | ||
|
||
interface Stats{ | ||
id: string; | ||
user_id: string; | ||
display_name: string; | ||
created_at: string; | ||
updated_at: string; | ||
high_score: number | null; | ||
play_count: number; | ||
rank: number | null; | ||
}; | ||
|
||
export function useFetchRanking( limit = 20 ){ | ||
const [ranking, setRanking] = useState<Stats[] | null>(null); | ||
const [loading, setLoading] = useState(true); | ||
const [error, setError] = useState(false); | ||
const [page, setPage] = useState(0); | ||
const hasFetchedInitial = useRef(false); | ||
const scrollPosition = useRef(0); | ||
|
||
const fetchRankings = useCallback(async (fetchPage = page) => { | ||
setLoading(true); | ||
setError(false); | ||
|
||
const start = fetchPage * limit; | ||
const end = start + limit - 1; | ||
|
||
const { data, error } = await supabase | ||
.from('profiles_with_stats') | ||
.select('*') | ||
.order('rank', { ascending: true }) | ||
.range(start, end); | ||
|
||
if(error){ | ||
console.error('Error Fetching Rankings'); | ||
setError(true); | ||
} | ||
else{ | ||
setRanking((prev) => [...(prev ?? []), ...(data ?? [])]) | ||
} | ||
|
||
setLoading(false); | ||
|
||
window.scrollTo(0, scrollPosition.current); | ||
}, [page, limit]); | ||
|
||
useEffect(() => { | ||
if(!hasFetchedInitial.current){ | ||
fetchRankings(0); | ||
hasFetchedInitial.current = true; | ||
} | ||
}) | ||
|
||
useEffect(() => { | ||
if(page > 0){ | ||
fetchRankings(page); | ||
} | ||
}, [fetchRankings]); | ||
|
||
const loadMore = () => { | ||
scrollPosition.current = window.scrollY; | ||
setPage((prev) => prev + 1); | ||
}; | ||
|
||
return { ranking, loading, error, loadMore }; | ||
} |