diff --git a/package-lock.json b/package-lock.json index 13346f6..8b0c4e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,8 @@ "@emotion/styled": "^11.14.1", "@mui/material": "^9.0.1", "react": "^19.2.6", - "react-dom": "^19.2.6" + "react-dom": "^19.2.6", + "zod": "^4.4.3" }, "devDependencies": { "@eslint/js": "^10.0.1", @@ -3472,7 +3473,6 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index 73b191c..aca2bdb 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "@emotion/styled": "^11.14.1", "@mui/material": "^9.0.1", "react": "^19.2.6", - "react-dom": "^19.2.6" + "react-dom": "^19.2.6", + "zod": "^4.4.3" }, "devDependencies": { "@eslint/js": "^10.0.1", diff --git a/src/App.tsx b/src/App.tsx index 1127eb0..0e818e0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,12 +1,78 @@ -import { CssBaseline, Typography } from '@mui/material'; +import { z } from 'zod' +// TODO: Use Material UI later. +// TODO: Improve error-handling. Introduce some proper server response. + +type ImageId = string +type ImageRef = string + +type Image = { + id: ImageId, + dimension: Dimension, + source: ImageRef, +} + +type Dimension = { + width: number, + height: number, +} + +// WARNING: page starts at 1 +type Pagination = { + page: number, + limit: number, // page size +} + +const Pagination = { + init(): Pagination { return { page: 1, limit: 10 } }, +} + + +// === api === +const picsumApiImageSchema = z.object({ + id: z.string(), + author: z.string(), + width: z.number(), + height: z.number(), + url: z.string(), + download_url: z.string(), // WARNING: the api-endpoint returns "url" and "download_url". You definitely want "download_url" for image sources. +}) +type PicsumApiImage = z.infer + +async function getPicsumImages({ page, limit }: Pagination): Promise { + const response = await fetch(`https://picsum.photos/v2/list?limit=${limit}&page=${page}`) + if (!response.ok) { + throw new Error(`Failed to fetch images: ${response.status}`) + } + + const json: unknown = await response.json() + const data = z.array(picsumApiImageSchema).parse(json) + + return data +} + +async function getImages(pagination: Pagination): Promise { + const data = await getPicsumImages(pagination) + return data.map(({ id, download_url, width, height }) => ({ id: id as ImageId, dimension: { width, height }, source: download_url as ImageRef })) +} + +async function getImageIds(pagination: Pagination): Promise { + const data = await getPicsumImages(pagination) + return data.map(({ id }) => id) +} + +// Use this for `` +function getImageSource(id: ImageId, dimension: Dimension): ImageRef { + return `https://picsum.photos/id/${id}/${dimension.width}/${dimension.height}`; +} + +// test +// (async () => { +// const imgs = await getImages({ page: 2, limit: 10 }) +// console.log(imgs) +// })() export default function App() { return ( - <> - -
- Hello world -
- - ); +

hello world

+ ) }