diff --git a/src/api/auth.ts b/src/api/auth/api.ts similarity index 72% rename from src/api/auth.ts rename to src/api/auth/api.ts index 4f1b338..c5e078e 100644 --- a/src/api/auth.ts +++ b/src/api/auth/api.ts @@ -1,6 +1,6 @@ -import { Credentials } from "@/types"; +import { apiClient } from "../client"; -import { apiClient } from "./client"; +import { Credentials } from "./dto"; async function login(credentials: Credentials) { await apiClient.post("/login", credentials); diff --git a/src/api/auth/dto.ts b/src/api/auth/dto.ts new file mode 100644 index 0000000..279aabe --- /dev/null +++ b/src/api/auth/dto.ts @@ -0,0 +1,4 @@ +export interface Credentials { + username: string; + password: string; +} diff --git a/src/api/auth/index.ts b/src/api/auth/index.ts new file mode 100644 index 0000000..9e19686 --- /dev/null +++ b/src/api/auth/index.ts @@ -0,0 +1 @@ +export { default } from "./api"; diff --git a/src/api/feed.ts b/src/api/feed.ts deleted file mode 100644 index 6927733..0000000 --- a/src/api/feed.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { FeedResponse, Image, User } from "@/types"; - -import { apiClient } from "./client"; - -async function getFeed() { - const response = await apiClient.get("/feed"); - 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/feed/api.ts b/src/api/feed/api.ts new file mode 100644 index 0000000..fd95a28 --- /dev/null +++ b/src/api/feed/api.ts @@ -0,0 +1,22 @@ +import { apiClient } from "../client"; +import { ImageDto } from "../media/dto"; +import { dtoToImage } from "../media/transform"; +import { dtoToShout } from "../shout/transform"; +import { UserDto } from "../user/dto"; +import { dtoToUser } from "../user/transform"; + +import { FeedResponse } from "./dto"; + +async function getFeed() { + const response = await apiClient.get("/feed"); + const shouts = response.data.data.map(dtoToShout); + const users = response.data.included + .filter((u): u is UserDto => u.type === "user") + .map(dtoToUser); + const images = response.data.included + .filter((i): i is ImageDto => i.type === "image") + .map(dtoToImage); + return { shouts, users, images }; +} + +export default { getFeed }; diff --git a/src/api/feed/dto.ts b/src/api/feed/dto.ts new file mode 100644 index 0000000..c5174c8 --- /dev/null +++ b/src/api/feed/dto.ts @@ -0,0 +1,8 @@ +import { ImageDto } from "../media/dto"; +import { ShoutDto } from "../shout/dto"; +import { UserDto } from "../user/dto"; + +export interface FeedResponse { + data: ShoutDto[]; + included: (UserDto | ImageDto)[]; +} diff --git a/src/api/feed/index.ts b/src/api/feed/index.ts new file mode 100644 index 0000000..9e19686 --- /dev/null +++ b/src/api/feed/index.ts @@ -0,0 +1 @@ +export { default } from "./api"; diff --git a/src/api/media.ts b/src/api/media.ts deleted file mode 100644 index e796bc1..0000000 --- a/src/api/media.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Image } from "@/types"; - -import { apiClient } from "./client"; - -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; -} - -export default { uploadImage }; diff --git a/src/api/media/api.ts b/src/api/media/api.ts new file mode 100644 index 0000000..9d1e182 --- /dev/null +++ b/src/api/media/api.ts @@ -0,0 +1,15 @@ +import { apiClient } from "../client"; + +import { ImageDto } from "./dto"; +import { dtoToImage } from "./transform"; + +async function uploadImage(file: File) { + const formData = new FormData(); + formData.append("image", file); + + const response = await apiClient.post<{ data: ImageDto }>("/image", formData); + const imageDto = response.data.data; + return dtoToImage(imageDto); +} + +export default { uploadImage }; diff --git a/src/api/media/dto.ts b/src/api/media/dto.ts new file mode 100644 index 0000000..b5b83fa --- /dev/null +++ b/src/api/media/dto.ts @@ -0,0 +1,7 @@ +export interface ImageDto { + id: string; + type: "image"; + attributes: { + url: string; + }; +} diff --git a/src/api/media/index.ts b/src/api/media/index.ts new file mode 100644 index 0000000..9e19686 --- /dev/null +++ b/src/api/media/index.ts @@ -0,0 +1 @@ +export { default } from "./api"; diff --git a/src/api/media/transform.ts b/src/api/media/transform.ts new file mode 100644 index 0000000..444f756 --- /dev/null +++ b/src/api/media/transform.ts @@ -0,0 +1,10 @@ +import { Image } from "@/domain"; + +import { ImageDto } from "./dto"; + +export function dtoToImage(dto: ImageDto): Image { + return { + id: dto.id, + url: dto.attributes.url, + }; +} diff --git a/src/api/shout.ts b/src/api/shout.ts deleted file mode 100644 index 80b729d..0000000 --- a/src/api/shout.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { CreateShoutInput, CreateShoutReplyInput, Shout } from "@/types"; - -import { apiClient } from "./client"; - -async function createShout(input: CreateShoutInput) { - const response = await apiClient.post<{ data: Shout }>(`/shout`, input); - const shout = response.data.data; - return shout; -} - -async function createReply({ shoutId, replyId }: CreateShoutReplyInput) { - const response = await apiClient.post<{ data: Shout }>( - `/shout/${shoutId}/reply`, - { replyId } - ); - const reply = response.data.data; - return reply; -} - -export default { createShout, createReply }; diff --git a/src/api/shout/api.ts b/src/api/shout/api.ts new file mode 100644 index 0000000..d5ce43f --- /dev/null +++ b/src/api/shout/api.ts @@ -0,0 +1,21 @@ +import { apiClient } from "../client"; + +import { CreateShoutInput, CreateShoutReplyInput, ShoutDto } from "./dto"; +import { dtoToShout } from "./transform"; + +async function createShout(input: CreateShoutInput) { + const response = await apiClient.post<{ data: ShoutDto }>(`/shout`, input); + const shoutDto = response.data.data; + return dtoToShout(shoutDto); +} + +async function createReply({ shoutId, replyId }: CreateShoutReplyInput) { + const response = await apiClient.post<{ data: ShoutDto }>( + `/shout/${shoutId}/reply`, + { replyId } + ); + const replyDto = response.data.data; + return dtoToShout(replyDto); +} + +export default { createShout, createReply }; diff --git a/src/api/shout/dto.ts b/src/api/shout/dto.ts new file mode 100644 index 0000000..b448437 --- /dev/null +++ b/src/api/shout/dto.ts @@ -0,0 +1,25 @@ +export interface ShoutDto { + id: string; + type: "shout"; + createdAt: number; + attributes: { + authorId: string; + text: string; + likes: number; + reshouts: number; + imageId?: string; + }; + relationships: { + replies: string[]; + replyTo?: string; + }; +} +export interface CreateShoutInput { + message: string; + imageId?: string; +} + +export interface CreateShoutReplyInput { + shoutId: string; + replyId: string; +} diff --git a/src/api/shout/index.ts b/src/api/shout/index.ts new file mode 100644 index 0000000..9e19686 --- /dev/null +++ b/src/api/shout/index.ts @@ -0,0 +1 @@ +export { default } from "./api"; diff --git a/src/api/shout/transform.ts b/src/api/shout/transform.ts new file mode 100644 index 0000000..4a305d0 --- /dev/null +++ b/src/api/shout/transform.ts @@ -0,0 +1,16 @@ +import { Shout } from "@/domain"; + +import { ShoutDto } from "./dto"; + +export function dtoToShout(shoutDto: ShoutDto): Shout { + return { + id: shoutDto.id, + createdAt: shoutDto.createdAt, + authorId: shoutDto.attributes.authorId, + imageId: shoutDto.attributes.imageId, + likes: shoutDto.attributes.likes, + reshouts: shoutDto.attributes.reshouts, + text: shoutDto.attributes.text, + replies: shoutDto.relationships.replies, + }; +} diff --git a/src/api/user.ts b/src/api/user.ts deleted file mode 100644 index 02a487e..0000000 --- a/src/api/user.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Me, User, UserShoutsResponse } from "@/types"; - -import { apiClient } from "./client"; - -async function getMe() { - const response = await apiClient.get<{ data: Me }>("/me"); - const me = response.data.data; - return me; -} - -async function getUser(handle: string) { - const response = await apiClient.get<{ data: User }>(`/user/${handle}`); - const user = response.data.data; - return user; -} - -async function getUserShouts(handle: string) { - const response = await apiClient.get( - `/user/${handle}/shouts` - ); - const shouts = response.data.data; - const images = response.data.included; - return { shouts, images }; -} - -export default { getMe, getUser, getUserShouts }; diff --git a/src/api/user/api.ts b/src/api/user/api.ts new file mode 100644 index 0000000..0e85dd9 --- /dev/null +++ b/src/api/user/api.ts @@ -0,0 +1,29 @@ +import { apiClient } from "../client"; +import { dtoToImage } from "../media/transform"; +import { dtoToShout } from "../shout/transform"; + +import { MeDto, UserDto, UserShoutsResponse } from "./dto"; +import { dtoToMe, dtoToUser } from "./transform"; + +async function getMe() { + const response = await apiClient.get<{ data: MeDto }>("/me"); + const meDto = response.data.data; + return dtoToMe(meDto); +} + +async function getUser(handle: string) { + const response = await apiClient.get<{ data: UserDto }>(`/user/${handle}`); + const userDto = response.data.data; + return dtoToUser(userDto); +} + +async function getUserShouts(handle: string) { + const response = await apiClient.get( + `/user/${handle}/shouts` + ); + const shouts = response.data.data.map(dtoToShout); + const images = response.data.included.map(dtoToImage); + return { shouts, images }; +} + +export default { getMe, getUser, getUserShouts }; diff --git a/src/api/user/dto.ts b/src/api/user/dto.ts new file mode 100644 index 0000000..48d4f20 --- /dev/null +++ b/src/api/user/dto.ts @@ -0,0 +1,22 @@ +import { ImageDto } from "../media/dto"; +import { ShoutDto } from "../shout/dto"; + +export interface UserDto { + id: string; + type: "user"; + attributes: { + handle: string; + avatar: string; + info?: string; + }; + relationships: { + followerIds: string[]; + }; +} + +export interface MeDto extends UserDto {} + +export interface UserShoutsResponse { + data: ShoutDto[]; + included: ImageDto[]; +} diff --git a/src/api/user/index.ts b/src/api/user/index.ts new file mode 100644 index 0000000..9e19686 --- /dev/null +++ b/src/api/user/index.ts @@ -0,0 +1 @@ +export { default } from "./api"; diff --git a/src/api/user/transform.ts b/src/api/user/transform.ts new file mode 100644 index 0000000..31a8f68 --- /dev/null +++ b/src/api/user/transform.ts @@ -0,0 +1,17 @@ +import { Me, User } from "@/domain"; + +import { MeDto, UserDto } from "./dto"; + +export function dtoToUser(dto: UserDto): User { + return { + id: dto.id, + avatar: dto.attributes.avatar, + handle: dto.attributes.handle, + info: dto.attributes.info, + followerIds: dto.relationships.followerIds, + }; +} + +export function dtoToMe(dto: MeDto): Me { + return dtoToUser(dto); +} diff --git a/src/components/header/header.tsx b/src/components/header/header.tsx index 9a7be17..24b0fe6 100644 --- a/src/components/header/header.tsx +++ b/src/components/header/header.tsx @@ -5,7 +5,7 @@ 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"; +import { Me } from "@/domain"; export function Header() { const [isLoadingMe, setIsLoadingMe] = useState(true); @@ -41,16 +41,9 @@ export function Header() { return (
- - {me.attributes.handle} - {`@${me.attributes.handle}`} + + {me.handle} + {`@${me.handle}`} diff --git a/src/domain/index.ts b/src/domain/index.ts new file mode 100644 index 0000000..f157779 --- /dev/null +++ b/src/domain/index.ts @@ -0,0 +1,26 @@ +export interface User { + id: string; + handle: string; + avatar: string; + info?: string; + followerIds: string[]; +} + +export interface Me extends User {} + +export interface Shout { + id: string; + createdAt: number; + authorId: string; + text: string; + likes: number; + reshouts: number; + imageId?: string; + replies: string[]; + replyTo?: string; +} + +export interface Image { + id: string; + url: string; +} diff --git a/src/pages/feed/feed.tsx b/src/pages/feed/feed.tsx index e6e9fca..1e03fb6 100644 --- a/src/pages/feed/feed.tsx +++ b/src/pages/feed/feed.tsx @@ -3,7 +3,7 @@ import { useEffect, useState } from "react"; import FeedApi from "@/api/feed"; import { LoadingView } from "@/components/loading"; import { ShoutList } from "@/components/shout-list"; -import { Image, Shout, User } from "@/types"; +import { Image, Shout, User } from "@/domain"; export function Feed() { const [feed, setFeed] = useState<{ diff --git a/src/pages/user-profile/user-info.tsx b/src/pages/user-profile/user-info.tsx index 40db567..8a65c23 100644 --- a/src/pages/user-profile/user-info.tsx +++ b/src/pages/user-profile/user-info.tsx @@ -1,4 +1,4 @@ -import { User } from "@/types"; +import { User } from "@/domain"; interface UserInfoProps { user: User; @@ -9,17 +9,17 @@ export function UserInfo({ user }: UserInfoProps) {
{`${user.attributes.handle}'s
-

@{user.attributes.handle}

+

@{user.handle}

- ({user.relationships.followerIds.length} follower) + ({user.followerIds.length} follower)
-

{user.attributes.info || "Shush! I'm a ghost."}

+

{user.info || "Shush! I'm a ghost."}

); diff --git a/src/pages/user-profile/user-profile.tsx b/src/pages/user-profile/user-profile.tsx index 60c6500..17a5192 100644 --- a/src/pages/user-profile/user-profile.tsx +++ b/src/pages/user-profile/user-profile.tsx @@ -4,7 +4,7 @@ import { Navigate, useParams } from "react-router"; import UserApi from "@/api/user"; import { LoadingSpinner } from "@/components/loading"; import { ShoutList } from "@/components/shout-list"; -import { Image, Shout, User } from "@/types"; +import { Image, Shout, User } from "@/domain"; import { UserInfo } from "./user-info"; diff --git a/src/types/index.ts b/src/types/index.ts deleted file mode 100644 index df10f45..0000000 --- a/src/types/index.ts +++ /dev/null @@ -1,71 +0,0 @@ -/* DATA TYPES */ -export interface User { - id: string; - type: "user"; - attributes: { - handle: string; - avatar: string; - info?: string; - }; - relationships: { - followerIds: string[]; - }; -} - -export interface Me extends User {} - -export interface Shout { - id: string; - type: "shout"; - createdAt: number; - attributes: { - authorId: string; - text: string; - likes: number; - reshouts: number; - imageId?: string; - }; - relationships: { - replies: string[]; - replyTo?: string; - }; -} - -export interface Image { - id: string; - type: "image"; - attributes: { - url: string; - }; -} - -/* RESPONSE TYPES */ -export interface FeedResponse { - data: Shout[]; - included: (User | Image)[]; -} - -export interface UserResponse { - data: User; -} - -export interface UserShoutsResponse { - data: Shout[]; - included: Image[]; -} - -/* INPUT TYPES */ -export interface Credentials { - username: string; - password: string; -} - -export interface CreateShoutInput { - message: string; - imageId?: string; -} - -export interface CreateShoutReplyInput { - shoutId: string; - replyId: string; -}