diff --git a/.gitignore b/.gitignore
index 8f322f0..436eb65 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,3 +33,5 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
+
+.idea
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..b58b603
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,5 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/README.md b/README.md
index f4da3c4..721795c 100644
--- a/README.md
+++ b/README.md
@@ -1,34 +1 @@
-This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
-
-## Getting Started
-
-First, run the development server:
-
-```bash
-npm run dev
-# or
-yarn dev
-# or
-pnpm 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/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
-
-## 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/deployment) for more details.
+# Cat breed guesser
\ No newline at end of file
diff --git a/app/favicon.ico b/app/favicon.ico
deleted file mode 100644
index 718d6fe..0000000
Binary files a/app/favicon.ico and /dev/null differ
diff --git a/app/layout.tsx b/app/layout.tsx
index ae84562..d37d370 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -5,8 +5,7 @@ import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'] })
export const metadata: Metadata = {
- title: 'Create Next App',
- description: 'Generated by create next app',
+ title: 'Guess the cat breed!',
}
export default function RootLayout({
diff --git a/app/page.tsx b/app/page.tsx
index b5840ee..0e21740 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -1,113 +1,188 @@
-import Image from 'next/image'
+'use client'
+import Image from "next/image";
+import {useEffect, useState} from "react";
+import {Breed, TheCatAPI} from "@thatapicompany/thecatapi";
-export default function Home() {
- return (
-
-
-
- Get started by editing
- app/page.tsx
-
-
-
+export default function Page() {
+ const [html, setHtml] = useState(Loading...
);
+ const [apiKey, setApiKey] = useState("");
+ const [score, setScore] = useState(0);
+ const [message, setMessage] = useState("");
+ const [error, setError] = useState("");
+ const [currentImage, setCurrentImage] = useState("/loading.gif");
+ const [currentBreed, setCurrentBreed] = useState(Breed.AEGEAN);
+ const [randomBreeds, setRandomBreeds]: [randomBreeds: Breed[], setRandomBreeds: any] = useState([]);
+ const [api, setApi] = useState(new TheCatAPI(""));
-
-
-
+ useEffect(() => {
+ if (window.localStorage.getItem("apiKey") !== null && window.localStorage.getItem("apiKey") !== undefined) {
+ setApiKey(window.localStorage.getItem("apiKey") as string)
+ } else {
+ setApiKeyUi()
+ }
+ }, []);
-
-
- )
+ useEffect(() => {
+ if (apiKey === "") {
+ return
+ }
+ if (randomBreeds.length === 0) return
+ setGameUi()
+ }, [randomBreeds])
+
+ async function setNewImage() {
+ try {
+ const img= await api.images.searchImages({
+ limit: 1,
+ hasBreeds: true,
+ mimeTypes: ["jpg", "png"],
+ })
+ if (img[0].breeds === undefined) {
+ window.localStorage.removeItem("apiKey")
+ setMessage("Error!")
+ return
+ }
+ setCurrentBreed(img[0].breeds[0].id)
+ setCurrentImage(img[0].url)
+ } catch (e) {
+ console.error(e)
+ window.localStorage.removeItem("apiKey")
+ setMessage("API key error!")
+ }
+ }
+
+ useEffect(() => {
+ if (error == "API key error!") setApiKeyUi()
+ }, [error])
+
+ function setApiKeyUi() {
+ setHtml(
+
+
+
+
+
{error}
+
Please set your own TheCatAPI key.
+
You can get one completely for free here .
+
+ )
+ }
+
+ function setGameUi() {
+ setHtml(
+
+ {
+ randomBreeds.map((breed, i) => {
+ return checkAnswer(breed)}>{ Object.keys(Breed)[Object.values(Breed).indexOf(breed)].toLowerCase().replaceAll("_", " ").replace(/(^\w)|(\s+\w)/g, letter => letter.toUpperCase()) }
+ })
+ }
+
+
+
Score: {score}
+
+
+
{message}
+
+
+
+
+
+ {
+ window.localStorage.removeItem("apiKey")
+ setMessage("")
+ setApiKey("")
+ setApiKeyUi()
+ }}>Remove API key
+
+
)
+ }
+
+ function shuffle(array: T[]): T[] {
+ for (let i = array.length - 1; i > 0; i--) {
+ const j = Math.floor(Math.random() * (i + 1));
+ [array[i], array[j]] = [array[j], array[i]];
+ }
+ return array;
+ }
+
+ function getRandomBreeds() {
+ let keys= Object.values(Breed).filter((breed) => breed !== currentBreed)
+ keys = shuffle(keys)
+ let items = []
+ for (let i = 0; i < 3; i++) {
+ items.push(keys[i])
+ }
+ items.push(currentBreed)
+ setRandomBreeds(shuffle(items))
+ }
+
+ function checkAnswer(answer: Breed) {
+ if (answer === currentBreed) {
+ setScore(score + 1)
+ setMessage("Correct!")
+ } else {
+ setScore(0)
+ setMessage("Incorrect!")
+ }
+ setNewImage()
+ }
+
+ return html
}
diff --git a/next.config.js b/next.config.js
index 767719f..b3b866f 100644
--- a/next.config.js
+++ b/next.config.js
@@ -1,4 +1,12 @@
/** @type {import('next').NextConfig} */
-const nextConfig = {}
+const nextConfig = {
+ images: {
+ remotePatterns: [
+ {
+ hostname: 'cdn2.thecatapi.com',
+ }
+ ]
+ }
+}
module.exports = nextConfig
diff --git a/package-lock.json b/package-lock.json
index 39bd3fa..e46e567 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,7 @@
"name": "cbg",
"version": "0.1.0",
"dependencies": {
+ "@thatapicompany/thecatapi": "^1.0.2",
"@types/node": "20.4.1",
"@types/react": "18.2.14",
"@types/react-dom": "18.2.6",
@@ -394,6 +395,15 @@
"tslib": "^2.4.0"
}
},
+ "node_modules/@thatapicompany/thecatapi": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@thatapicompany/thecatapi/-/thecatapi-1.0.2.tgz",
+ "integrity": "sha512-hg49j/P0uWstsCUMzPZbxQRgKOcNTIVTnkSpEpAmKvKobaBJBtLmiXUDVrU+tEWoS5SVH1vZ0LH9oSZbtymNDA==",
+ "dependencies": {
+ "axios": "^0.27.2",
+ "form-data": "^4.0.0"
+ }
+ },
"node_modules/@types/json5": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
@@ -708,6 +718,11 @@
"resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
"integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag=="
},
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+ },
"node_modules/autoprefixer": {
"version": "10.4.14",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz",
@@ -759,6 +774,15 @@
"node": ">=4"
}
},
+ "node_modules/axios": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
+ "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
+ "dependencies": {
+ "follow-redirects": "^1.14.9",
+ "form-data": "^4.0.0"
+ }
+ },
"node_modules/axobject-query": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz",
@@ -995,6 +1019,17 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/commander": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
@@ -1121,6 +1156,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/dequal": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
@@ -1833,6 +1876,25 @@
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ=="
},
+ "node_modules/follow-redirects": {
+ "version": "1.15.2",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
+ "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
"node_modules/for-each": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
@@ -1841,6 +1903,19 @@
"is-callable": "^1.1.3"
}
},
+ "node_modules/form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/fraction.js": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
@@ -2659,6 +2734,25 @@
"node": ">=8.6"
}
},
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/mimic-fn": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
diff --git a/package.json b/package.json
index bb0bbcd..fa19225 100644
--- a/package.json
+++ b/package.json
@@ -3,12 +3,13 @@
"version": "0.1.0",
"private": true,
"scripts": {
- "dev": "next dev",
+ "dev": "next dev -p 6767",
"build": "next build",
- "start": "next start",
+ "start": "next start -p 6767",
"lint": "next lint"
},
"dependencies": {
+ "@thatapicompany/thecatapi": "^1.0.2",
"@types/node": "20.4.1",
"@types/react": "18.2.14",
"@types/react-dom": "18.2.6",
diff --git a/public/loading.gif b/public/loading.gif
new file mode 100644
index 0000000..b3ee14d
Binary files /dev/null and b/public/loading.gif differ
diff --git a/public/next.svg b/public/next.svg
deleted file mode 100644
index 5174b28..0000000
--- a/public/next.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/public/vercel.svg b/public/vercel.svg
deleted file mode 100644
index d2f8422..0000000
--- a/public/vercel.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file