mirror of
https://github.com/usatiuk/y.git
synced 2025-10-29 02:37:49 +01:00
editing messages
This commit is contained in:
@@ -6,6 +6,7 @@ import "./Chat.scss";
|
|||||||
import "./PostForm.scss";
|
import "./PostForm.scss";
|
||||||
import { Message } from "./Message";
|
import { Message } from "./Message";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
import { getTokenUserUuid } from "./api/utils";
|
||||||
|
|
||||||
export function Chat() {
|
export function Chat() {
|
||||||
const loaderData = useLoaderData() as LoaderToType<typeof chatLoader>;
|
const loaderData = useLoaderData() as LoaderToType<typeof chatLoader>;
|
||||||
@@ -51,7 +52,12 @@ export function Chat() {
|
|||||||
</fetcher.Form>
|
</fetcher.Form>
|
||||||
<div className={"messages"}>
|
<div className={"messages"}>
|
||||||
{sortedMessages.map((m) => (
|
{sortedMessages.map((m) => (
|
||||||
<Message key={m.id} message={m} chat={chat} />
|
<Message
|
||||||
|
key={m.id}
|
||||||
|
message={m}
|
||||||
|
chat={chat}
|
||||||
|
actions={m.authorUuid == getTokenUserUuid()}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
&.messageEditing {
|
&.messageEditing {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
min-height: 6rem;
|
min-height: 3rem;
|
||||||
border: 1px solid #D0D0D0;
|
border: 1px solid #D0D0D0;
|
||||||
|
|
||||||
@include post-editor;
|
@include post-editor;
|
||||||
|
|||||||
@@ -1,14 +1,53 @@
|
|||||||
import "./Message.scss";
|
import "./Message.scss";
|
||||||
import { TChatTo, TMessageTo } from "./api/dto";
|
import { TChatTo, TMessageTo } from "./api/dto";
|
||||||
import { Link } from "react-router-dom";
|
import { Form, Link, useNavigation, useSubmit } from "react-router-dom";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
export function Message({
|
export function Message({
|
||||||
message,
|
message,
|
||||||
chat,
|
chat,
|
||||||
|
actions,
|
||||||
}: {
|
}: {
|
||||||
message: TMessageTo;
|
message: TMessageTo;
|
||||||
chat: TChatTo;
|
chat: TChatTo;
|
||||||
|
actions: boolean;
|
||||||
}) {
|
}) {
|
||||||
|
const [editing, setEditing] = useState(false);
|
||||||
|
const submit = useSubmit();
|
||||||
|
const navigation = useNavigation();
|
||||||
|
const busy = navigation.state === "submitting";
|
||||||
|
|
||||||
|
if (editing) {
|
||||||
|
return (
|
||||||
|
<div className={"message messageEditing"}>
|
||||||
|
<Form className={"postForm"} method="patch">
|
||||||
|
<input
|
||||||
|
hidden={true}
|
||||||
|
name={"messageId"}
|
||||||
|
value={message.id}
|
||||||
|
/>
|
||||||
|
<textarea
|
||||||
|
placeholder={"Write something!"}
|
||||||
|
name="text"
|
||||||
|
defaultValue={message.contents}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
name="intent"
|
||||||
|
value="updateMessage"
|
||||||
|
type="submit"
|
||||||
|
onClick={(e) => {
|
||||||
|
setEditing(false);
|
||||||
|
submit(e.currentTarget);
|
||||||
|
}}
|
||||||
|
disabled={busy}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={"message"}>
|
<div className={"message"}>
|
||||||
<span className={"text"}>{message.contents}</span>
|
<span className={"text"}>{message.contents}</span>
|
||||||
@@ -24,26 +63,26 @@ export function Message({
|
|||||||
by {message.authorUsername}
|
by {message.authorUsername}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
{/*{actions && (*/}
|
{actions && (
|
||||||
{/* <div className={"actions"}>*/}
|
<div className={"actions"}>
|
||||||
{/* {<button onClick={() => setEditing(true)}>edit</button>}*/}
|
{<button onClick={() => setEditing(true)}>edit</button>}
|
||||||
{/* <Form method={"delete"}>*/}
|
<Form method={"delete"}>
|
||||||
{/* <input*/}
|
<input
|
||||||
{/* hidden={true}*/}
|
hidden={true}
|
||||||
{/* name={"postToDeleteId"}*/}
|
name={"messageToDeleteId"}
|
||||||
{/* value={id}*/}
|
value={message.id}
|
||||||
{/* />*/}
|
/>
|
||||||
{/* <button*/}
|
<button
|
||||||
{/* name="intent"*/}
|
name="intent"
|
||||||
{/* value="deletePost"*/}
|
value="deleteMessage"
|
||||||
{/* type={"submit"}*/}
|
type={"submit"}
|
||||||
{/* disabled={busy}*/}
|
disabled={busy}
|
||||||
{/* >*/}
|
>
|
||||||
{/* delete*/}
|
delete
|
||||||
{/* </button>*/}
|
</button>
|
||||||
{/* </Form>*/}
|
</Form>
|
||||||
{/* </div>*/}
|
</div>
|
||||||
{/*)}*/}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { isError } from "./api/dto";
|
|||||||
import { deleteToken, getTokenUserUuid, setToken } from "./api/utils";
|
import { deleteToken, getTokenUserUuid, setToken } from "./api/utils";
|
||||||
import { createPost, deletePost, updatePost } from "./api/Post";
|
import { createPost, deletePost, updatePost } from "./api/Post";
|
||||||
import { createChat } from "./api/Chat";
|
import { createChat } from "./api/Chat";
|
||||||
import { addMessagesToChat } from "./api/Message";
|
import { addMessagesToChat, deleteMessage, editMessage } from "./api/Message";
|
||||||
|
|
||||||
export type ActionToType<T extends (...args: any) => any> =
|
export type ActionToType<T extends (...args: any) => any> =
|
||||||
| Exclude<Awaited<ReturnType<T>>, Response>
|
| Exclude<Awaited<ReturnType<T>>, Response>
|
||||||
@@ -117,8 +117,20 @@ export async function newChatAction({ request }: ActionFunctionArgs) {
|
|||||||
|
|
||||||
export async function chatAction({ request }: ActionFunctionArgs) {
|
export async function chatAction({ request }: ActionFunctionArgs) {
|
||||||
const formData = await request.formData();
|
const formData = await request.formData();
|
||||||
return await addMessagesToChat(
|
const intent = formData.get("intent")!.toString();
|
||||||
Number(formData.get("chatId")!.toString()),
|
if (intent == "addMessage") {
|
||||||
formData.get("text")!.toString(),
|
return await addMessagesToChat(
|
||||||
);
|
Number(formData.get("chatId")!.toString()),
|
||||||
|
formData.get("text")!.toString(),
|
||||||
|
);
|
||||||
|
} else if (intent == "deleteMessage") {
|
||||||
|
return await deleteMessage(
|
||||||
|
Number(formData.get("messageToDeleteId")!.toString()),
|
||||||
|
);
|
||||||
|
} else if (intent == "updateMessage") {
|
||||||
|
return await editMessage(
|
||||||
|
Number(formData.get("messageId")!.toString()),
|
||||||
|
formData.get("text")!.toString(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import {
|
import {
|
||||||
MessagesToResp,
|
MessagesToResp,
|
||||||
MessageToResp,
|
MessageToResp,
|
||||||
|
NoContentToResp,
|
||||||
TMessagesToResp,
|
TMessagesToResp,
|
||||||
TMessageToResp,
|
TMessageToResp,
|
||||||
|
TNoContentToResp,
|
||||||
} from "./dto";
|
} from "./dto";
|
||||||
import { fetchJSONAuth } from "./utils";
|
import { fetchJSONAuth } from "./utils";
|
||||||
|
|
||||||
@@ -20,3 +22,27 @@ export async function addMessagesToChat(
|
|||||||
contents: messageContents,
|
contents: messageContents,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function editMessage(
|
||||||
|
messageId: number,
|
||||||
|
messageContents: string,
|
||||||
|
): Promise<TMessageToResp> {
|
||||||
|
return fetchJSONAuth(
|
||||||
|
"/message/by-id/" + messageId,
|
||||||
|
"PATCH",
|
||||||
|
MessageToResp,
|
||||||
|
{
|
||||||
|
contents: messageContents,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteMessage(
|
||||||
|
messageId: number,
|
||||||
|
): Promise<TNoContentToResp> {
|
||||||
|
return fetchJSONAuth(
|
||||||
|
"/message/by-id/" + messageId,
|
||||||
|
"DELETE",
|
||||||
|
NoContentToResp,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.*;
|
|||||||
import org.springframework.web.server.ResponseStatusException;
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@@ -52,4 +53,26 @@ public class MessageController {
|
|||||||
messageService.create(message);
|
messageService.create(message);
|
||||||
return messageMapper.makeDto(message);
|
return messageMapper.makeDto(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PatchMapping(path = "/by-id/{id}")
|
||||||
|
public MessageTo update(Principal principal, @PathVariable long id, @RequestBody MessageCreateTo messageCreateTo) {
|
||||||
|
var message = messageService.readById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
||||||
|
if (!Objects.equals(message.getAuthor().getUuid(), principal.getName()))
|
||||||
|
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
|
||||||
|
message.setContents(messageCreateTo.contents());
|
||||||
|
messageService.update(message);
|
||||||
|
return messageMapper.makeDto(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping(path = "/by-id/{id}")
|
||||||
|
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||||
|
public void delete(Principal principal, @PathVariable long id) {
|
||||||
|
var read = messageService.readById(id);
|
||||||
|
if (read.isEmpty()) return;
|
||||||
|
if (!Objects.equals(read.get().getAuthor().getId(), principal.getName())) {
|
||||||
|
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
|
||||||
|
}
|
||||||
|
messageService.deleteById(id);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user