From 07728003b08ff6448e281b734102644eee643e25 Mon Sep 17 00:00:00 2001 From: Johannes Kettmann Date: Fri, 5 Apr 2024 08:35:34 +0200 Subject: [PATCH 1/2] Step 3: Transform response data in API layer --- src/api/feed.ts | 11 +++++++++-- src/api/media.ts | 3 ++- src/api/shout.ts | 6 ++++-- src/api/user.ts | 10 +++++++--- src/components/header/header.tsx | 2 +- src/components/shout/reply-dialog.tsx | 6 +++--- src/pages/feed/feed.tsx | 13 +++++++------ src/pages/user-profile/user-profile.tsx | 24 ++++++++++++------------ 8 files changed, 45 insertions(+), 30 deletions(-) diff --git a/src/api/feed.ts b/src/api/feed.ts index d4ef136..6927733 100644 --- a/src/api/feed.ts +++ b/src/api/feed.ts @@ -1,10 +1,17 @@ -import { FeedResponse } from "@/types"; +import { FeedResponse, Image, User } from "@/types"; import { apiClient } from "./client"; async function getFeed() { const response = await apiClient.get("/feed"); - return response.data; + const shouts = response.data.data; + const users = response.data.included.filter( + (u): u is User => u.type === "user" + ); + const images = response.data.included.filter( + (i): i is Image => i.type === "image" + ); + return { shouts, users, images }; } export default { getFeed }; diff --git a/src/api/media.ts b/src/api/media.ts index bf4a46c..6c506da 100644 --- a/src/api/media.ts +++ b/src/api/media.ts @@ -4,7 +4,8 @@ import { apiClient } from "./client"; async function uploadImage(formData: FormData) { const response = await apiClient.post<{ data: Image }>("/image", formData); - return response.data; + const image = response.data.data; + return image; } export default { uploadImage }; diff --git a/src/api/shout.ts b/src/api/shout.ts index 680cf6d..80b729d 100644 --- a/src/api/shout.ts +++ b/src/api/shout.ts @@ -4,7 +4,8 @@ import { apiClient } from "./client"; async function createShout(input: CreateShoutInput) { const response = await apiClient.post<{ data: Shout }>(`/shout`, input); - return response.data; + const shout = response.data.data; + return shout; } async function createReply({ shoutId, replyId }: CreateShoutReplyInput) { @@ -12,7 +13,8 @@ async function createReply({ shoutId, replyId }: CreateShoutReplyInput) { `/shout/${shoutId}/reply`, { replyId } ); - return response.data; + const reply = response.data.data; + return reply; } export default { createShout, createReply }; diff --git a/src/api/user.ts b/src/api/user.ts index 5cc35a3..02a487e 100644 --- a/src/api/user.ts +++ b/src/api/user.ts @@ -4,19 +4,23 @@ import { apiClient } from "./client"; async function getMe() { const response = await apiClient.get<{ data: Me }>("/me"); - return response.data; + const me = response.data.data; + return me; } async function getUser(handle: string) { const response = await apiClient.get<{ data: User }>(`/user/${handle}`); - return response.data; + const user = response.data.data; + return user; } async function getUserShouts(handle: string) { const response = await apiClient.get( `/user/${handle}/shouts` ); - return response.data; + const shouts = response.data.data; + const images = response.data.included; + return { shouts, images }; } export default { getMe, getUser, getUserShouts }; diff --git a/src/components/header/header.tsx b/src/components/header/header.tsx index b503ad1..9a7be17 100644 --- a/src/components/header/header.tsx +++ b/src/components/header/header.tsx @@ -15,7 +15,7 @@ export function Header() { useEffect(() => { UserApi.getMe() - .then((response) => setMe(response.data)) + .then((me) => setMe(me)) .catch(() => setHasError(true)) .finally(() => setIsLoadingMe(false)); }, []); diff --git a/src/components/shout/reply-dialog.tsx b/src/components/shout/reply-dialog.tsx index cea5935..cc597ae 100644 --- a/src/components/shout/reply-dialog.tsx +++ b/src/components/shout/reply-dialog.tsx @@ -40,7 +40,7 @@ export function ReplyDialog({ children, shoutId }: ReplyDialogProps) { useEffect(() => { UserApi.getMe() - .then((response) => setIsAuthenticated(Boolean(response.data))) + .then((me) => setIsAuthenticated(Boolean(me))) .catch(() => setHasError(true)) .finally(() => setIsLoading(false)); }, []); @@ -60,7 +60,7 @@ export function ReplyDialog({ children, shoutId }: ReplyDialogProps) { const formData = new FormData(); formData.append("image", files[0]); const image = await MediaApi.uploadImage(formData); - imageId = image.data.id; + imageId = image.id; } const newShout = await ShoutApi.createShout({ message, @@ -68,7 +68,7 @@ export function ReplyDialog({ children, shoutId }: ReplyDialogProps) { }); await ShoutApi.createReply({ shoutId, - replyId: newShout.data.id, + replyId: newShout.id, }); setOpen(false); } catch (error) { diff --git a/src/pages/feed/feed.tsx b/src/pages/feed/feed.tsx index d11e664..e6e9fca 100644 --- a/src/pages/feed/feed.tsx +++ b/src/pages/feed/feed.tsx @@ -3,10 +3,14 @@ import { useEffect, useState } from "react"; import FeedApi from "@/api/feed"; import { LoadingView } from "@/components/loading"; import { ShoutList } from "@/components/shout-list"; -import { FeedResponse, Image, User } from "@/types"; +import { Image, Shout, User } from "@/types"; export function Feed() { - const [feed, setFeed] = useState(); + const [feed, setFeed] = useState<{ + shouts: Shout[]; + images: Image[]; + users: User[]; + }>(); const [hasError, setHasError] = useState(false); useEffect(() => { @@ -22,12 +26,9 @@ export function Feed() { if (!feed) { return ; } - - const users = feed.included.filter((u): u is User => u.type === "user"); - const images = feed.included.filter((i): i is Image => i.type === "image"); return (
- +
); } diff --git a/src/pages/user-profile/user-profile.tsx b/src/pages/user-profile/user-profile.tsx index 1823ec9..60c6500 100644 --- a/src/pages/user-profile/user-profile.tsx +++ b/src/pages/user-profile/user-profile.tsx @@ -4,15 +4,16 @@ import { Navigate, useParams } from "react-router"; import UserApi from "@/api/user"; import { LoadingSpinner } from "@/components/loading"; import { ShoutList } from "@/components/shout-list"; -import { UserResponse, UserShoutsResponse } from "@/types"; +import { Image, Shout, User } from "@/types"; import { UserInfo } from "./user-info"; export function UserProfile() { const { handle } = useParams<{ handle: string }>(); - const [user, setUser] = useState(); - const [userShouts, setUserShouts] = useState(); + const [user, setUser] = useState(); + const [shouts, setShouts] = useState(); + const [images, setImages] = useState([]); const [hasError, setHasError] = useState(false); useEffect(() => { @@ -21,11 +22,14 @@ export function UserProfile() { } UserApi.getUser(handle) - .then((response) => setUser(response)) + .then((user) => setUser(user)) .catch(() => setHasError(true)); UserApi.getUserShouts(handle) - .then((response) => setUserShouts(response)) + .then(({ shouts, images }) => { + setShouts(shouts); + setImages(images); + }) .catch(() => setHasError(true)); }, [handle]); @@ -36,18 +40,14 @@ export function UserProfile() { if (hasError) { return
An error occurred
; } - if (!user || !userShouts) { + if (!user || !shouts) { return ; } return (
- - + +
); } From a960a40746d5ac2eee4b240c9e259aa8e8195cee Mon Sep 17 00:00:00 2001 From: Johannes Kettmann Date: Fri, 5 Apr 2024 08:40:35 +0200 Subject: [PATCH 2/2] Step 3: Transform input data in API layer --- src/api/media.ts | 5 ++++- src/components/shout/reply-dialog.tsx | 13 +++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/api/media.ts b/src/api/media.ts index 6c506da..e796bc1 100644 --- a/src/api/media.ts +++ b/src/api/media.ts @@ -2,7 +2,10 @@ import { Image } from "@/types"; import { apiClient } from "./client"; -async function uploadImage(formData: FormData) { +async function uploadImage(file: File) { + const formData = new FormData(); + formData.append("image", file); + const response = await apiClient.post<{ data: Image }>("/image", formData); const image = response.data.data; return image; diff --git a/src/components/shout/reply-dialog.tsx b/src/components/shout/reply-dialog.tsx index cc597ae..70e3f35 100644 --- a/src/components/shout/reply-dialog.tsx +++ b/src/components/shout/reply-dialog.tsx @@ -55,21 +55,22 @@ export function ReplyDialog({ children, shoutId }: ReplyDialogProps) { try { const message = event.currentTarget.elements.message.value; const files = event.currentTarget.elements.image.files; - let imageId = undefined; + + let image; if (files?.length) { - const formData = new FormData(); - formData.append("image", files[0]); - const image = await MediaApi.uploadImage(formData); - imageId = image.id; + image = await MediaApi.uploadImage(files[0]); } + const newShout = await ShoutApi.createShout({ message, - imageId, + imageId: image?.id, }); + await ShoutApi.createReply({ shoutId, replyId: newShout.id, }); + setOpen(false); } catch (error) { console.error(error);