refactor: unify chat context type

This commit is contained in:
Tristan Yang
2023-05-15 13:26:55 +08:00
parent c6edf3e8b1
commit 110b0891ad
30 changed files with 79 additions and 51 deletions
+2 -1
View File
@@ -10,6 +10,7 @@ import { ChatMessage, ContentTypeKey, UploadFileResponse } from "../../types/mes
import handleChatMessage from "../../common/hook/useStreaming/chat.handler";
import { RootState } from "../store";
import { upsertArchiveMessage } from "../slices/message.archive";
import { ChatContext } from "../../types/common";
export const messageApi = createApi({
reducerPath: "messageApi",
@@ -175,7 +176,7 @@ export const messageApi = createApi({
}
}
}),
loadMoreMessages: builder.query<ChatMessage[], { context?: "channel" | "user", id: number; mid?: number; limit?: number }>({
loadMoreMessages: builder.query<ChatMessage[], { context?: ChatContext, id: number; mid?: number; limit?: number }>({
query: ({ context = "channel", id, mid = "", limit = 100 }) => {
const url = context == "channel" ?
`/group/${id}/history?limit=${limit}${mid ? `&before=${mid}` : ""}`
+2 -1
View File
@@ -3,6 +3,7 @@ import { MuteDTO } from "../../types/message";
import { OG } from "../../types/resource";
import { AutoDeleteMessageSettingDTO, AutoDeleteMsgForGroup, AutoDeleteMsgForUser, AutoDeleteSettingForChannels, AutoDeleteSettingForUsers, PinChat, PinChatTarget } from "../../types/sse";
import { resetAuthData } from "./auth.data";
import { ChatContext } from "../../types/common";
type ChannelAside = "members" | "voice" | "voice_fullscreen" | null;
type DMAside = "voice" | null;
@@ -188,7 +189,7 @@ const footprintSlice = createSlice({
const { key, value } = action.payload;
state.og[key] = value;
},
updateHistoryMark(state, action: PayloadAction<{ type: "channel" | "user", id: number, mid: string, }>) {
updateHistoryMark(state, action: PayloadAction<{ type: ChatContext, id: number, mid: string, }>) {
const { type, id, mid } = action.payload;
if (type == "channel") {
state.historyChannels[id] = mid;
+2 -1
View File
@@ -2,9 +2,10 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { KEY_UID } from "../config";
import { ConnectionState } from "agora-rtc-sdk-ng";
import { resetAuthData } from "./auth.data";
import { ChatContext } from "../../types/common";
export type VoiceBasicInfo = {
context: "channel" | "dm",
context: ChatContext,
from?: number,
id: number,// means to in dm context
}
+2 -1
View File
@@ -11,13 +11,14 @@ import IconClose from "../../../assets/icons/close.circle.svg";
import VideoMessage from "./VideoMessage";
import AudioMessage from "./AudioMessage";
import clsx from "clsx";
import { ChatContext } from "../../../types/common";
const isLocalFile = (content: string) => {
return content.startsWith("blob:");
};
interface Props {
context: "user" | "channel";
context: ChatContext;
to: number;
created_at: number;
from_uid: number;
+3 -2
View File
@@ -21,14 +21,15 @@ import moreIcon from "../../../assets/icons/more.svg?url";
import useMessageOperation from "./useMessageOperation";
import { useTranslation } from "react-i18next";
import clsx from "clsx";
import { ChatContext } from "../../../types/common";
type Props = {
isSelf: boolean;
context: "user" | "channel";
context: ChatContext;
contextId: number;
mid: number;
toggleEditMessage: () => void;
};
const Commands: FC<Props> = ({ isSelf, context = "user", contextId = 0, mid = 0, toggleEditMessage }) => {
const Commands: FC<Props> = ({ isSelf, context = "dm", contextId = 0, mid = 0, toggleEditMessage }) => {
const { t } = useTranslation();
const {
canDelete,
+2 -1
View File
@@ -13,8 +13,9 @@ import { updateSelectMessages } from "../../../app/slices/ui";
import useSendMessage from "../../hook/useSendMessage";
import useMessageOperation from "./useMessageOperation";
import { useTranslation } from "react-i18next";
import { ChatContext } from "../../../types/common";
type Props = {
context: "user" | "channel";
context: ChatContext;
contextId: number;
mid: number;
visible: boolean;
+3 -2
View File
@@ -5,8 +5,9 @@ import { removeMessage } from '../../../app/slices/message';
import { removeChannelMsg } from '../../../app/slices/message.channel';
import { removeUserMsg } from '../../../app/slices/message.user';
import IconTimer from '../../../assets/icons/timer.svg';
import { ChatContext } from '../../../types/common';
type Props = {
context: "user" | "channel",
context: ChatContext,
contextId: number,
mid: number;
expiresIn: number;
@@ -18,7 +19,7 @@ const ExpireTimer: FC<Props> = ({ mid, createAt, expiresIn, context, contextId }
const dispatch = useDispatch();
const clearMsgFromClient = useCallback(
() => {
if (context == "user") {
if (context == "dm") {
dispatch(removeUserMsg({ mid, id: contextId }));
} else {
dispatch(removeChannelMsg({ mid, id: contextId }));
@@ -4,9 +4,10 @@ import Avatar from "../Avatar";
import IconForward from "../../../assets/icons/forward.svg";
import useNormalizeMessage from "../../hook/useNormalizeMessage";
import { useTranslation } from "react-i18next";
import { ChatContext } from "../../../types/common";
type Props = {
context: "user" | "channel";
context: ChatContext;
to: number;
from_uid: number;
id: string;
+8 -6
View File
@@ -18,11 +18,12 @@ import usePinMessage from "../../hook/usePinMessage";
import { useAppSelector } from "../../../app/store";
import ExpireTimer from "./ExpireTimer";
import IconInfo from '../../../assets/icons/info.svg';
import { ChatContext } from "../../../types/common";
interface IProps {
readOnly?: boolean;
contextId: number;
context?: "user" | "channel";
context?: ChatContext;
read?: boolean;
mid: number;
updateReadIndex?: (param: any) => void;
@@ -31,7 +32,7 @@ const Message: FC<IProps> = ({
readOnly = false,
contextId,
mid,
context = "user",
context = "dm",
updateReadIndex,
read = true
}) => {
@@ -40,8 +41,9 @@ const Message: FC<IProps> = ({
const [edit, setEdit] = useState(false);
const avatarRef = useRef(null);
const { getPinInfo } = usePinMessage(context == "channel" ? contextId : 0);
const { message, reactionMessageData, usersData, loginUid } = useAppSelector((store) => {
const { message, reactionMessageData, usersData, loginUid, enableRightLayout } = useAppSelector((store) => {
return {
enableRightLayout: store.server.chat_layout_mode == "SelfRight",
reactionMessageData: store.reactionMessage,
message: store.message[mid],
usersData: store.users.byId,
@@ -57,7 +59,7 @@ const Message: FC<IProps> = ({
if (!read) {
// 标记已读
const data =
context == "user"
context == "dm"
? { users: [{ uid: +contextId, mid }] }
: { groups: [{ gid: +contextId, mid }] };
if (updateReadIndex) {
@@ -94,7 +96,7 @@ const Message: FC<IProps> = ({
// return null;
const _key = properties?.local_id || mid;
const showExpire = (expires_in ?? 0) > 0;
const isSelf = fromUid == loginUid;
const isSelf = fromUid == loginUid && enableRightLayout;
return (
<div
@@ -116,7 +118,7 @@ const Message: FC<IProps> = ({
interactive
placement="right"
trigger="click"
content={<Profile uid={fromUid || 0} type="card" cid={context == "user" ? 0 : contextId} />}
content={<Profile uid={fromUid || 0} type="card" cid={context == "dm" ? 0 : contextId} />}
>
<div className="cursor-pointer w-10 h-10 shrink-0" data-uid={fromUid} ref={avatarRef}>
<Avatar className="w-10 h-10 rounded-full object-cover" width={40} height={40} src={currUser?.avatar} name={currUser?.name} />
@@ -8,9 +8,10 @@ import FileMessage from "../FileMessage";
import { ContentType } from "../../../types/message";
import LinkifyText from '../LinkifyText';
import VoiceMessage from "../VoiceMessage";
import { ChatContext } from "../../../types/common";
type Props = {
context: "user" | "channel";
context: ChatContext;
to?: number;
from_uid?: number;
created_at?: number;
@@ -7,10 +7,11 @@ import { ContentTypes } from "../../../app/config";
import useCopy from "../../hook/useCopy";
import usePinMessage from "../../hook/usePinMessage";
import { useAppSelector } from "../../../app/store";
import { ChatContext } from "../../../types/common";
interface Params {
mid: number;
context: "user" | "channel";
context: ChatContext;
contextId: number;
}
+3 -3
View File
@@ -23,6 +23,7 @@ import { CONFIG } from "./config";
import User from "../User";
import { useAppSelector } from "../../../app/store";
import { isMobile } from "../../utils";
import { ChatContext } from "../../../types/common";
export const TEXT_EDITOR_PREFIX = "_text_editor";
let components = createPlateUI({
@@ -30,11 +31,10 @@ let components = createPlateUI({
// customize your components by plugin key
});
const initials = [{ type: ELEMENT_PARAGRAPH, children: [{ text: "" }] }];
type ctx = "channel" | "user";
type Props = {
updateDraft: (draft: any) => void | null;
initialValue?: any;
id: `${ctx}_${number}`;
id: `${ChatContext}_${number}`;
placeholder: string;
sendMessages: any;
updateMessages: any;
@@ -51,7 +51,7 @@ const Plugins: FC<Props> = ({
}) => {
// const { getMenuProps, getItemProps } = useComboboxControls();
const [context, to] = id.split("_") as [ctx, number];
const [context, to] = id.split("_") as [ChatContext, number];
const { addStageFile } = useUploadFile({ context, id: to });
const enableMentions = members.length > 0;
const userData = useAppSelector((store) => store.users.byId);
+2 -1
View File
@@ -7,6 +7,7 @@ import useSendMessage from "../../hook/useSendMessage";
import { useAppSelector } from "../../../app/store";
import { MessagePayload } from "../../../app/slices/message";
import LinkifyText from "../LinkifyText";
import { ChatContext } from "../../../types/common";
const renderContent = (data: MessagePayload) => {
const { content_type, content, thumbnail = "", properties } = data;
@@ -63,7 +64,7 @@ export default function Replying({
id,
mid
}: {
context: "user" | "channel";
context: ChatContext;
id: number;
mid: number;
}) {
+2 -1
View File
@@ -7,6 +7,7 @@ import FullscreenIcon from "../../../assets/icons/fullscreen.svg";
import ExitFullscreenIcon from "../../../assets/icons/fullscreen.exit.svg";
import useUploadFile from "../../hook/useUploadFile";
import { useTranslation } from "react-i18next";
import { ChatContext } from "../../../types/common";
type Props = {
sendMessages: () => void;
@@ -15,7 +16,7 @@ type Props = {
toggleMode: () => void;
mode: "markdown" | "text";
to: number;
context: "user" | "channel";
context: ChatContext;
sendVisible: boolean
};
const Toolbar: FC<Props> = ({
@@ -5,6 +5,7 @@ import { getFileIcon, formatBytes } from "../../../utils";
import useUploadFile from "../../../hook/useUploadFile";
import EditIcon from "../../../../assets/icons/edit.svg";
import DeleteIcon from "../../../../assets/icons/delete.svg";
import { ChatContext } from "../../../../types/common";
type EditProps = {
index: number;
@@ -14,7 +15,7 @@ export default function UploadFileList({
context,
id
}: {
context: "user" | "channel";
context: ChatContext;
id: number;
}) {
const eidtor = useMixedEditor(`${context}_${id}`);
+4 -3
View File
@@ -20,13 +20,14 @@ import { useAppDispatch, useAppSelector } from "../../../app/store";
import TextInput from "../TextInput";
import useUserOperation from "../../hook/useUserOperation";
import StyledButton from "../styled/Button";
import { ChatContext } from "../../../types/common";
const Modes = {
text: "text",
markdown: "markdown"
};
interface IProps {
context?: "channel" | "user";
context?: ChatContext;
id: number;
}
const Send: FC<IProps> = ({
@@ -35,7 +36,7 @@ const Send: FC<IProps> = ({
id
}) => {
const { t } = useTranslation("chat");
const { unblockThisContact, blocked } = useUserOperation({ uid: context == "user" ? id : undefined, cid: context == "channel" ? id : undefined });
const { unblockThisContact, blocked } = useUserOperation({ uid: context == "dm" ? id : undefined, cid: context == "channel" ? id : undefined });
const { resetStageFiles } = useUploadFile({ context, id });
const { getDraft, getUpdateDraft } = useDraft({ context, id });
const editor = useMixedEditor(`${context}_${id}`);
@@ -144,7 +145,7 @@ const Send: FC<IProps> = ({
const members =
context == "channel" ? (channelsData[id]?.is_public ? uids : channelsData[id]?.members) : [];
const isMarkdownMode = mode == Modes.markdown;
if (context == "user" && blocked) {
if (context == "dm" && blocked) {
return <div className="p-5 bg-gray-200 rounded-lg w-full dark:bg-gray-600 text-red-300">
{t("contact_block_tip")}
<StyledButton className="mini ml-4" onClick={unblockThisContact}>{t("unblock")}</StyledButton>
+2 -1
View File
@@ -9,11 +9,12 @@ import { updateChannelVisibleAside, updateDMVisibleAside } from '../../../app/sl
import { addVoiceMember, updatePin, updateVoicingInfo, upsertVoiceList } from '../../../app/slices/voice';
import { useAppSelector } from '../../../app/store';
import { playAgoraVideo } from '../../utils';
import { ChatContext } from '../../../types/common';
type VoiceProps = {
id: number,
context?: "channel" | "dm"
context?: ChatContext
}
const audioJoin = new Audio(AudioJoin);
const useVoice = ({ id, context = "channel" }: VoiceProps) => {
+2 -1
View File
@@ -3,9 +3,10 @@ import { useDispatch } from "react-redux";
import { addMessage, MessagePayload } from "../../app/slices/message";
import { addChannelMsg } from "../../app/slices/message.channel";
import { addUserMsg } from "../../app/slices/message.user";
import { ChatContext } from "../../types/common";
interface IProps {
context: "channel" | "user";
context: ChatContext;
to: number;
}
export default function useAddLocalFileMessage({ context, to }: IProps) {
+2 -1
View File
@@ -1,7 +1,8 @@
import { updateDraftMarkdown, updateDraftMixedText } from "../../app/slices/ui";
import { useAppDispatch, useAppSelector } from "../../app/store";
import { ChatContext } from "../../types/common";
const useDraft = ({ context = "user", id = 0 }: { context: "user" | "channel"; id: number }) => {
const useDraft = ({ context = "dm", id = 0 }: { context: ChatContext; id: number }) => {
const dispatch = useAppDispatch();
const _key = `${context}_${id}`;
const { draftMarkdown, draftMixedText } = useAppSelector((store) => {
+3 -2
View File
@@ -2,14 +2,15 @@ import { removeMessage } from "../../app/slices/message";
import { removeChannelMsg } from "../../app/slices/message.channel";
import { removeUserMsg } from "../../app/slices/message.user";
import { useAppDispatch } from "../../app/store";
import { ChatContext } from "../../types/common";
// todo: check usage
interface Props {
context: "user" | "channel";
context: ChatContext;
id: number;
}
export default function useRemoveLocalMessage({ context = "user", id = 0 }: Props) {
export default function useRemoveLocalMessage({ context = "dm", id = 0 }: Props) {
const dispatch = useAppDispatch();
const removeContextMessage = context == "channel" ? removeChannelMsg : removeUserMsg;
+4 -3
View File
@@ -5,9 +5,10 @@ import { useSendMsgMutation } from "../../app/services/user";
import { useReplyMessageMutation } from "../../app/services/message";
import { useAppDispatch, useAppSelector } from "../../app/store";
import { ContentTypeKey } from "../../types/message";
import { ChatContext } from "../../types/common";
interface Props {
context: "user" | "channel";
context: ChatContext;
from?: number;
to: number;
}
@@ -21,7 +22,7 @@ interface SendMessagesDTO {
type SendMessageDTO = { type: ContentTypeKey } & Partial<MessagePayload> & { ignoreLocal?: boolean }
const useSendMessage = (props?: Props) => {
const { context = "user", from = 0, to = 0 } = props || {};
const { context = "dm", from = 0, to = 0 } = props || {};
const dispatch = useAppDispatch();
const stageFiles = useAppSelector((store) => store.ui.uploadFiles[`${context}_${to}`] || []);
const [replyMessage, { isError: replyErr, isLoading: replying, isSuccess: replySuccess }] =
@@ -32,7 +33,7 @@ const useSendMessage = (props?: Props) => {
] = useSendChannelMsgMutation();
const [sendUserMsg, { isLoading: userSending, isSuccess: userSuccess, isError: userError }] =
useSendMsgMutation();
const sendFn = context == "user" ? sendUserMsg : sendChannelMsg;
const sendFn = context == "dm" ? sendUserMsg : sendChannelMsg;
const sendMessages = async ({
type = "text",
content,
+2 -1
View File
@@ -6,9 +6,10 @@ import { usePrepareUploadFileMutation, useUploadFileMutation } from "../../app/s
import { useAppDispatch, useAppSelector } from "../../app/store";
import { Message } from "../../types/channel";
import { UploadFileResponse } from "../../types/message";
import { ChatContext } from "../../types/common";
interface IProps {
context: "channel" | "user";
context: ChatContext;
id: number;
}
const useUploadFile = (props?: IProps) => {
+2 -2
View File
@@ -31,11 +31,11 @@ const DMChat: FC<Props> = ({ uid = 0, dropFiles }) => {
return (
<Layout
to={uid}
context="user"
context="dm"
dropFiles={dropFiles}
aside={
<ul className="flex flex-col gap-6">
<ServerVersionChecker version="0.3.6" empty={true}>
<ServerVersionChecker version="0.3.7" empty={true}>
<VoiceChat context={`dm`} id={uid} />
</ServerVersionChecker>
<Tooltip tip="Saved Items" placement="left">
+2 -1
View File
@@ -1,8 +1,9 @@
// import { useEffect } from "react";
import { ChatPrefixes } from "../../../app/config";
import { useTranslation } from "react-i18next";
import { ChatContext } from "../../../types/common";
type Props = {
context: "user" | "channel",
context: ChatContext,
name: string
};
const DnDTip = ({ context, name }: Props) => {
@@ -3,9 +3,10 @@ import clsx from 'clsx';
import { useTranslation } from 'react-i18next';
import { useAppSelector } from '../../../app/store';
import getUnreadCount from '../utils';
import { ChatContext } from '../../../types/common';
type Props = {
context: "channel" | "user",
context: ChatContext,
id: number,
scrollToBottom?: () => void
}
@@ -20,7 +21,7 @@ const NewMessageBottomTip = ({ context, id, scrollToBottom }: Props) => {
} = useAppSelector((store) => {
return {
readIndex: context == "channel" ? store.footprint.readChannels[id] : store.footprint.readUsers[id],
mids: context == "user" ? store.userMessage.byId[id] : store.channelMessage[id],
mids: context == "dm" ? store.userMessage.byId[id] : store.channelMessage[id],
selects: store.ui.selectMessages[`${context}_${id}`],
loginUid: store.authData.user?.uid ?? 0,
data: context == "channel" ? store.channels.byId[id] : undefined,
+2 -1
View File
@@ -11,9 +11,10 @@ import IconClose from "../../../assets/icons/close.circle.svg";
import ForwardModal from "../../../common/component/ForwardModal";
import DeleteMessageConfirmModal from "../../../common/component/DeleteMessageConfirm";
import { useAppDispatch, useAppSelector } from "../../../app/store";
import { ChatContext } from "../../../types/common";
type Props = {
context: "user" | "channel";
context: ChatContext;
id: number;
};
const Operations: FC<Props> = ({ context, id }) => {
@@ -12,8 +12,9 @@ import CustomList from './CustomList';
import CustomHeader from './CustomHeader';
import { useDispatch } from 'react-redux';
import { updateHistoryMark } from '../../../../app/slices/footprint';
import { ChatContext } from '../../../../types/common';
type Props = {
context: "user" | "channel",
context: ChatContext,
id: number
}
// const firstMsgIndex = 10000;
@@ -36,8 +37,8 @@ const VirtualMessageFeed = ({ context, id }: Props) => {
footprint
} = useAppSelector((store) => {
return {
historyMid: context == "user" ? store.footprint.historyUsers[id] : store.footprint.historyChannels[id],
mids: context == "user" ? store.userMessage.byId[id] : store.channelMessage[id],
historyMid: context == "dm" ? store.footprint.historyUsers[id] : store.footprint.historyChannels[id],
mids: context == "dm" ? store.userMessage.byId[id] : store.channelMessage[id],
selects: store.ui.selectMessages[`${context}_${id}`],
footprint: store.footprint,
loginUser: store.authData.user,
+4 -3
View File
@@ -19,6 +19,7 @@ import ImagePreview from "../../../common/component/ImagePreview";
import VirtualMessageFeed from "./VirtualMessageFeed";
import DMVoice from "./DMVoicing";
import AddContactTip from "./AddContactTip";
import { ChatContext } from "../../../types/common";
interface Props {
readonly?: boolean;
@@ -27,7 +28,7 @@ interface Props {
users?: ReactElement | null;
voice?: ReactElement | null;
dropFiles?: File[];
context: "channel" | "user";
context: ChatContext;
to: number;
}
@@ -100,8 +101,8 @@ const Layout: FC<Props> = ({
{header}
<div className="w-full h-full flex items-start justify-between relative" >
<div className="rounded-br-2xl flex flex-col absolute bottom-0 w-full h-full" ref={messagesContainer}>
{context == "user" && <DMVoice uid={to} />}
{context == "user" && <AddContactTip uid={to} />}
{context == "dm" && <DMVoice uid={to} />}
{context == "dm" && <AddContactTip uid={to} />}
{/* 消息流 */}
<VirtualMessageFeed key={`${context}_${to}`} context={context} id={to} />
{/* 发送框 */}
+3 -2
View File
@@ -10,6 +10,7 @@ import { updateSelectMessages } from "../../app/slices/ui";
import { useAppSelector } from "../../app/store";
import LinkifyText from "../../common/component/LinkifyText";
import i18n from "../../i18n";
import { ChatContext } from "../../types/common";
export function getUnreadCount({
mids = [],
@@ -122,7 +123,7 @@ type Params = {
prev?: object | null;
curr: object | null;
contextId: number;
context: "user" | "channel";
context: ChatContext;
};
export const renderMessageFragment = ({
readonly = false,
@@ -132,7 +133,7 @@ export const renderMessageFragment = ({
prev,
curr = null,
contextId = 0,
context = "user"
context = "dm"
}: Params) => {
if (!curr) return <div className="w-full h-[1px] invisible"></div>;
let { created_at, mid } = curr;
+1 -1
View File
@@ -19,7 +19,7 @@ const MessageInput = (props: Props) => {
const { sendMessage } = useSendMessage({
from,
to,
context: "user"
context: "dm"
});
const [content, setContent] = useState('');