diff --git a/src/api/auth.ts b/src/api/auth.ts new file mode 100644 index 0000000..4f1b338 --- /dev/null +++ b/src/api/auth.ts @@ -0,0 +1,13 @@ +import { Credentials } from "@/types"; + +import { apiClient } from "./client"; + +async function login(credentials: Credentials) { + await apiClient.post("/login", credentials); +} + +async function logout() { + await apiClient.post("/logout"); +} + +export default { login, logout }; diff --git a/src/api/feed.ts b/src/api/feed.ts new file mode 100644 index 0000000..d4ef136 --- /dev/null +++ b/src/api/feed.ts @@ -0,0 +1,10 @@ +import { FeedResponse } from "@/types"; + +import { apiClient } from "./client"; + +async function getFeed() { + const response = await apiClient.get("/feed"); + return response.data; +} + +export default { getFeed }; diff --git a/src/api/media.ts b/src/api/media.ts new file mode 100644 index 0000000..bf4a46c --- /dev/null +++ b/src/api/media.ts @@ -0,0 +1,10 @@ +import { Image } from "@/types"; + +import { apiClient } from "./client"; + +async function uploadImage(formData: FormData) { + const response = await apiClient.post<{ data: Image }>("/image", formData); + return response.data; +} + +export default { uploadImage }; diff --git a/src/api/shout.ts b/src/api/shout.ts new file mode 100644 index 0000000..680cf6d --- /dev/null +++ b/src/api/shout.ts @@ -0,0 +1,18 @@ +import { CreateShoutInput, CreateShoutReplyInput, Shout } from "@/types"; + +import { apiClient } from "./client"; + +async function createShout(input: CreateShoutInput) { + const response = await apiClient.post<{ data: Shout }>(`/shout`, input); + return response.data; +} + +async function createReply({ shoutId, replyId }: CreateShoutReplyInput) { + const response = await apiClient.post<{ data: Shout }>( + `/shout/${shoutId}/reply`, + { replyId } + ); + return response.data; +} + +export default { createShout, createReply }; diff --git a/src/api/user.ts b/src/api/user.ts new file mode 100644 index 0000000..5cc35a3 --- /dev/null +++ b/src/api/user.ts @@ -0,0 +1,22 @@ +import { Me, User, UserShoutsResponse } from "@/types"; + +import { apiClient } from "./client"; + +async function getMe() { + const response = await apiClient.get<{ data: Me }>("/me"); + return response.data; +} + +async function getUser(handle: string) { + const response = await apiClient.get<{ data: User }>(`/user/${handle}`); + return response.data; +} + +async function getUserShouts(handle: string) { + const response = await apiClient.get( + `/user/${handle}/shouts` + ); + return response.data; +} + +export default { getMe, getUser, getUserShouts }; diff --git a/src/components/header/header.tsx b/src/components/header/header.tsx index 23f1f2a..b503ad1 100644 --- a/src/components/header/header.tsx +++ b/src/components/header/header.tsx @@ -1,7 +1,8 @@ import { useEffect, useState } from "react"; import { Link } from "react-router-dom"; -import { apiClient } from "@/api/client"; +import AuthApi from "@/api/auth"; +import UserApi from "@/api/user"; import { LoginDialog } from "@/components/login-dialog"; import { Button } from "@/components/ui/button"; import { Me } from "@/types"; @@ -13,16 +14,15 @@ export function Header() { const [hasError, setHasError] = useState(false); useEffect(() => { - apiClient - .get<{ data: Me }>("/me") - .then((response) => setMe(response.data.data)) + UserApi.getMe() + .then((response) => setMe(response.data)) .catch(() => setHasError(true)) .finally(() => setIsLoadingMe(false)); }, []); async function logout() { setIsLoadingLogout(true); - await apiClient.post("/logout"); + await AuthApi.logout(); setIsLoadingLogout(false); window.location.reload(); } diff --git a/src/components/login-dialog/login-dialog.tsx b/src/components/login-dialog/login-dialog.tsx index 09184a2..57054a4 100644 --- a/src/components/login-dialog/login-dialog.tsx +++ b/src/components/login-dialog/login-dialog.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; -import { apiClient } from "@/api/client"; +import AuthApi from "@/api/auth"; import { Button } from "@/components/ui/button"; import { Dialog, @@ -37,7 +37,7 @@ export function LoginDialog({ children }: LoginDialogProps) { const username = event.currentTarget.elements.username.value; const password = event.currentTarget.elements.password.value; - await apiClient.post("/login", { username, password }); + await AuthApi.login({ username, password }); setIsLoading(false); setOpen(false); diff --git a/src/components/shout/reply-dialog.tsx b/src/components/shout/reply-dialog.tsx index 1fe8cb3..cea5935 100644 --- a/src/components/shout/reply-dialog.tsx +++ b/src/components/shout/reply-dialog.tsx @@ -1,6 +1,8 @@ import { useEffect, useState } from "react"; -import { apiClient } from "@/api/client"; +import MediaApi from "@/api/media"; +import ShoutApi from "@/api/shout"; +import UserApi from "@/api/user"; import { LoginDialog } from "@/components/login-dialog"; import { Button } from "@/components/ui/button"; import { @@ -15,7 +17,6 @@ import { import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; -import { Image, Me, Shout } from "@/types"; interface ReplyFormElements extends HTMLFormControlsCollection { message: HTMLTextAreaElement; @@ -38,9 +39,8 @@ export function ReplyDialog({ children, shoutId }: ReplyDialogProps) { const [hasError, setHasError] = useState(false); useEffect(() => { - apiClient - .get<{ data: Me }>("/me") - .then((response) => setIsAuthenticated(Boolean(response.data.data))) + UserApi.getMe() + .then((response) => setIsAuthenticated(Boolean(response.data))) .catch(() => setHasError(true)) .finally(() => setIsLoading(false)); }, []); @@ -59,18 +59,16 @@ export function ReplyDialog({ children, shoutId }: ReplyDialogProps) { if (files?.length) { const formData = new FormData(); formData.append("image", files[0]); - const imageResponse = await apiClient.post<{ data: Image }>( - "/image", - formData - ); - imageId = imageResponse.data.data.id; + const image = await MediaApi.uploadImage(formData); + imageId = image.data.id; } - const newShoutResponse = await apiClient.post<{ data: Shout }>(`/shout`, { + const newShout = await ShoutApi.createShout({ message, imageId, }); - await apiClient.post(`/shout/${shoutId}/reply`, { - replyId: newShoutResponse.data.data.id, + await ShoutApi.createReply({ + shoutId, + replyId: newShout.data.id, }); setOpen(false); } catch (error) { diff --git a/src/pages/feed/feed.tsx b/src/pages/feed/feed.tsx index c3194f8..d11e664 100644 --- a/src/pages/feed/feed.tsx +++ b/src/pages/feed/feed.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from "react"; -import { apiClient } from "@/api/client"; +import FeedApi from "@/api/feed"; import { LoadingView } from "@/components/loading"; import { ShoutList } from "@/components/shout-list"; import { FeedResponse, Image, User } from "@/types"; @@ -10,9 +10,8 @@ export function Feed() { const [hasError, setHasError] = useState(false); useEffect(() => { - apiClient - .get("/feed") - .then((response) => setFeed(response.data)) + FeedApi.getFeed() + .then((feed) => setFeed(feed)) .catch(() => setHasError(true)); }, []); diff --git a/src/pages/user-profile/user-profile.tsx b/src/pages/user-profile/user-profile.tsx index 0cb5ad3..1823ec9 100644 --- a/src/pages/user-profile/user-profile.tsx +++ b/src/pages/user-profile/user-profile.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from "react"; import { Navigate, useParams } from "react-router"; -import { apiClient } from "@/api/client"; +import UserApi from "@/api/user"; import { LoadingSpinner } from "@/components/loading"; import { ShoutList } from "@/components/shout-list"; import { UserResponse, UserShoutsResponse } from "@/types"; @@ -16,14 +16,16 @@ export function UserProfile() { const [hasError, setHasError] = useState(false); useEffect(() => { - apiClient - .get(`/user/${handle}`) - .then((response) => setUser(response.data)) + if (!handle) { + return; + } + + UserApi.getUser(handle) + .then((response) => setUser(response)) .catch(() => setHasError(true)); - apiClient - .get(`/user/${handle}/shouts`) - .then((response) => setUserShouts(response.data)) + UserApi.getUserShouts(handle) + .then((response) => setUserShouts(response)) .catch(() => setHasError(true)); }, [handle]);