Skip to content

Commit

Permalink
9 migrate to nextjs (#11)
Browse files Browse the repository at this point in the history
* Bootstrapped Nextjs app

* Removed protected page contents to see if it builds

* ran pnpm install

* Recreated the nextjs app and added the game

* fixed linting issues

* Added title and favicon metadata
  • Loading branch information
marlanperumal authored Oct 22, 2024
1 parent 2e0ee9b commit 65bab17
Show file tree
Hide file tree
Showing 30 changed files with 4,642 additions and 22 deletions.
6 changes: 6 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extends": ["next/core-web-vitals", "next/typescript"],
"rules": {
"@typescript-eslint/no-empty-object-type": "off"
}
}
36 changes: 36 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
28 changes: 28 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Next.js: debug server-side",
"type": "node-terminal",
"request": "launch",
"command": "pnpm run dev"
},
{
"name": "Next.js: debug client-side",
"type": "chrome",
"request": "launch",
"url": "http://localhost:3000"
},
{
"name": "Next.js: debug full stack",
"type": "node-terminal",
"request": "launch",
"command": "pnpm run dev",
"serverReadyAction": {
"pattern": "- Local:.+(https?://.+)",
"uriFormat": "%s",
"action": "debugWithChrome"
}
}
]
}
21 changes: 0 additions & 21 deletions LICENSE

This file was deleted.

37 changes: 36 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,36 @@
# eighty20-split
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).

## Getting Started

First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.

This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.

## Learn More

To learn more about Next.js, take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!

## Deploy on Vercel

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
23 changes: 23 additions & 0 deletions app/auth/confirm/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { type EmailOtpType } from "@supabase/supabase-js";
import { type NextRequest } from "next/server";

import { createClient } from "@/utils/supabase/server";
import { redirect } from "next/navigation";

export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const token_hash = searchParams.get("token_hash");
const type = searchParams.get("type") as EmailOtpType | null;
const next = searchParams.get("next") ?? "/";

if (token_hash && type) {
const supabase = createClient();

const { error } = await supabase.auth.verifyOtp({ type, token_hash });

if (!error) {
redirect(next);
}
}
redirect("/error");
}
3 changes: 3 additions & 0 deletions app/error/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function ErrorPage() {
return <p>Sorry, something went wrong</p>;
}
226 changes: 226 additions & 0 deletions app/game.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
"use client";

import { useRef, useEffect, useState, useCallback } from "react";
import { Card, CardHeader } from "@/components/ui/card";

interface Dot {
x: number;
y: number;
}

export default function Game() {
const canvasRef = useRef<HTMLCanvasElement>(null);
const canvasWidth = canvasRef.current?.width ?? 0;
const canvasHeight = canvasRef.current?.height ?? 0;

const totalDots = 1000;
const [dots, setDots] = useState<Dot[]>([]);
const [lineStart, setLineStart] = useState<Dot | null>(null);
const [lineEnd, setLineEnd] = useState<Dot | null>(null);
const [isDrawing, setIsDrawing] = useState(false);
const [percentageAbove, setPercentageAbove] = useState(0);
const [percentageBelow, setPercentageBelow] = useState(0);
const [score, setScore] = useState(0);

useEffect(() => {
// Function to generate random dots
function generateDots() {
const newDots: Dot[] = [];
for (let i = 0; i < totalDots; i++) {
const dot: Dot = {
x: Math.random() * canvasWidth,
y: Math.random() * canvasHeight,
};
newDots.push(dot);
}
setDots(newDots);
}
generateDots();
}, [canvasWidth, canvasHeight]);

// Function to draw the dots on the canvas
const drawDots = useCallback(
(ctx: CanvasRenderingContext2D) => {
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
ctx.fillStyle = "black";
dots.forEach((dot) => {
ctx.beginPath();
ctx.arc(dot.x, dot.y, 3, 0, Math.PI * 2);
ctx.fill();
});
},
[canvasWidth, canvasHeight, dots]
);

useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;

drawDots(ctx);
}, [drawDots]);

// Function to extend the line beyond the canvas edges
const extendLine = useCallback(
(start: Dot, end: Dot) => {
const slope = (end.y - start.y) / (end.x - start.x);

const extendedStart = { x: 0, y: start.y - slope * start.x };
const extendedEnd = {
x: canvasWidth,
y: start.y + slope * (canvasWidth - start.x),
};

return { extendedStart, extendedEnd };
},
[canvasWidth]
);

// Function to draw a line on the canvas
const drawLine = useCallback(
(ctx: CanvasRenderingContext2D, start: Dot, end: Dot) => {
const { extendedStart, extendedEnd } = extendLine(start, end);
// Fill the area above the line

ctx.fillStyle = "rgba(0, 0, 255, 0.4)"; // Semi-transparent blue
ctx.beginPath();
ctx.moveTo(extendedStart.x, extendedStart.y);
ctx.lineTo(extendedEnd.x, extendedEnd.y);
ctx.lineTo(canvasWidth, extendedEnd.y);
ctx.lineTo(canvasWidth, 0);
ctx.lineTo(0, 0);
ctx.lineTo(0, extendedStart.y);
ctx.closePath();
ctx.fill();

// Fill the area below the line
ctx.fillStyle = "rgba(255, 0, 0, 0.4)"; // Semi-transparent red
ctx.beginPath();
ctx.moveTo(extendedStart.x, extendedStart.y);
ctx.lineTo(extendedEnd.x, extendedEnd.y);
ctx.lineTo(canvasWidth, extendedEnd.y);
ctx.lineTo(canvasWidth, canvasHeight);
ctx.lineTo(0, canvasHeight);
ctx.lineTo(0, extendedStart.y);
ctx.closePath();
ctx.fill();

// Draw the line
ctx.strokeStyle = "black";
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(extendedStart.x, extendedStart.y);
ctx.lineTo(extendedEnd.x, extendedEnd.y);
ctx.stroke();
},
[canvasWidth, canvasHeight, extendLine]
);

// Function to calculate the split (dummy implementation)
const calculateSplit = useCallback(
(start: Dot, end: Dot) => {
// Implement the logic for calculating the split
const { extendedStart, extendedEnd } = extendLine(start, end);

let dotsAbove = 0;
let dotsBelow = 0;

const A = extendedEnd.y - extendedStart.y;
const B = extendedStart.x - extendedEnd.x;
const C = A * extendedStart.x + B * extendedStart.y;

dots.forEach((dot) => {
const position = A * dot.x + B * dot.y - C;
if (position > 0) {
dotsAbove++;
} else {
dotsBelow++;
}
});

const total = dotsAbove + dotsBelow;
const percentageAboveCalc = (dotsAbove / total) * 100;
const percentageBelowCalc = (dotsBelow / total) * 100;
setPercentageAbove(percentageAboveCalc);
setPercentageBelow(percentageBelowCalc);

// Calculate the score
const targetAbove = 80;
const diffAbove = Math.abs(percentageAboveCalc - targetAbove);
const baseScore = 100 - diffAbove;
// Scale the score such that an undersplit is penalized as much as an oversplit
const scoreCalc = (baseScore / 100) ** 16 * 1000;
setScore(scoreCalc);
},
[dots, extendLine]
);

useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;

// drawDots(ctx);

// Function to handle mouse down (start drawing the line)
const handleMouseDown = (e: MouseEvent) => {
setLineStart({ x: e.offsetX, y: e.offsetY });
setIsDrawing(true);
};

// Function to handle mouse move (draw the line as the mouse moves)
const handleMouseMove = (e: MouseEvent) => {
if (!isDrawing || !lineStart) return;
const newLineEnd: Dot = { x: e.offsetX, y: e.offsetY };
setLineEnd(newLineEnd);
drawDots(ctx);
drawLine(ctx, lineStart, newLineEnd);
};

// Function to handle mouse up (stop drawing the line)
const handleMouseUp = (e: MouseEvent) => {
setIsDrawing(false);
if (lineStart && lineEnd) {
const newLineEnd: Dot = { x: e.offsetX, y: e.offsetY };
setLineEnd(newLineEnd);
drawLine(ctx, lineStart, newLineEnd);
calculateSplit(lineStart, newLineEnd);
}
};

canvas.addEventListener("mousedown", handleMouseDown);
canvas.addEventListener("mousemove", handleMouseMove);
canvas.addEventListener("mouseup", handleMouseUp);

return () => {
canvas.removeEventListener("mousedown", handleMouseDown);
canvas.removeEventListener("mousemove", handleMouseMove);
canvas.removeEventListener("mouseup", handleMouseUp);
};
}, [dots, lineStart, lineEnd, isDrawing, calculateSplit, drawDots, drawLine]);

return (
<>
<canvas
ref={canvasRef}
width={500}
height={400}
id="gameCanvas"
className="cursor-crosshair mb-2"
></canvas>
<Card className="w-[500px]">
<CardHeader className="text-center">
<p className="font-bold">
{`Dots Above: ${percentageAbove.toFixed(
2
)}%, Dots Below: ${percentageBelow.toFixed(
2
)}%, Score: ${score.toFixed(0)}`}
</p>
</CardHeader>
</Card>
</>
);
}
Loading

0 comments on commit 65bab17

Please sign in to comment.