Browse Source

FEATURE | Edite message

merge-requests/184/merge
Vitalik 2 years ago
parent
commit
e88f02bd40
  1. 1
      .eslintrc.js
  2. 4
      src/api/chats-messages/request.interfaces.ts
  3. 5
      src/api/chats-messages/requests.ts
  4. 25
      src/containers/Chats/chats.screen.tsx
  5. 10
      src/containers/Chats/configs/chat-message-menu-options.config.ts
  6. 1
      src/containers/Chats/enums/chat-message-action.enum.ts
  7. 25
      src/containers/Chats/hooks/use-chat-messages.hook.ts
  8. 22
      src/containers/Chats/hooks/use-create-text-message.hook.ts
  9. 22
      src/containers/Chats/plugins/chat-bar.component.tsx
  10. 3
      src/containers/Chats/plugins/chat-messages.component.tsx
  11. 36
      src/containers/Chats/plugins/edted-bar-section.component.tsx
  12. 10
      src/services/domain/chat-messages.service.ts
  13. 9
      src/services/system/real-time.service.ts
  14. 6
      src/shared/events/index.ts

1
.eslintrc.js

@ -24,5 +24,6 @@ module.exports = { @@ -24,5 +24,6 @@ module.exports = {
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-this-alias": "off",
"@typescript-eslint/no-empty-interface": "off",
"@typescript-eslint/ban-types": "off",
},
};

4
src/api/chats-messages/request.interfaces.ts

@ -11,6 +11,10 @@ export interface ISendTextMessage { @@ -11,6 +11,10 @@ export interface ISendTextMessage {
mentionsMessage?: string;
replyToId?: number;
}
export interface IEditTextMessage {
newMessage: string;
messageId: number;
}
export interface ISendStickerMessage {
chatId: number;

5
src/api/chats-messages/requests.ts

@ -16,6 +16,10 @@ export const sendTextMessageReq = ( @@ -16,6 +16,10 @@ export const sendTextMessageReq = (
return http.post<void>(`app/chats-messages/text-message`, data);
};
export const editTextMessageReq = (data: Req.IEditTextMessage) => {
return http.patch<void>("chats-messages/text-message", data);
};
export const sendStickerMessageReq = (data: Req.ISendStickerMessage) => {
return http.post<void>(`app/chats-messages/sticker-message`, data);
};
@ -70,4 +74,5 @@ export const chatMessagesApi = { @@ -70,4 +74,5 @@ export const chatMessagesApi = {
unpinMessageReq,
clearChatReq,
sendStickerMessageReq,
editTextMessageReq,
};

25
src/containers/Chats/chats.screen.tsx

@ -74,16 +74,11 @@ const ChatsPage: FC = () => { @@ -74,16 +74,11 @@ const ChatsPage: FC = () => {
loadMore,
loadPage,
setSearchVal,
// handleChatAction,
isLoading,
onPin,
isPinned,
// isLoadingNext,
// resetFlatList,
} = useChatList();
console.log("selected chat ID", selectedChatId);
const { headerChatInfo, chatDetails } = useChatDetails(selectedChatId);
const {
@ -157,6 +152,8 @@ const ChatsPage: FC = () => { @@ -157,6 +152,8 @@ const ChatsPage: FC = () => {
setMessage: setNewMessage,
isSending,
sendMessage,
editedMessage,
clearEditedMessage,
} = useCreateTextMessage({
chatMembers: chatDetails?.chatMembers,
chatId: selectedChatId,
@ -164,12 +161,15 @@ const ChatsPage: FC = () => { @@ -164,12 +161,15 @@ const ChatsPage: FC = () => {
replyToMessage,
});
const {
imageMessages: newImage,
setImages: setNewImages,
isSending: isFileSending,
sendImageMessage,
} = useSendFiles({
useEffect(() => {
if (editedMessage && replyToMessage) clearReplyTo();
}, [editedMessage]);
useEffect(() => {
if (replyToMessage && editedMessage) clearEditedMessage();
}, [replyToMessage]);
useSendFiles({
// replyToMessage,
chatId: selectedChatId,
onSend: onSendMessage,
@ -306,6 +306,9 @@ const ChatsPage: FC = () => { @@ -306,6 +306,9 @@ const ChatsPage: FC = () => {
replyToMessage={replyToMessage}
clearReplyTo={clearReplyTo}
replyToName={replyToUserName}
editedMessage={editedMessage}
onPressClearEditedMessage={clearEditedMessage}
isSending={isSending}
/>
</>
) : (

10
src/containers/Chats/configs/chat-message-menu-options.config.ts

@ -7,6 +7,7 @@ interface IProps { @@ -7,6 +7,7 @@ interface IProps {
canPin?: boolean;
canUnpin?: boolean;
canDownload?: boolean;
canEdit?: boolean;
}
export const getChatMessageMenuOptions = ({
@ -16,6 +17,7 @@ export const getChatMessageMenuOptions = ({ @@ -16,6 +17,7 @@ export const getChatMessageMenuOptions = ({
canPin,
canUnpin,
canDownload,
canEdit,
}: IProps) => {
const menuOptions = {
copy: {
@ -23,6 +25,11 @@ export const getChatMessageMenuOptions = ({ @@ -23,6 +25,11 @@ export const getChatMessageMenuOptions = ({
label: "Копіювати",
onClick: () => onClick(ChatMessageActionEnum.COPY),
},
edit: {
key: "edit",
label: "Редагувати",
onClick: () => onClick(ChatMessageActionEnum.EDIT),
},
forward: {
key: "forward",
label: "Переслати",
@ -68,6 +75,9 @@ export const getChatMessageMenuOptions = ({ @@ -68,6 +75,9 @@ export const getChatMessageMenuOptions = ({
if (canCopy) optionsKeys.push("copy");
optionsKeys.push("delete");
if (canDeleteForAll) optionsKeys.push("deleteForAll");
if (canEdit) optionsKeys.push("edit");
console.log("edit", optionsKeys);
const options = optionsKeys.map((key) => menuOptions[key]);

1
src/containers/Chats/enums/chat-message-action.enum.ts

@ -7,4 +7,5 @@ export enum ChatMessageActionEnum { @@ -7,4 +7,5 @@ export enum ChatMessageActionEnum {
DELETE,
DELETE_FOR_ALL,
DOWNLOAD,
EDIT,
}

25
src/containers/Chats/hooks/use-chat-messages.hook.ts

@ -19,6 +19,7 @@ import { appEvents } from "@/shared/events"; @@ -19,6 +19,7 @@ import { appEvents } from "@/shared/events";
import { ChatMessageActionEnum } from "../enums";
import { getChatMessageMenuOptions } from "../configs";
import { chatMessagesApi } from "@/api";
import store from "@/store";
export const useChatMessages = (
chatId: number,
@ -122,6 +123,10 @@ export const useChatMessages = ( @@ -122,6 +123,10 @@ export const useChatMessages = (
appEvents.emit("openForwardMessageModal", { message, isShow: true });
};
const onEdit = (message: IChatMessage) => {
appEvents.emit("onPressEditmessage", { message });
};
const actions = {
[ChatMessageActionEnum.FORWARD]: onForward,
[ChatMessageActionEnum.DELETE]: onDelete,
@ -130,6 +135,7 @@ export const useChatMessages = ( @@ -130,6 +135,7 @@ export const useChatMessages = (
[ChatMessageActionEnum.COPY]: onCopy,
[ChatMessageActionEnum.PIN]: onPin,
[ChatMessageActionEnum.UNPIN]: onUnpin,
[ChatMessageActionEnum.EDIT]: onEdit,
};
const onMessageActions = (message: IChatMessage, role: ChatMemberRole) => {
@ -138,6 +144,8 @@ export const useChatMessages = ( @@ -138,6 +144,8 @@ export const useChatMessages = (
const canCopy = copyEnabledTypes.includes(message.type);
const canPin = !message.isPined;
const canUnpin = message.isPined;
const canEdit =
message.type === MessageType.Text && message.userId === account.id;
const options = getChatMessageMenuOptions({
canDeleteForAll,
@ -146,6 +154,7 @@ export const useChatMessages = ( @@ -146,6 +154,7 @@ export const useChatMessages = (
canCopy,
onClick: (actionType: ChatMessageActionEnum) =>
actions[actionType](message),
canEdit,
});
appEvents.emit("openMessageMenuOptions", { items: options });
@ -159,9 +168,7 @@ export const useChatMessages = ( @@ -159,9 +168,7 @@ export const useChatMessages = (
};
const onLoadNew = async (limit?: number) => {
const id = messages[messages.length - 1]?.id;
await loadNew(limit);
// setTimeout(() => setScrollToId(id), 80);
};
useEffect(() => {
@ -289,6 +296,20 @@ export const useChatMessages = ( @@ -289,6 +296,20 @@ export const useChatMessages = (
_setItems(filteredItems);
};
const onUpdateMessage = ({ message }) => {
console.log("on update message");
const newItems = messages.map((it) => {
if (it.id === message.id) {
return message;
}
return it;
});
_setItems(newItems);
};
useSocketListener("chat/update-message", onUpdateMessage, [messages]);
// APP EVENTS AND SOCKET LISTENERS //
useEventsListener("chat/new-media-message", (data) => {

22
src/containers/Chats/hooks/use-create-text-message.hook.ts

@ -6,6 +6,7 @@ import { @@ -6,6 +6,7 @@ import {
hasImageUrl,
IChatMember,
IChatMessage,
useEventsListener,
} from "@/shared";
import _ from "lodash";
import { useSelector } from "react-redux";
@ -28,6 +29,16 @@ export const useCreateTextMessage = ({ @@ -28,6 +29,16 @@ export const useCreateTextMessage = ({
const [message, setMessage] = useState<string>("");
const [isSending, setSending] = useState<boolean>(false);
const [editedMessage, setEditedMessage] = useState<IChatMessage>(null);
useEventsListener(
"onPressEditmessage",
({ message }) => {
setEditedMessage(message);
setMessage(message.content?.message);
},
[setEditedMessage, setMessage]
);
const sendMessage = async () => {
if (!message || message.trim().length === 0) return;
@ -35,16 +46,12 @@ export const useCreateTextMessage = ({ @@ -35,16 +46,12 @@ export const useCreateTextMessage = ({
setSending(true);
try {
// const transformed = "";
const data: ISendTextMessage = {
chatId,
message: message,
replyToId: replyToMessage?.id,
};
// if (!_.isEqual(message, transformed)) data.mentionsMessage = message;
onSend();
setMessage(null);
await chatMessagesService.sendTextMessage(data);
@ -70,11 +77,18 @@ export const useCreateTextMessage = ({ @@ -70,11 +77,18 @@ export const useCreateTextMessage = ({
avatar: hasImageUrl(avatarUrl, name),
};
});
const clearEditedMessage = () => {
setEditedMessage(null);
setMessage("");
};
return {
suggestionItems: allItems,
message,
setMessage,
isSending,
sendMessage,
editedMessage,
clearEditedMessage,
};
};

22
src/containers/Chats/plugins/chat-bar.component.tsx

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
import { IconComponent } from "@/shared";
import React, { FC, useCallback } from "react";
import { IconComponent, useEventsListener } from "@/shared";
import React, { FC, useCallback, useRef } from "react";
import "./style.scss";
import paperClipIcon from "@/assets/img/paperClip-icon.svg";
import x1Icon from "@/assets/img/x-1-icon.svg";
@ -11,6 +11,7 @@ import { IChatMessage, ISuggestionUser } from "./interfaces"; @@ -11,6 +11,7 @@ import { IChatMessage, ISuggestionUser } from "./interfaces";
import { Avatar } from "../atoms";
import { Button } from "antd";
import { ReplyBarSection } from "./reply-bar-section.component";
import { EditedBarSection } from "./edted-bar-section.component";
interface ChatBarProps {
onPressSend: () => void;
@ -26,9 +27,21 @@ interface ChatBarProps { @@ -26,9 +27,21 @@ interface ChatBarProps {
replyToMessage?: IChatMessage;
replyToName?: string;
clearReplyTo?: () => void;
editedMessage?: IChatMessage;
onPressClearEditedMessage?: () => void;
}
export const ChatBar: FC<ChatBarProps> = (props) => {
const inputRef = useRef<any>(null);
useEventsListener(
"onPressEditmessage",
() => {
if (inputRef.current) inputRef.current?.focus();
},
[inputRef.current]
);
const renderLeftPart = () => {
if (props?.withAttachmentsBtn)
return (
@ -128,11 +141,16 @@ export const ChatBar: FC<ChatBarProps> = (props) => { @@ -128,11 +141,16 @@ export const ChatBar: FC<ChatBarProps> = (props) => {
onPressClose={props.clearReplyTo}
name={props.replyToName}
/>
<EditedBarSection
message={props.editedMessage}
onPressClose={props.onPressClearEditedMessage}
/>
<div className="chat-bar-input-field">
<div className="chat-bar-left">
{renderLeftPart()}
<MentionsInput
inputRef={inputRef}
className="message-input"
value={props.message}
onChange={(e) => {

3
src/containers/Chats/plugins/chat-messages.component.tsx

@ -57,11 +57,8 @@ export const ChatMessages: FC<ChatMessagesProps> = ({ @@ -57,11 +57,8 @@ export const ChatMessages: FC<ChatMessagesProps> = ({
...props
}) => {
const [activeAudioId, setActiveAudioId] = useState<number>(null);
const [lastOffset, setOffset] = useState(0);
// const [isScrolling, setScrolling] = useState(false);
const listRef = useRef(null);
const ref = useRef(null);

36
src/containers/Chats/plugins/edted-bar-section.component.tsx

@ -0,0 +1,36 @@ @@ -0,0 +1,36 @@
import { getMessagePreviewText, IconComponent } from "@/shared";
import React, { FC, useMemo } from "react";
import { IChatMessage } from "./interfaces";
import { MessageMediaPreview } from "./message-media-preview.component";
import x1Icon from "@/assets/img/x-1-icon.svg";
interface IProps {
message: IChatMessage;
onPressClose: () => void;
}
export const EditedBarSection: FC<IProps> = ({ message, onPressClose }) => {
if (!message) return null;
const text = useMemo(() => getMessagePreviewText(message), [message]);
return (
<div className="reply-bar-container">
<div className="reply-main-section">
<div className="reply-media-preview">
<MessageMediaPreview
type={message?.type}
uri={message?.content?.fileUrl}
/>
</div>
<div className="reply-text-section">
<span>{`Редагування повідомленя`}</span>
<span className="collapsed">{text}</span>
</div>
</div>
<div className="reply-close-section" onClick={onPressClose}>
<IconComponent name={x1Icon} />
</div>
</div>
);
};

10
src/services/domain/chat-messages.service.ts

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
import { chatMessagesApi } from "@/api";
import {
IDeleteMessageParams,
IEditTextMessage,
IFetchChatMessages,
ISendFileMessage,
ISendStickerMessage,
@ -33,6 +34,14 @@ const sendTextMessage = async (data: ISendTextMessage) => { @@ -33,6 +34,14 @@ const sendTextMessage = async (data: ISendTextMessage) => {
}
};
const editTextMessage = async (data: IEditTextMessage) => {
try {
await chatMessagesApi.editTextMessageReq(data);
} catch (error) {
console.log("ERROR | SEND CHAT MESSAGES ERROR ", error);
}
};
const sendStickerMessage = async (data: ISendStickerMessage) => {
try {
await chatMessagesApi.sendStickerMessageReq(data);
@ -93,4 +102,5 @@ export const chatMessagesService = { @@ -93,4 +102,5 @@ export const chatMessagesService = {
deleteChatMessage,
clearAllChatMessages,
sendStickerMessage,
editTextMessage,
};

9
src/services/system/real-time.service.ts

@ -15,7 +15,7 @@ export class SocketIo { @@ -15,7 +15,7 @@ export class SocketIo {
this.socket = io(config.socketUrl, {
transports: ["websocket", "polling"],
reconnection: true,
secure: true
secure: true,
});
this._on("connect", () => {
this.emit("join-user", store().getState().account.account);
@ -28,16 +28,16 @@ export class SocketIo { @@ -28,16 +28,16 @@ export class SocketIo {
get header() {
return {
authorization: "Bearer " + store()?.getState()?.auth?.accessToken
authorization: "Bearer " + store()?.getState()?.auth?.accessToken,
};
}
_on(key, action) {
this.socket.on(key, data => action(data));
this.socket.on(key, (data) => action(data));
}
_onSocketSendEvent(key) {
this.socket.on(key, data => {
this.socket.on(key, (data) => {
socketEvents.emit(key, data);
});
}
@ -82,6 +82,7 @@ export class SocketIo { @@ -82,6 +82,7 @@ export class SocketIo {
this._onSocketSendEvent("user/disconnected");
this._onSocketSendEvent("user/deleted");
this._onSocketSendEvent("user/change-permissions");
this._onSocketSendEvent("chat/update-message");
this._onSocketSendEvent("stopSessions");

6
src/shared/events/index.ts

@ -64,6 +64,10 @@ export type AppEvents = { @@ -64,6 +64,10 @@ export type AppEvents = {
selectSticker: {
onSelect: (sticker: string) => void;
};
onPressEditmessage: {
message: IChatMessage;
};
};
export type SocketEvents = {
@ -93,6 +97,8 @@ export type SocketEvents = { @@ -93,6 +97,8 @@ export type SocketEvents = {
stopSessions: { sessionsIds: number[] };
notification: { notification: IPushNotification };
"chat/update-message": { message: IChatMessage };
};
export const appEvents = new Events<AppEvents>();

Loading…
Cancel
Save