From 58693fdb0025a20f779a382d5893374a96b05b4e Mon Sep 17 00:00:00 2001 From: ChristoferMendes Date: Fri, 10 May 2024 12:10:08 -0300 Subject: [PATCH] feat: Add SeedersAndPeers component with skeleton loader --- .../seeders-and-peers-skeleton.tsx | 20 +++++ .../seeders-and-peers/seeders-and-peers.tsx | 54 +++++++++++++ .../game-details/seeders-and-peers/types.ts | 5 ++ .../seeders-and-peers/useMagnetData.tsx | 75 +++++++++++++++++++ 4 files changed, 154 insertions(+) create mode 100644 src/renderer/src/pages/game-details/seeders-and-peers/seeders-and-peers-skeleton.tsx create mode 100644 src/renderer/src/pages/game-details/seeders-and-peers/seeders-and-peers.tsx create mode 100644 src/renderer/src/pages/game-details/seeders-and-peers/types.ts create mode 100644 src/renderer/src/pages/game-details/seeders-and-peers/useMagnetData.tsx diff --git a/src/renderer/src/pages/game-details/seeders-and-peers/seeders-and-peers-skeleton.tsx b/src/renderer/src/pages/game-details/seeders-and-peers/seeders-and-peers-skeleton.tsx new file mode 100644 index 00000000..dc0f7381 --- /dev/null +++ b/src/renderer/src/pages/game-details/seeders-and-peers/seeders-and-peers-skeleton.tsx @@ -0,0 +1,20 @@ +import Skeleton from "react-loading-skeleton"; + +export function SeedersAndPeersSkeleton() { + return ( +
+ + +
+ ); +} diff --git a/src/renderer/src/pages/game-details/seeders-and-peers/seeders-and-peers.tsx b/src/renderer/src/pages/game-details/seeders-and-peers/seeders-and-peers.tsx new file mode 100644 index 00000000..5244d8e4 --- /dev/null +++ b/src/renderer/src/pages/game-details/seeders-and-peers/seeders-and-peers.tsx @@ -0,0 +1,54 @@ +import { GameRepack } from "@types"; +import { Sprout, Users } from "lucide-react"; + +import { useMagnetData } from "./useMagnetData"; +import { Tooltip } from "@renderer/components/tooltip/tooltip"; +import { SeedersAndPeersSkeleton } from "./seeders-and-peers-skeleton"; +import { vars } from "@renderer/theme.css"; + +interface SeedersAndPeersProps { + repack: GameRepack; +} + +export function SeedersAndPeers({ repack }: Readonly) { + const { magnetData, isLoading, error } = useMagnetData(repack.magnet); + + if (isLoading) { + return ; + } + + if (error) { + return null; + } + + return ( +
+ + + + {magnetData?.seeders} + + + + + + {magnetData?.peers} + + +
+ ); +} diff --git a/src/renderer/src/pages/game-details/seeders-and-peers/types.ts b/src/renderer/src/pages/game-details/seeders-and-peers/types.ts new file mode 100644 index 00000000..23532807 --- /dev/null +++ b/src/renderer/src/pages/game-details/seeders-and-peers/types.ts @@ -0,0 +1,5 @@ +export type TorrentData = { + seeders: number; + peers: number; + lastTracked?: Date; +}; diff --git a/src/renderer/src/pages/game-details/seeders-and-peers/useMagnetData.tsx b/src/renderer/src/pages/game-details/seeders-and-peers/useMagnetData.tsx new file mode 100644 index 00000000..b072ac22 --- /dev/null +++ b/src/renderer/src/pages/game-details/seeders-and-peers/useMagnetData.tsx @@ -0,0 +1,75 @@ +import { useCallback, useEffect, useState } from "react"; +import { TorrentData } from "./types"; + +const cache: Record = {}; + +export function useMagnetData(magnet: string) { + const [magnetData, setMagnetData] = useState(cache[magnet] || null); + const [isLoading, setIsLoading] = useState(() => { + if (cache[magnet]) { + return false; + } + + return true; + }); + const [error, setError] = useState(null); + + useEffect(() => { + if (!magnet) { + return; + } + + if (cache[magnet]) { + setMagnetData(cache[magnet]); + setIsLoading(false); + return; + } + + window.electron.getMagnetData(magnet).then( + (result) => { + if (result) { + setMagnetData(result); + setIsLoading(false); + + cache[magnet] = result; + cache[magnet].lastTracked = new Date(); + } + }, + (error) => { + setError(error); + setIsLoading(false); + } + ); + }, []); + + const invalidateCache = useCallback(() => { + const TWO_MINUTES = 2 * 60 * 1000; + const cacheExpiresIn = TWO_MINUTES; + + Object.keys(cache).forEach((key) => { + const lastTracked = cache[key].lastTracked; + + if (!lastTracked) { + return; + } + + if (Date.now() - lastTracked.getTime() > cacheExpiresIn) { + delete cache[key]; + } + }); + }, []); + + useEffect(() => { + invalidateCache(); + + return () => { + invalidateCache(); + } + }, []); + + return { + magnetData, + isLoading, + error, + }; +}