editing chats!

This commit is contained in:
Stepan Usatiuk
2023-12-27 20:04:22 +01:00
parent 2c501557d0
commit 4512b0e17a
8 changed files with 219 additions and 6 deletions

View File

@@ -12,6 +12,7 @@ import { Signup } from "./Signup";
import { Home } from "./Home";
import {
chatAction,
editChatAction,
homeAction,
loginAction,
newChatAction,
@@ -22,6 +23,7 @@ import {
import {
chatListLoader,
chatLoader,
editChatLoader,
feedLoader,
homeLoader,
newChatLoader,
@@ -34,6 +36,7 @@ import { UserList } from "./UserList";
import { Chats } from "./Chats";
import { ChatCreate } from "./ChatCreate";
import { Chat } from "./Chat";
import { ChatEdit } from "./ChatEdit";
const router = createBrowserRouter([
{
@@ -71,6 +74,12 @@ const router = createBrowserRouter([
loader: chatLoader,
action: chatAction,
},
{
path: "messages/chat/:id/edit",
element: <ChatEdit />,
loader: editChatLoader,
action: editChatAction,
},
{
path: "users",
element: <UserList />,

View File

@@ -27,6 +27,10 @@
margin-right: 1rem;
}
.chatHeaderActions {
flex-grow: 0;
}
margin-bottom: 1rem;
padding-bottom: 0.2rem;
}

View File

@@ -1,4 +1,9 @@
import { useFetcher, useLoaderData, useRevalidator } from "react-router-dom";
import {
Link,
useFetcher,
useLoaderData,
useRevalidator,
} from "react-router-dom";
import { chatLoader, LoaderToType } from "./loaders";
import { isError } from "./api/dto";
@@ -37,7 +42,14 @@ export function Chat() {
return (
<div className={"chat"}>
<div className={"chatHeader"}>{chat.name}</div>
<div className={"chatHeader"}>
<span className={"chatHeaderName"}>{chat.name}</span>
{chat.creatorUuid == getTokenUserUuid() && (
<span className={"chatHeaderActions"}>
<Link to={"edit"}>edit</Link>
</span>
)}
</div>
<fetcher.Form className={"messageForm postForm"} method="post">
<textarea placeholder={"Write something!"} name="text" />
<input hidden={true} value={chat.id} name={"chatId"} />

86
client/src/ChatEdit.tsx Normal file
View File

@@ -0,0 +1,86 @@
import {
Form,
useActionData,
useLoaderData,
useNavigation,
} from "react-router-dom";
import { editChatLoader, LoaderToType } from "./loaders";
import { isError } from "./api/dto";
import { ActionToType, editChatAction } from "./actions";
import { getTokenUserUuid } from "./api/utils";
import "./ChatCreate.scss";
export function ChatEdit() {
const loaderData = useLoaderData() as LoaderToType<typeof editChatLoader>;
if (!loaderData) return <div>error</div>;
const { people, chat, chatMembers } = loaderData;
if (
!people ||
isError(people) ||
!chat ||
isError(chat) ||
!chatMembers ||
isError(chatMembers)
) {
return <div>error</div>;
}
let errors: JSX.Element[] = [];
const actionData = useActionData() as ActionToType<typeof editChatAction>;
if (isError(actionData)) {
errors = actionData.errors.map((e) => {
return <a>{e}</a>;
});
}
const navigation = useNavigation();
const busy = navigation.state === "submitting";
return (
<div className={"chatsNew"}>
<div className={"errors"}>{errors}</div>
<Form method="patch">
<input type={"hidden"} name={"chatId"} value={chat.id} />
<label htmlFor="fname">Chat name:</label>
<input type="text" name="name" defaultValue={chat.name} />
<label htmlFor="members">Members:</label>
<select
multiple
defaultValue={chatMembers.map((cme) => cme.uuid)}
name={"members"}
>
{people
.filter((p) => p.uuid != getTokenUserUuid())
.map((p) => (
<option value={p.uuid}>{p.username}</option>
))}
</select>
<button
type="submit"
name={"intent"}
value={"update"}
disabled={busy}
>
Update
</button>
</Form>
<Form method={"delete"}>
<input type={"hidden"} name={"chatId"} value={chat.id} />
<button
type="submit"
name={"intent"}
value={"delete"}
disabled={busy}
>
Delete
</button>
</Form>
</div>
);
}

View File

@@ -10,7 +10,7 @@ import { login } from "./api/Token";
import { isError } from "./api/dto";
import { deleteToken, getTokenUserUuid, setToken } from "./api/utils";
import { createPost, deletePost, updatePost } from "./api/Post";
import { createChat } from "./api/Chat";
import { createChat, deleteChat, updateChat } from "./api/Chat";
import { addMessagesToChat, deleteMessage, editMessage } from "./api/Message";
export type ActionToType<T extends (...args: any) => any> =
@@ -115,6 +115,33 @@ export async function newChatAction({ request }: ActionFunctionArgs) {
} else return ret;
}
export async function editChatAction({ request }: ActionFunctionArgs) {
const formData = await request.formData();
const intent = formData.get("intent")!.toString();
if (intent == "update") {
const ret = await updateChat(
Number(formData.get("chatId")!.toString()),
formData.get("name")!.toString(),
[
...formData.getAll("members")!.map((p) => p.toString()),
getTokenUserUuid()!,
],
);
if (ret && !isError(ret)) {
return redirect("/home/messages/chat/" + ret.id);
} else return ret;
} else if (intent == "delete") {
const ret = await deleteChat(
Number(formData.get("chatId")!.toString()),
);
if (ret && !isError(ret)) {
return redirect("/home/messages/chats");
} else return ret;
}
}
export async function chatAction({ request }: ActionFunctionArgs) {
const formData = await request.formData();
const intent = formData.get("intent")!.toString();

View File

@@ -1,5 +1,14 @@
import { fetchJSONAuth } from "./utils";
import { ChatsToResp, ChatToResp, TChatsToResp, TChatToResp } from "./dto";
import {
ChatsToResp,
ChatToResp,
NoContentToResp,
PersonToArrResp,
TChatsToResp,
TChatToResp,
TNoContentToResp,
TPersonToArrResp,
} from "./dto";
export async function getMyChats(): Promise<TChatsToResp> {
return fetchJSONAuth("/chat/my", "GET", ChatsToResp);
@@ -9,9 +18,32 @@ export async function getChat(id: number): Promise<TChatToResp> {
return fetchJSONAuth("/chat/by-id/" + id, "GET", ChatToResp);
}
export async function getMembers(id: number): Promise<TPersonToArrResp> {
return fetchJSONAuth(
"/chat/by-id/" + id + "/members",
"GET",
PersonToArrResp,
);
}
export async function createChat(
name: string,
memberUuids: string[],
): Promise<TChatToResp> {
return fetchJSONAuth("/chat", "POST", ChatToResp, { name, memberUuids });
}
export async function updateChat(
id: number,
name: string,
memberUuids: string[],
): Promise<TChatToResp> {
return fetchJSONAuth("/chat/by-id/" + id, "PATCH", ChatToResp, {
name,
memberUuids,
});
}
export async function deleteChat(id: number): Promise<TNoContentToResp> {
return fetchJSONAuth("/chat/by-id/" + id, "DELETE", NoContentToResp);
}

View File

@@ -12,7 +12,7 @@ import {
getPostsByAuthorUuid,
getPostsByFollowees,
} from "./api/Post";
import { getChat, getMyChats } from "./api/Chat";
import { getChat, getMembers, getMyChats } from "./api/Chat";
import { getMessagesByChat } from "./api/Message";
export type LoaderToType<T extends (...args: any) => any> =
@@ -86,6 +86,14 @@ export async function newChatLoader() {
return await getAllPerson();
}
export async function editChatLoader({ params }: { params: { id?: number } }) {
return {
people: await getAllPerson(),
chat: await getChat(params.id!),
chatMembers: await getMembers(params.id!),
};
}
export async function chatLoader({ params }: { params: { id?: number } }) {
return {
chat: await getChat(params.id!),