Cleanup. Fix simple linter error.
This commit is contained in:
parent
ca1a2bf1ca
commit
9138a187a9
2 changed files with 32 additions and 36 deletions
|
|
@ -34,6 +34,7 @@ export default tseslint.config(
|
||||||
"error",
|
"error",
|
||||||
{ prefer: "type-imports", fixStyle: "inline-type-imports" },
|
{ prefer: "type-imports", fixStyle: "inline-type-imports" },
|
||||||
],
|
],
|
||||||
|
"@typescript-eslint/consistent-type-definitions": "off",
|
||||||
"@typescript-eslint/no-unused-vars": [
|
"@typescript-eslint/no-unused-vars": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
|
|
|
||||||
67
src/App.tsx
67
src/App.tsx
|
|
@ -10,24 +10,12 @@ import {
|
||||||
DialogContent,
|
DialogContent,
|
||||||
} from "@mui/material"
|
} from "@mui/material"
|
||||||
|
|
||||||
// TODO: Use Material UI later.
|
|
||||||
// TODO: Improve error-handling. Introduce some proper server response.
|
// TODO: Improve error-handling. Introduce some proper server response.
|
||||||
|
|
||||||
// For better type-error messages when you have inexhaustive switch cases.
|
|
||||||
function assertNever(value: never): never {
|
|
||||||
throw new Error(`Unexpected value: ${value}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// === Image ===
|
// === Image ===
|
||||||
type ImageId = string
|
type ImageId = string
|
||||||
type ImageRef = string
|
type ImageRef = string
|
||||||
|
|
||||||
type Image = {
|
|
||||||
id: ImageId
|
|
||||||
dimension: Dimension
|
|
||||||
source: ImageRef
|
|
||||||
}
|
|
||||||
|
|
||||||
// === Dimension ===
|
// === Dimension ===
|
||||||
|
|
||||||
type Dimension = {
|
type Dimension = {
|
||||||
|
|
@ -35,8 +23,11 @@ type Dimension = {
|
||||||
height: number
|
height: number
|
||||||
}
|
}
|
||||||
const Dimension = {
|
const Dimension = {
|
||||||
medium: { width: 180, height: 180 } as Dimension,
|
medium: { width: 180, height: 180 },
|
||||||
big: { width: 720, height: 720 } as Dimension,
|
big: { width: 720, height: 720 },
|
||||||
|
} satisfies {
|
||||||
|
medium: Dimension
|
||||||
|
big: Dimension
|
||||||
}
|
}
|
||||||
|
|
||||||
// === Page ===
|
// === Page ===
|
||||||
|
|
@ -69,7 +60,7 @@ const Page = {
|
||||||
},
|
},
|
||||||
// for hashing in maps to avoid identity problems
|
// for hashing in maps to avoid identity problems
|
||||||
key(page: Page): PageKey {
|
key(page: Page): PageKey {
|
||||||
return `${page.page}:${page.limit}`
|
return `${String(page.page)}:${String(page.limit)}`
|
||||||
},
|
},
|
||||||
isFirst(page: Page): boolean {
|
isFirst(page: Page): boolean {
|
||||||
return page.page === FIRST_PAGE
|
return page.page === FIRST_PAGE
|
||||||
|
|
@ -88,9 +79,11 @@ const picsumApiImageSchema = z.object({
|
||||||
type PicsumApiImage = z.infer<typeof picsumApiImageSchema>
|
type PicsumApiImage = z.infer<typeof picsumApiImageSchema>
|
||||||
|
|
||||||
async function getPicsumImages({ page, limit }: Page): Promise<PicsumApiImage[]> {
|
async function getPicsumImages({ page, limit }: Page): Promise<PicsumApiImage[]> {
|
||||||
const response = await fetch(`https://picsum.photos/v2/list?limit=${limit}&page=${page}`)
|
const response = await fetch(
|
||||||
|
`https://picsum.photos/v2/list?limit=${String(limit)}&page=${String(page)}`,
|
||||||
|
)
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`Failed to fetch images: ${response.status}`)
|
throw new Error(`Failed to fetch images: ${String(response.status)}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const json: unknown = await response.json()
|
const json: unknown = await response.json()
|
||||||
|
|
@ -99,16 +92,6 @@ async function getPicsumImages({ page, limit }: Page): Promise<PicsumApiImage[]>
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: We don't really need this.
|
|
||||||
async function getImages(page: Page): Promise<Image[]> {
|
|
||||||
const data = await getPicsumImages(page)
|
|
||||||
return data.map(({ id, download_url, width, height }) => ({
|
|
||||||
id: id as ImageId,
|
|
||||||
dimension: { width, height },
|
|
||||||
source: download_url as ImageRef,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getImageIds(page: Page): Promise<ImageId[]> {
|
async function getImageIds(page: Page): Promise<ImageId[]> {
|
||||||
const data = await getPicsumImages(page)
|
const data = await getPicsumImages(page)
|
||||||
return data.map(({ id }) => id)
|
return data.map(({ id }) => id)
|
||||||
|
|
@ -116,7 +99,7 @@ async function getImageIds(page: Page): Promise<ImageId[]> {
|
||||||
|
|
||||||
// Use this for `<img src=... />`
|
// Use this for `<img src=... />`
|
||||||
function getImageSource(id: ImageId, dimension: Dimension): ImageRef {
|
function getImageSource(id: ImageId, dimension: Dimension): ImageRef {
|
||||||
return `https://picsum.photos/id/${id}/${dimension.width}/${dimension.height}`
|
return `https://picsum.photos/id/${id}/${String(dimension.width)}/${String(dimension.height)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
// === Generic Remote Data ===
|
// === Generic Remote Data ===
|
||||||
|
|
@ -218,7 +201,7 @@ function useApp(): [State, Dispatch] {
|
||||||
return { ...state, selectedImage: undefined }
|
return { ...state, selectedImage: undefined }
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return assertNever(msg)
|
return msg satisfies never
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -254,10 +237,10 @@ function imageGridStyle(dimension: Dimension) {
|
||||||
return {
|
return {
|
||||||
display: "grid",
|
display: "grid",
|
||||||
gridTemplateColumns: {
|
gridTemplateColumns: {
|
||||||
xs: `repeat(2, ${dimension.width}px)`,
|
xs: `repeat(2, ${String(dimension.width)}px)`,
|
||||||
md: `repeat(5, ${dimension.width}px)`,
|
md: `repeat(5, ${String(dimension.width)}px)`,
|
||||||
},
|
},
|
||||||
gridAutoRows: `${dimension.height}px`,
|
gridAutoRows: `${String(dimension.height)}px`,
|
||||||
gap: 2,
|
gap: 2,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -287,7 +270,9 @@ function Images({ images }: { images: ImageId[] }) {
|
||||||
<Box
|
<Box
|
||||||
component="img"
|
component="img"
|
||||||
src={getImageSource(imageId, Dimension.medium)}
|
src={getImageSource(imageId, Dimension.medium)}
|
||||||
onClick={() => dispatch({ tag: "imageClicked", imageId })}
|
onClick={() => {
|
||||||
|
dispatch({ tag: "imageClicked", imageId })
|
||||||
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
width: Dimension.medium.width,
|
width: Dimension.medium.width,
|
||||||
height: Dimension.medium.height,
|
height: Dimension.medium.height,
|
||||||
|
|
@ -307,7 +292,9 @@ function ImageModal({ selectedImage }: { selectedImage: ImageId | undefined }) {
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
open={selectedImage !== undefined}
|
open={selectedImage !== undefined}
|
||||||
onClose={() => dispatch({ tag: "modalCloseButtonClicked" })}
|
onClose={() => {
|
||||||
|
dispatch({ tag: "modalCloseButtonClicked" })
|
||||||
|
}}
|
||||||
maxWidth={false}
|
maxWidth={false}
|
||||||
>
|
>
|
||||||
<DialogContent sx={{ p: 0, overflow: "hidden" }}>
|
<DialogContent sx={{ p: 0, overflow: "hidden" }}>
|
||||||
|
|
@ -359,7 +346,9 @@ export default function App() {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => dispatch({ tag: "previousButtonClicked" })}
|
onClick={() => {
|
||||||
|
dispatch({ tag: "previousButtonClicked" })
|
||||||
|
}}
|
||||||
disabled={Page.isFirst(state.page)}
|
disabled={Page.isFirst(state.page)}
|
||||||
>
|
>
|
||||||
prev
|
prev
|
||||||
|
|
@ -374,7 +363,13 @@ export default function App() {
|
||||||
>
|
>
|
||||||
{state.page.page}
|
{state.page.page}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Button onClick={() => dispatch({ tag: "nextButtonClicked" })}>next</Button>
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
dispatch({ tag: "nextButtonClicked" })
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
next
|
||||||
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue