update posts

This commit is contained in:
Stepan Usatiuk
2023-12-17 15:04:19 +01:00
parent 8940b3d6c6
commit 18ac8ded71
10 changed files with 109 additions and 31 deletions

View File

@@ -6,6 +6,17 @@
padding: 7px;
resize: none;
margin: 1rem 0;
transition: height 0.5s;
&.postEditing {
padding: 0;
&:focus-within {
border-color: mediumpurple;
outline: solid mediumpurple 2px;
}
height: 6rem;
transition: height 0.5s;
}
.text {
word-wrap: anywhere;
@@ -39,8 +50,18 @@
}
.actions {
display: flex;
align-self: end;
> * {
margin-left: 0.5rem;
}
> *:first-child {
margin-left: 0;
}
button {
background: none;
border: none;

View File

@@ -1,5 +1,8 @@
import "./Post.scss";
import { Form, Link } from "react-router-dom";
import { Form, Link, useSubmit } from "react-router-dom";
import { useState } from "react";
import "./PostForm.scss";
export function Post({
text,
@@ -14,6 +17,35 @@ export function Post({
actions: boolean;
id: number;
}) {
const [editing, setEditing] = useState(false);
const submit = useSubmit();
if (editing) {
return (
<div className={"post postEditing"}>
<Form className={"postForm"} method="patch">
<input hidden={true} name={"postId"} value={id} />
<textarea
placeholder={"Write something!"}
name="text"
defaultValue={text}
/>
<button
name="intent"
value="updatePost"
type="submit"
onClick={(e) => {
setEditing(false);
submit(e.currentTarget);
}}
>
save
</button>
</Form>
</div>
);
}
return (
<div className={"post"}>
<span className={"text"}>{text}</span>
@@ -29,6 +61,7 @@ export function Post({
</div>
{actions && (
<div className={"actions"}>
{<button onClick={() => setEditing(true)}>edit</button>}
<Form method={"delete"}>
<input
hidden={true}

24
client/src/PostForm.scss Normal file
View File

@@ -0,0 +1,24 @@
.postForm {
min-height: 100%;
display: flex;
flex-direction: row;
flex: auto;
textarea {
flex-grow: 1;
border: 0px;
border-radius: 7px 0 0 7px;
padding: 7px;
resize: none;
&:focus {
outline: none;
}
}
button {
border: 0px;
border-radius: 0 7px 7px 0;
}
}

View File

@@ -37,30 +37,6 @@
outline: solid mediumpurple 2px;
}
form {
min-height: 100%;
display: flex;
flex-direction: row;
flex: auto;
textarea {
flex-grow: 1;
border: 0px;
border-radius: 7px 0 0 7px;
padding: 7px;
resize: none;
&:focus {
outline: none;
}
}
button {
border: 0px;
border-radius: 0 7px 7px 0;
}
}
}
.posts {

View File

@@ -1,4 +1,3 @@
import "./Profile.scss";
import { Form, Link, useLoaderData } from "react-router-dom";
import { LoaderToType, profileLoader } from "./loaders";
import { isError } from "./api/dto";
@@ -6,6 +5,9 @@ import { Post } from "./Post";
import { useHomeContext } from "./HomeContext";
import { PostList } from "./PostList";
import "./PostForm.scss";
import "./Profile.scss";
export interface IProfileProps {
self: boolean;
}
@@ -35,7 +37,7 @@ export function Profile({ self }: IProfileProps) {
</div>
{self && (
<div className={"newPost"}>
<Form method="post">
<Form className={"postForm"} method="post">
<textarea
placeholder={"Write something!"}
name="text"

View File

@@ -3,7 +3,7 @@ import { ActionFunctionArgs, redirect } from "react-router-dom";
import { login } from "./api/Token";
import { isError } from "./api/dto";
import { deleteToken, setToken } from "./api/utils";
import { createPost, deletePost } from "./api/Post";
import { createPost, deletePost, updatePost } from "./api/Post";
export type ActionToType<T extends (...args: any) => any> =
| Exclude<Awaited<ReturnType<T>>, Response>
@@ -59,6 +59,11 @@ export async function profileSelfAction({ request }: ActionFunctionArgs) {
return await deletePost(
parseInt(formData.get("postToDeleteId")!.toString()),
);
} else if (intent == "updatePost") {
return await updatePost(
formData.get("text")!.toString(),
parseInt(formData.get("postId")!.toString()),
);
}
}

View File

@@ -14,6 +14,14 @@ export async function createPost(text: string): Promise<TPostToResp> {
});
}
export async function updatePost(
text: string,
postId: number,
): Promise<TPostToResp> {
return fetchJSONAuth("/post/" + postId, "PATCH", PostToResp, {
text,
});
}
export async function deletePost(id: number): Promise<TNoContentToResp> {
return fetchJSONAuth(`/post/${id.toString()}`, "DELETE", NoContentToResp);
}

View File

@@ -64,6 +64,15 @@ public class PostController {
return PostMapper.makeDto(post.get());
}
@PatchMapping(path = "/{id}")
public PostTo update(Principal principal, @PathVariable long id, @RequestBody PostCreateTo postCreateTo) {
var post = postService.readById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
if (!Objects.equals(post.getAuthor().getUuid(), principal.getName()))
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
post.setText(postCreateTo.text());
postService.update(post);
return PostMapper.makeDto(post);
}
@DeleteMapping(path = "/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)

View File

@@ -12,7 +12,7 @@ public interface CrudService<T extends EntityWithId<ID>, ID extends Serializable
Iterable<T> readAll();
void update(ID id, T e);
void update(T e);
void deleteById(ID id);
}

View File

@@ -27,8 +27,8 @@ public abstract class CrudServiceImpl<T extends EntityWithId<ID>, ID extends Ser
}
@Override
public void update(ID id, T e) {
public void update(T e) {
getRepository().save(e);
}
@Override