Skip to content

Commit

Permalink
ランキングの各アイテムと表示のコンポーネント作成・Supabaseからランキングを取得するフックの作成・一度に取得する件数の制限・さらに読…
Browse files Browse the repository at this point in the history
…み込む機能 ランキング機能の作成 #26
  • Loading branch information
K-Yoshizawa authored and shun-shobon committed Nov 6, 2024
1 parent 7740bc4 commit 3b380c7
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 0 deletions.
12 changes: 12 additions & 0 deletions app/Ranking.css
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);
}
48 changes: 48 additions & 0 deletions app/components/Ranking.tsx
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>
);
}
68 changes: 68 additions & 0 deletions app/hooks/useFetchRanking.ts
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 };
}

0 comments on commit 3b380c7

Please sign in to comment.