-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Teollan
committed
Aug 31, 2024
1 parent
aeee258
commit 58a2624
Showing
37 changed files
with
870 additions
and
357 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
import React, { PropsWithChildren, useContext, useState } from 'react'; | ||
|
||
import { Comment, CommentData, Post, User } from '../../types'; | ||
import { FutureValue, useFuture } from '../../components/Future'; | ||
|
||
import { postsService } from '../../services/PostsService'; | ||
import { commentsService } from '../../services/CommentsService'; | ||
import { getUsers } from '../../utils/users'; | ||
import { NoUserSelectedError } from '../../errors/NoUserSelectedError'; | ||
|
||
type Context = { | ||
users: FutureValue<User[]>; | ||
selectedUser: User | null; | ||
|
||
selectUser: (user: User) => void; | ||
|
||
posts: FutureValue<Post[]>; | ||
selectedPost: Post | null; | ||
|
||
selectPost: (post: Post) => void; | ||
clearPostSelection: () => void; | ||
|
||
comments: FutureValue<Comment[]>; | ||
|
||
leaveComment: (comment: CommentData) => Promise<void>; | ||
deleteComment: (comment: Comment) => Promise<void>; | ||
}; | ||
|
||
const AppContext = React.createContext<Context | null>(null); | ||
|
||
export const AppContextProvider = ({ children }: PropsWithChildren) => { | ||
const users = useFuture<User[]>(getUsers, []); | ||
const [selectedUser, setSelectedUser] = useState<User | null>(null); | ||
|
||
const posts = useFuture(() => { | ||
if (!selectedUser) { | ||
return Promise.reject(new NoUserSelectedError()); | ||
} | ||
|
||
return postsService.getUserPosts(selectedUser as User); | ||
}, [selectedUser]); | ||
const [selectedPost, setSelectedPost] = useState<Post | null>(null); | ||
|
||
const comments = useFuture(() => { | ||
if (!selectedPost) { | ||
return Promise.reject(); | ||
} | ||
|
||
return commentsService.getPostComments(selectedPost as Post); | ||
}, [selectedPost]); | ||
|
||
function selectUser(user: User) { | ||
setSelectedUser(user); | ||
setSelectedPost(null); | ||
} | ||
|
||
function selectPost(post: Post) { | ||
setSelectedPost(post); | ||
} | ||
|
||
function clearPostSelection() { | ||
setSelectedPost(null); | ||
} | ||
|
||
async function leaveComment(comment: CommentData) { | ||
const newComment = await commentsService.leaveComment( | ||
selectedPost as Post, | ||
comment, | ||
); | ||
|
||
comments.setImmediately([...comments.value, newComment]); | ||
} | ||
|
||
async function deleteComment(target: Comment) { | ||
comments.setImmediately( | ||
comments.value.filter(comment => comment.id !== target.id), | ||
); | ||
|
||
try { | ||
await commentsService.deleteComment(target); | ||
} catch { | ||
comments.setImmediately([...comments.value]); | ||
} | ||
} | ||
|
||
const context: Context = { | ||
users, | ||
selectedUser, | ||
|
||
selectUser, | ||
|
||
posts, | ||
selectedPost, | ||
|
||
selectPost, | ||
clearPostSelection, | ||
|
||
comments, | ||
|
||
leaveComment, | ||
deleteComment, | ||
}; | ||
|
||
return <AppContext.Provider value={context}>{children}</AppContext.Provider>; | ||
}; | ||
|
||
export const useAppContext = () => useContext(AppContext) as Context; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { useAppContext } from '../../BLoC/App/AppContext'; | ||
import { Comment } from '../../types'; | ||
|
||
type Props = { | ||
comment: Comment; | ||
}; | ||
|
||
export const CommentCard = ({ comment }: Props) => { | ||
const { deleteComment } = useAppContext(); | ||
|
||
return ( | ||
<article className="message is-small" data-cy="Comment"> | ||
<div className="message-header"> | ||
<a href={`mailto:${comment.email}`} data-cy="CommentAuthor"> | ||
{comment.name} | ||
</a> | ||
<button | ||
onClick={() => deleteComment(comment)} | ||
data-cy="CommentDelete" | ||
type="button" | ||
className="delete is-small" | ||
aria-label="delete" | ||
> | ||
delete button | ||
</button> | ||
</div> | ||
|
||
<div className="message-body" data-cy="CommentBody"> | ||
{comment.body} | ||
</div> | ||
</article> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './CommentCard'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { ReactNode } from 'react'; | ||
import { FutureValue, FutureState } from './useFuture'; | ||
|
||
type Props<T> = { | ||
future: FutureValue<T>; | ||
whilePending?: () => ReactNode; | ||
whileError?: (error: unknown) => ReactNode; | ||
whileReady?: (value: T) => ReactNode; | ||
}; | ||
|
||
export function Future<T>({ | ||
future, | ||
whilePending, | ||
whileError, | ||
whileReady, | ||
}: Props<T>) { | ||
return ( | ||
<> | ||
{(() => { | ||
switch (future.state) { | ||
case FutureState.Pending: | ||
return whilePending && whilePending(); | ||
case FutureState.Error: | ||
return whileError && whileError(future.error); | ||
case FutureState.Ready: | ||
return whileReady && whileReady(future.value); | ||
} | ||
})()} | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './Future'; | ||
export * from './useFuture'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
/* eslint-disable react-hooks/exhaustive-deps */ | ||
import { useEffect, useState } from 'react'; | ||
|
||
export enum FutureState { | ||
Pending, | ||
Ready, | ||
Error, | ||
} | ||
|
||
export type FutureValue<T> = { | ||
value: T; | ||
setImmediately: (value: T) => void; | ||
state: FutureState; | ||
error?: unknown; | ||
}; | ||
|
||
export function useFuture<T>( | ||
promise: () => Promise<T>, | ||
deps: React.DependencyList, | ||
): FutureValue<T> { | ||
const [value, setValue] = useState<T | undefined>(undefined); | ||
const [state, setState] = useState(FutureState.Pending); | ||
const [error, setError] = useState<unknown>(undefined); | ||
|
||
useEffect(() => { | ||
async function update() { | ||
setState(FutureState.Pending); | ||
|
||
try { | ||
setValue(await promise()); | ||
setState(FutureState.Ready); | ||
} catch (e) { | ||
setError(e); | ||
setState(FutureState.Error); | ||
} | ||
} | ||
|
||
update(); | ||
}, deps); | ||
|
||
return { | ||
get value() { | ||
return value as T; | ||
}, | ||
setImmediately: (newValue: T) => { | ||
setValue(newValue); | ||
setState(FutureState.Ready); | ||
}, | ||
state, | ||
error, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.