Skip to content

Commit 5b85868

Browse files
authored
Merge pull request #85 from codersforcauses/issue-77-Incorporate_playable_games_into_individual_game_pages
Issue 77 incorporate playable games into individual game pages
2 parents f51e441 + 003251b commit 5b85868

15 files changed

Lines changed: 218 additions & 19 deletions
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// notes:
2+
// - width and height don't match itch.io, making games look smaller
3+
// - you can just embed from itch.io, but only the developer can get the embed link as far as i can tell
4+
// - would need to save embed link, width and height to db
5+
// - this method is reliant on itch.io staying up and not changing anything
6+
// - would want a play button so it doesn't autoload (especially for bigger more intense games)
7+
import Image from "next/image";
8+
import React, { useState } from "react";
9+
10+
import { Button } from "./button";
11+
12+
type GameEmbedProps = {
13+
embedID: string;
14+
gameWidth: number;
15+
gameHeight: number;
16+
gameImage: string;
17+
};
18+
19+
export function GameEmbed({
20+
embedID,
21+
gameWidth,
22+
gameHeight,
23+
gameImage,
24+
}: GameEmbedProps) {
25+
const [isPlaying, setIsPlaying] = useState(false);
26+
27+
return (
28+
<div
29+
className={`retroBorder flex items-center justify-center bg-background`}
30+
style={{ width: gameWidth + 26 * 2, height: gameHeight + 26 * 2 }}
31+
>
32+
{!isPlaying ? (
33+
<div>
34+
<Image
35+
src={gameImage}
36+
alt="Game Cover"
37+
width={gameWidth}
38+
height={gameHeight}
39+
className="absolute translate-x-[-50%] translate-y-[calc(-50%)] blur-sm"
40+
style={{ width: "auto", height: gameHeight - 52 }}
41+
/>
42+
<Button
43+
onClick={() => setIsPlaying(!isPlaying)}
44+
size={"lg"}
45+
className="absolute translate-x-[-50%] translate-y-[calc(-50%)] text-3xl"
46+
>
47+
Play
48+
</Button>
49+
</div>
50+
) : (
51+
<div>
52+
<iframe
53+
src={`https://itch.io/embed-upload/${embedID}?color=110e1a`}
54+
width={gameWidth}
55+
height={gameHeight}
56+
></iframe>
57+
</div>
58+
)}
59+
</div>
60+
);
61+
}

client/src/hooks/useGames.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ type ApiGame = {
2424
itchEmbedID: string;
2525
thumbnail: string | null;
2626
event: number | null;
27+
itchGameEmbedID: string;
28+
itchGameWidth: number;
29+
itchGameHeight: number;
2730
contributors: Contributor[];
2831
};
2932

client/src/pages/_app.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
55
import type { AppProps } from "next/app";
66
import { Fira_Code, Inter as FontSans, Jersey_10 } from "next/font/google";
77

8-
import Footer from "@/components/main/Footer";
98
import Navbar from "@/components/main/Navbar";
109

1110
const fontSans = FontSans({
@@ -37,7 +36,6 @@ export default function App({ Component, pageProps }: AppProps) {
3736
>
3837
<Navbar />
3938
<Component {...pageProps} />
40-
<Footer />
4139
</main>
4240
</QueryClientProvider>
4341
);

client/src/pages/games/[id].tsx

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useRouter } from "next/router";
33
import React from "react";
44
import { SocialIcon } from "react-social-icons";
55

6+
import { GameEmbed } from "@/components/ui/GameEmbed";
67
import { ItchEmbed } from "@/components/ui/ItchEmbed";
78
import { useGame } from "@/hooks/useGames";
89

@@ -51,6 +52,9 @@ export default function IndividualGamePage() {
5152
const gameTitle = game.name;
5253
const gameCover = game.gameCover;
5354
const gameDescription = game.description.split("\n");
55+
const gameEmbedID = game.itchGameEmbedID;
56+
const gameWidth = game.itchGameWidth;
57+
const gameHeight = game.itchGameHeight;
5458

5559
const completionLabels: Record<number, string> = {
5660
1: "WIP",
@@ -83,16 +87,27 @@ export default function IndividualGamePage() {
8387
return (
8488
<div className="min-h-screen bg-background font-sans text-foreground">
8589
<main>
86-
<section className="w-full bg-popover">
87-
<div className="mx-auto max-w-7xl p-0 sm:p-8">
88-
<Image
89-
src={gameCover}
90-
alt="Game Cover"
91-
width={800}
92-
height={800}
93-
className="max-h-[60vh] w-full object-cover sm:mx-auto sm:h-auto sm:max-h-[60vh] sm:rounded-2xl sm:object-contain"
94-
priority
95-
/>
90+
<section className="w-full items-center justify-center bg-popover">
91+
<div className="mx-auto flex max-w-7xl justify-center p-0 sm:p-8">
92+
{gameEmbedID != "0" ? (
93+
<div className="m-auto flex overflow-auto">
94+
<GameEmbed
95+
embedID={gameEmbedID}
96+
gameWidth={gameWidth}
97+
gameHeight={gameHeight}
98+
gameImage={gameCover}
99+
/>
100+
</div>
101+
) : (
102+
<Image
103+
src={gameCover}
104+
alt="Game Cover"
105+
width={800}
106+
height={800}
107+
className="max-h-[60vh] w-full object-cover sm:mx-auto sm:h-auto sm:max-h-[60vh] sm:rounded-2xl sm:object-contain"
108+
priority
109+
/>
110+
)}
96111
</div>
97112
</section>
98113

@@ -171,9 +186,8 @@ export default function IndividualGamePage() {
171186
</section>
172187

173188
<section className="mt-8 flex w-full flex-col items-center gap-6">
174-
{game.itchEmbedID && (
175-
<ItchEmbed embedID={game.itchEmbedID} name={gameTitle} />
176-
)}
189+
<ItchEmbed embedID={game.itchEmbedID} name={gameTitle} />
190+
177191
<h2 className="font-jersey10 text-5xl text-primary">ARTWORK</h2>
178192

179193
<div className="mx-auto mb-6 flex h-auto w-full max-w-4xl flex-col items-center gap-4 px-4 sm:flex-row sm:justify-center sm:gap-6 sm:px-6 md:h-60">

client/src/pages/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ export default function Landing() {
9696
width={600}
9797
height={430}
9898
alt="placeholder"
99-
className="min-w-80 border-[26px] border-accent [clip-path:polygon(20px_20px,calc(100%-20px)_20px,100%_32px,100%_30%,calc(100%-20px)_45%,calc(100%-20px)_calc(100%-8px),80%_calc(100%-8px),75%_calc(100%-20px),20px_calc(100%-20px),0%_60%,0%_30%,20px_25%)]"
99+
className="retroBorder min-w-80"
100100
/>
101101
<Image
102102
src="/bomb.png"

client/src/placeholderData.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ export const placeholderGames = [
5656
hostURL: "/",
5757
itchEmbedID: "1",
5858
thumbnail: "/landing_placeholder.png",
59+
itchGameEmbedID: 0,
60+
itchGameWidth: 0,
61+
itchGameHeight: 0,
5962
event: 1,
6063
},
6164
{
@@ -67,6 +70,9 @@ export const placeholderGames = [
6770
hostURL: "/",
6871
itchEmbedID: "1",
6972
thumbnail: "/landing_placeholder.png",
73+
itchGameEmbedID: 0,
74+
itchGameWidth: 0,
75+
itchGameHeight: 0,
7076
event: 1,
7177
},
7278
{
@@ -78,6 +84,9 @@ export const placeholderGames = [
7884
hostURL: "/",
7985
itchEmbedID: "1",
8086
thumbnail: "/landing_placeholder.png",
87+
itchGameEmbedID: 0,
88+
itchGameWidth: 0,
89+
itchGameHeight: 0,
8190
event: 1,
8291
},
8392
];

client/src/styles/globals.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,8 @@
5757
body {
5858
@apply bg-background text-foreground;
5959
}
60+
61+
.retroBorder {
62+
@apply border-[26px] border-accent [clip-path:polygon(20px_20px,calc(100%-20px)_20px,100%_32px,100%_30%,calc(100%-20px)_45%,calc(100%-20px)_calc(100%-8px),80%_calc(100%-8px),75%_calc(100%-20px),20px_calc(100%-20px),0%_60%,0%_30%,20px_25%)];
63+
}
6064
}

server/game_dev/admin.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ class GameShowcaseAdmin(admin.ModelAdmin):
2727

2828

2929
class GamesAdmin(admin.ModelAdmin):
30-
list_display = ("id", "name", "description", "completion", "active", "hostURL", "itchEmbedID", "thumbnail", "event")
30+
list_display = ("id", "name", "description", "completion", "active", "hostURL", "itchEmbedID", "thumbnail", "itchGameEmbedID", "itchGameWidth",
31+
"itchGameHeight", "event")
3132
search_fields = ["name", "description"]
3233

3334

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Generated by Django 5.1.15 on 2026-02-04 07:39
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("game_dev", "0009_merge_20260131_1044"),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name="game",
15+
name="itchGameEmbedID",
16+
field=models.PositiveBigIntegerField(
17+
blank=True,
18+
default=0,
19+
help_text="If a game has a web demo stored on itch.io, please enter the embed ID",
20+
null=True,
21+
),
22+
),
23+
migrations.AddField(
24+
model_name="game",
25+
name="itchGameHeight",
26+
field=models.PositiveBigIntegerField(default=0),
27+
),
28+
migrations.AddField(
29+
model_name="game",
30+
name="itchGameWidth",
31+
field=models.PositiveBigIntegerField(default=0),
32+
),
33+
]
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Generated by Django 5.1.15 on 2026-02-05 06:34
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("game_dev", "0010_game_itchgameembedid_game_itchgameheight_and_more"),
10+
("game_dev", "0010_merge_20260131_1118"),
11+
]
12+
13+
operations = []

0 commit comments

Comments
 (0)