mirror of
https://github.com/usatiuk/y.git
synced 2025-10-29 02:37:49 +01:00
basic feed
This commit is contained in:
@@ -16,7 +16,12 @@ import {
|
|||||||
signupAction,
|
signupAction,
|
||||||
userListAction,
|
userListAction,
|
||||||
} from "./actions";
|
} from "./actions";
|
||||||
import { homeLoader, profileLoader, userListLoader } from "./loaders";
|
import {
|
||||||
|
feedLoader,
|
||||||
|
homeLoader,
|
||||||
|
profileLoader,
|
||||||
|
userListLoader,
|
||||||
|
} from "./loaders";
|
||||||
import { isError } from "./api/dto";
|
import { isError } from "./api/dto";
|
||||||
import { Feed } from "./Feed";
|
import { Feed } from "./Feed";
|
||||||
import { Messages } from "./Messages";
|
import { Messages } from "./Messages";
|
||||||
@@ -42,7 +47,7 @@ const router = createBrowserRouter([
|
|||||||
},
|
},
|
||||||
element: <Home />,
|
element: <Home />,
|
||||||
children: [
|
children: [
|
||||||
{ path: "feed", element: <Feed /> },
|
{ path: "feed", element: <Feed />, loader: feedLoader },
|
||||||
{ path: "messages", element: <Messages /> },
|
{ path: "messages", element: <Messages /> },
|
||||||
{
|
{
|
||||||
path: "users",
|
path: "users",
|
||||||
|
|||||||
9
client/src/Feed.scss
Normal file
9
client/src/Feed.scss
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
.feedView {
|
||||||
|
min-width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.posts {
|
||||||
|
padding: 2.5rem 2rem 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,21 @@
|
|||||||
|
import { useLoaderData } from "react-router-dom";
|
||||||
|
import { feedLoader, LoaderToType } from "./loaders";
|
||||||
|
import { useHomeContext } from "./HomeContext";
|
||||||
|
import { PostList } from "./PostList";
|
||||||
|
import { isError } from "./api/dto";
|
||||||
|
|
||||||
|
import "./Feed.scss";
|
||||||
|
|
||||||
export function Feed() {
|
export function Feed() {
|
||||||
return <a>feed</a>;
|
const loaderData = useLoaderData() as LoaderToType<typeof feedLoader>;
|
||||||
|
if (!loaderData || isError(loaderData)) {
|
||||||
|
return <div>error</div>;
|
||||||
|
}
|
||||||
|
const homeContext = useHomeContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={"feedView"}>
|
||||||
|
<PostList posts={loaderData} selfUuid={homeContext.user.uuid} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
29
client/src/PostList.tsx
Normal file
29
client/src/PostList.tsx
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { TPostToArr } from "./api/dto";
|
||||||
|
import { Post } from "./Post";
|
||||||
|
|
||||||
|
export function PostList({
|
||||||
|
posts,
|
||||||
|
selfUuid,
|
||||||
|
}: {
|
||||||
|
posts: TPostToArr | null;
|
||||||
|
selfUuid: string;
|
||||||
|
}) {
|
||||||
|
const sortedPosts = posts?.sort((a, b) => b.createdAt - a.createdAt);
|
||||||
|
return (
|
||||||
|
<div className={"posts"}>
|
||||||
|
{sortedPosts &&
|
||||||
|
sortedPosts.map((p) => {
|
||||||
|
const date = new Date(p.createdAt * 1000);
|
||||||
|
return (
|
||||||
|
<Post
|
||||||
|
text={p.text}
|
||||||
|
createdDate={`${date.toUTCString()}`}
|
||||||
|
key={p.id}
|
||||||
|
id={p.id}
|
||||||
|
actions={selfUuid == p.authorUuid}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import { LoaderToType, profileLoader } from "./loaders";
|
|||||||
import { isError } from "./api/dto";
|
import { isError } from "./api/dto";
|
||||||
import { Post } from "./Post";
|
import { Post } from "./Post";
|
||||||
import { useHomeContext } from "./HomeContext";
|
import { useHomeContext } from "./HomeContext";
|
||||||
|
import { PostList } from "./PostList";
|
||||||
|
|
||||||
export interface IProfileProps {
|
export interface IProfileProps {
|
||||||
self: boolean;
|
self: boolean;
|
||||||
@@ -18,10 +19,6 @@ export function Profile({ self }: IProfileProps) {
|
|||||||
return <div>Error</div>;
|
return <div>Error</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sortedPosts = loaderData.posts?.sort(
|
|
||||||
(a, b) => b.createdAt - a.createdAt,
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={"profileView"}>
|
<div className={"profileView"}>
|
||||||
<div className={"profileInfo"}>
|
<div className={"profileInfo"}>
|
||||||
@@ -41,21 +38,10 @@ export function Profile({ self }: IProfileProps) {
|
|||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className={"posts"}>
|
<PostList
|
||||||
{sortedPosts &&
|
posts={loaderData.posts}
|
||||||
sortedPosts.map((p) => {
|
selfUuid={homeContext.user.uuid}
|
||||||
const date = new Date(p.createdAt * 1000);
|
/>
|
||||||
return (
|
|
||||||
<Post
|
|
||||||
text={p.text}
|
|
||||||
createdDate={`${date.toUTCString()}`}
|
|
||||||
key={p.id}
|
|
||||||
id={p.id}
|
|
||||||
actions={self}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export async function createPost(text: string): Promise<TPostToResp> {
|
|||||||
text,
|
text,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deletePost(id: number): Promise<TNoContentToResp> {
|
export async function deletePost(id: number): Promise<TNoContentToResp> {
|
||||||
return fetchJSONAuth(`/post/${id.toString()}`, "DELETE", NoContentToResp);
|
return fetchJSONAuth(`/post/${id.toString()}`, "DELETE", NoContentToResp);
|
||||||
}
|
}
|
||||||
@@ -27,6 +28,10 @@ export async function getPostsByAuthorUuid(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getPostsByFollowees(): Promise<TPostToArrResp> {
|
||||||
|
return fetchJSONAuth(`/post/by-following`, "GET", PostToArrResp);
|
||||||
|
}
|
||||||
|
|
||||||
export async function getPostsByAuthorUsername(
|
export async function getPostsByAuthorUsername(
|
||||||
author: string,
|
author: string,
|
||||||
): Promise<TPostToArrResp> {
|
): Promise<TPostToArrResp> {
|
||||||
|
|||||||
@@ -7,7 +7,11 @@ import {
|
|||||||
import { deleteToken, getToken, getTokenUserUuid } from "./api/utils";
|
import { deleteToken, getToken, getTokenUserUuid } from "./api/utils";
|
||||||
import { redirect } from "react-router-dom";
|
import { redirect } from "react-router-dom";
|
||||||
import { isError } from "./api/dto";
|
import { isError } from "./api/dto";
|
||||||
import { getPostsByAuthorUsername, getPostsByAuthorUuid } from "./api/Post";
|
import {
|
||||||
|
getPostsByAuthorUsername,
|
||||||
|
getPostsByAuthorUuid,
|
||||||
|
getPostsByFollowees,
|
||||||
|
} from "./api/Post";
|
||||||
|
|
||||||
export type LoaderToType<T extends (...args: any) => any> =
|
export type LoaderToType<T extends (...args: any) => any> =
|
||||||
| Exclude<Awaited<ReturnType<T>>, Response>
|
| Exclude<Awaited<ReturnType<T>>, Response>
|
||||||
@@ -40,7 +44,12 @@ export async function profileLoader({
|
|||||||
}) {
|
}) {
|
||||||
const selfUuid = getTokenUserUuid();
|
const selfUuid = getTokenUserUuid();
|
||||||
if (!selfUuid) return redirect("/");
|
if (!selfUuid) return redirect("/");
|
||||||
if (selfUuid == params.username) {
|
|
||||||
|
const retUser = params.username
|
||||||
|
? await getPersonByUsername(params.username)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (retUser && !isError(retUser) && retUser.uuid == selfUuid) {
|
||||||
return redirect("/home/profile");
|
return redirect("/home/profile");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,10 +57,6 @@ export async function profileLoader({
|
|||||||
? await getPostsByAuthorUsername(params.username)
|
? await getPostsByAuthorUsername(params.username)
|
||||||
: await getPostsByAuthorUuid(selfUuid);
|
: await getPostsByAuthorUuid(selfUuid);
|
||||||
|
|
||||||
const retUser = params.username
|
|
||||||
? await getPersonByUsername(params.username)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(params.username && !retUser) ||
|
(params.username && !retUser) ||
|
||||||
retUser instanceof Response ||
|
retUser instanceof Response ||
|
||||||
@@ -66,3 +71,7 @@ export async function profileLoader({
|
|||||||
|
|
||||||
return { user: retUser, posts };
|
return { user: retUser, posts };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function feedLoader() {
|
||||||
|
return await getPostsByFollowees();
|
||||||
|
}
|
||||||
|
|||||||
@@ -47,12 +47,12 @@ public class PostController {
|
|||||||
@GetMapping(path = "/by-author-username")
|
@GetMapping(path = "/by-author-username")
|
||||||
public Stream<PostTo> readAllByAuthorUsername(@RequestParam Optional<String> author) {
|
public Stream<PostTo> readAllByAuthorUsername(@RequestParam Optional<String> author) {
|
||||||
if (author.isPresent())
|
if (author.isPresent())
|
||||||
return postService.readByAuthorId(author.get()).stream().map(PostMapper::makeDto);
|
return postService.readByAuthorUsername(author.get()).stream().map(PostMapper::makeDto);
|
||||||
else
|
else
|
||||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
|
throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(path = "/following")
|
@GetMapping(path = "/by-following")
|
||||||
public Stream<PostTo> readAllByFollowees(Principal principal) {
|
public Stream<PostTo> readAllByFollowees(Principal principal) {
|
||||||
return postService.readByPersonFollowees(principal.getName()).stream().map(PostMapper::makeDto);
|
return postService.readByPersonFollowees(principal.getName()).stream().map(PostMapper::makeDto);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user