Skip to content
Snippets Groups Projects
Commit dff98e61 authored by Yusuf Akgül's avatar Yusuf Akgül :hatching_chick:
Browse files

created lib folder to store fetch requests and utils + created types file + code cleanup, comments

parent 5d695c64
No related branches found
No related tags found
1 merge request!1Feature games list
Pipeline #32657 passed
...@@ -4,7 +4,11 @@ ...@@ -4,7 +4,11 @@
# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB. # Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings # See the documentation for all the connection string options: https://pris.ly/d/connection-strings
DATABASE_URL="file:./dev.db" DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"
IMDB_CLIENT_ID="imdb-client-id" TWITCH_AUTH_BASE_URL="https://id.twitch.tv/oauth2"
IMDB_AUTH="Bearer imdb-auth-id" IGDB_BASE_URL="https://api.igdb.com/v4"
\ No newline at end of file IGDB_IMG_BASE_URL="https://images.igdb.com/igdb/image/upload"
TWITCH_CLIENT_ID="imdb_client_id"
TWITCH_CLIENT_SECRET="imdb_auth_id"
\ No newline at end of file
import Image from "next/image"; import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
// this is a single game helper-component, only for design purposes
export default function Game({ id, name, cover }: { id: any, name: any, cover: any }) { export default function Game({ id, name, cover }: { id: any, name: any, cover: any }) {
return ( return (
<div> <div>
<h1>{name}</h1> <h1>{name}</h1>
<Link href={`/games/${id}`}> <Link href={`/games/${id}`}>
<Image src={"https:" + cover.url} alt={name} width={200} height={200} /> <Image src={cover.url} alt={name} width={264} height={374} priority={true} />
</Link> </Link>
</div> </div>
) )
......
// loading component, this renders when loading in /games happens
export default function Loading() { export default function Loading() {
return ( return (
<div> <div>
......
import { getGame, getGames } from "@/lib/igdb";
import { IGame } from "@/types/types";
import Image from "next/image"; import Image from "next/image";
type DetailView = { // renders a single game detail page
id: number; export default async function GameDetail({ params }: { params: { gameid: string } }) {
name: string; const data: IGame[] = await getGame(parseInt(params.gameid))
cover: { url: string };
summary: string;
}
type DetailViewArray = DetailView[];
export default async function GameDetail({ params }: { params: any }) {
const res = await fetch("https://api.igdb.com/v4/games", {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Client-ID': `${process.env.IMDB_CLIENT_ID}`,
'Authorization': `${process.env.IMDB_AUTH}`,
},
body: `fields name,cover.*,summary; where cover != null; where id = ${params.gameid};`,
});
const data: DetailViewArray = await res.json()
return ( return (
<div> <div>
Game Detail Game Detail
<h1>{data[0].name}</h1> <h1>{data[0].name}</h1>
<Image src={"https:" + data[0].cover.url} alt={data[0].name} width={200} height={200} priority /> <Image src={data[0].cover.url} alt={data[0].name} width={264} height={374} priority={true} />
<p>{data[0].summary}</p> <p>{data[0].summary}</p>
</div> </div>
) )
} }
// pre-renders static paths for all fetched games for faster page loads
export async function generateStaticParams() {
const games = await getGames()
return games.map((game) => ({
gameid: game.id.toString(),
}));
}
\ No newline at end of file
// root loading component, this renders when any loading happens
export default function Loading() { export default function Loading() {
return ( return (
<div> <div>
......
import { getGames } from "@/lib/igdb";
import { IGame } from "@/types/types";
import Game from "./Game"; import Game from "./Game";
type DetailView = { // renders a list of games
id: number;
name: string;
cover: { url: string };
summary: string;
}
type DetailViewArray = DetailView[];
export default async function GamesList() { export default async function GamesList() {
const res = await fetch("https://api.igdb.com/v4/games", { const data: IGame[] = await getGames()
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Client-ID': `${process.env.IMDB_CLIENT_ID}`,
'Authorization': `${process.env.IMDB_AUTH}`,
},
body: `fields name,cover.*; limit 40; where cover != null;`,
});
const data: DetailViewArray = await res.json()
return ( return (
<div> <div>
......
"use client"
import { Container } from '@mui/material'
import { Inter } from 'next/font/google' import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'] }) const inter = Inter({ subsets: ['latin'] })
...@@ -7,6 +10,7 @@ export const metadata = { ...@@ -7,6 +10,7 @@ export const metadata = {
description: 'Generated by create next app', description: 'Generated by create next app',
} }
// this is the root layout for all pages ({children})
export default function RootLayout({ export default function RootLayout({
children, children,
}: { }: {
...@@ -14,7 +18,11 @@ export default function RootLayout({ ...@@ -14,7 +18,11 @@ export default function RootLayout({
}) { }) {
return ( return (
<html lang="en"> <html lang="en">
<body className={inter.className}>{children}</body> <body className={inter.className}>
<Container>
{children}
</Container>
</body>
</html> </html>
) )
} }
// renders home page
export default function Home() { export default function Home() {
return ( return (
<main> <main>
......
import { IAuth, IGame } from "@/types/types"
import { getImageURL } from "./utils"
const AUTH_BASE_URL = process.env.TWITCH_AUTH_BASE_URL ?? ''
const IGDB_BASE_URL = process.env.IGDB_BASE_URL ?? ''
const CLIENT_ID = process.env.TWITCH_CLIENT_ID ?? ''
const CLIENT_SECRET = process.env.TWITCH_CLIENT_SECRET ?? ''
let _auth: IAuth
let _lastUpdate = 0
// fetches a new token if the current one is expired
async function getToken(): Promise<IAuth> {
if (!_auth || Date.now() - _lastUpdate > _auth.expires_in) {
const url = new URL(`${AUTH_BASE_URL}/token`)
url.searchParams.set('client_id', CLIENT_ID)
url.searchParams.set('client_secret', CLIENT_SECRET)
url.searchParams.set('grant_type', 'client_credentials')
const response = await fetch(url, { method: 'POST' })
_auth = await response.json() as IAuth
_lastUpdate = Date.now()
}
return _auth
}
// fetches the top 500 games with a rating of 96 or higher
export async function getGames(): Promise<IGame[]> {
const auth = await getToken()
const url = new URL(`${IGDB_BASE_URL}/games`)
const response = await fetch(url, {
method: 'POST',
headers: {
'Client-ID': CLIENT_ID,
'Authorization': `Bearer ${auth.access_token}`
},
body: `fields name, cover.*; limit 500; where rating > 96 & cover != null;`
})
const games = await response.json() as IGame[]
games.forEach(game => {
game.cover.url = getImageURL(game.cover.image_id, 'cover_big')
})
return games
}
// fetches a single game by id
export async function getGame(id: number): Promise<IGame[]> {
const auth = await getToken()
const url = new URL(`${IGDB_BASE_URL}/games`)
const response = await fetch(url, {
method: 'POST',
headers: {
'Client-ID': CLIENT_ID,
'Authorization': `Bearer ${auth.access_token}`
},
body: `fields name, cover.*, summary; where cover != null; where id = ${id};`
})
const games = await response.json() as IGame[]
games.forEach(game => {
game.cover.url = getImageURL(game.cover.image_id, 'cover_big')
})
return games
}
\ No newline at end of file
const IGDB_IMG_BASE_URL = process.env.IGDB_IMG_BASE_URL ?? ''
// changes the default size of the image to be fetched
export function getImageURL(hashId: string, size: string): string {
return `${IGDB_IMG_BASE_URL}/t_${size}_2x/${hashId}.jpg`
}
\ No newline at end of file
export enum EAgeRatingCategory {
'ESRB' = 0,
'PEGI' = 2,
'CERO' = 3,
'USK' = 4,
'GRAC' = 5,
'CLASS_IND' = 6,
'ACB' = 7,
}
export enum EAgeRatingRating {
'Three' = 1,
'Seven' = 2,
'Twelve' = 3,
'Sixteen' = 4,
'Eighteen' = 5,
'RP' = 6,
'EC' = 7,
'E' = 8,
'E10' = 9,
'T' = 10,
'M' = 11,
'AO' = 12,
'CERO_A' = 13,
'CERO_B' = 14,
'CERO_C' = 15,
'CERO_D' = 16,
'CERO_Z' = 17,
'USK_0' = 18,
'USK_6' = 19,
'USK_12' = 20,
'USK_16' = 21,
'USK_18' = 22,
'GRAC_ALL' = 23,
'GRAC_Twelve' = 24,
'GRAC_Fifteen' = 25,
'GRAC_Eighteen' = 26,
'GRAC_TESTING' = 27,
'CLASS_IND_L' = 28,
'CLASS_IND_Ten' = 29,
'CLASS_IND_Twelve' = 30,
'CLASS_IND_Fourteen' = 31,
'CLASS_IND_Sixteen' = 32,
'CLASS_IND_Eighteen' = 33,
'ACB_G' = 34,
'ACB_PG' = 35,
'ACB_M' = 36,
'ACB_MA15' = 37,
'ACB_R18' = 38,
'ACB_RC' = 39,
}
export enum EGameCategory {
'main_game' = 0,
'dlc_addon' = 1,
'expansion' = 2,
'bundle' = 3,
'standalone_expansion' = 4,
'mod' = 5,
'episode' = 6,
'season' = 7,
'remake' = 8,
'remaster' = 9,
'expanded_game' = 10,
'port' = 11,
'fork' = 12,
'pack' = 13,
'update' = 14,
}
export enum EGameStatus {
'released' = 0,
'alpha' = 2,
'beta' = 3,
'early_access' = 4,
'offline' = 5,
'cancelled' = 6,
'rumored' = 7,
'delisted' = 8,
}
export interface IAuth {
access_token: string;
expires_in: number;
token_type: 'bearer';
}
export interface IGame {
id: number;
age_ratings: EAgeRatingCategory[];
aggregrated_rating: number;
aggregrated_rating_count: number;
alternative_names: number[];
artworks: number[];
bundles: number[];
category: EGameCategory;
checksum: string;
collection: number;
cover: ICover;
created_at: number;
dlcs: number[];
expanded_games: number[];
expansions: number[];
external_games: number[];
first_release_date: number;
follows: number;
forks: number[];
franchise: number;
franchises: number[];
game_engines: number[];
game_localizations: number[];
game_modes: number[];
genres: number[];
hypes: number;
involved_companies: number[];
keywords: number[];
language_supports: number[];
multiplayer_modes: number[];
name: string;
parent_game: string;
platforms: number[];
player_perspectives: number[];
ports: number[];
rating: number;
rating_count: number;
release_dates: number[];
remakes: number[];
remasters: number[];
screenshots: number[];
similar_games: number[];
slug: string;
standalone_expansions: number[];
status: EGameStatus;
storyline: string;
summary: string;
tags: number[];
themes: number[];
total_rating: number;
total_rating_count: number;
updated_at: number;
url: string;
version_parent: number;
version_title: string;
videos: number[];
websites: number[];
}
export interface ICover {
id: number;
alpha_channel: boolean;
animated: boolean;
checksum: string;
game: number;
game_localization: number;
height: number;
image_id: string;
url: string;
width: number;
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment