diff --git a/src/common/component/ImagePreviewModal.tsx b/src/common/component/ImagePreviewModal.tsx index e5eb07fc..0c622bfb 100644 --- a/src/common/component/ImagePreviewModal.tsx +++ b/src/common/component/ImagePreviewModal.tsx @@ -14,7 +14,6 @@ const AniFadeIn = keyframes` `; const StyledWrapper = styled.div` - /* todo */ transition: all 0.5s ease; width: 100vw; height: 100vh; @@ -129,7 +128,6 @@ const ImagePreviewModal: FC = ({ download = true, data, closeModal }) => download={name} type={type} href={downloadLink || originUrl} - // target="_blank" rel="noreferrer" > Download original diff --git a/src/common/component/ManageMembers.tsx b/src/common/component/ManageMembers.tsx index f238edae..25f16e9c 100644 --- a/src/common/component/ManageMembers.tsx +++ b/src/common/component/ManageMembers.tsx @@ -166,7 +166,7 @@ const ManageMembers: FC = ({ cid }) => { if (!currUser) return null; const { name, email, is_admin } = currUser; const owner = channel && channel.owner == uid; - const switchRoleVisible = loginUser.is_admin && loginUser.uid !== uid; + const switchRoleVisible = loginUser?.is_admin && loginUser.uid !== uid; const dotsVisible = email || loginUser?.is_admin; const canRemove = loginUser?.is_admin && loginUser?.uid != uid; const canRemoveFromChannel = diff --git a/src/common/component/MarkdownRender.tsx b/src/common/component/MarkdownRender.tsx index 7c58812b..92a85402 100644 --- a/src/common/component/MarkdownRender.tsx +++ b/src/common/component/MarkdownRender.tsx @@ -1,6 +1,7 @@ import { useEffect, useState, useRef, FC } from "react"; import "prismjs/themes/prism.css"; import "@toast-ui/editor-plugin-code-syntax-highlight/dist/toastui-editor-plugin-code-syntax-highlight.css"; +//@ts-ignore import codeSyntaxHighlight from "@toast-ui/editor-plugin-code-syntax-highlight/dist/toastui-editor-plugin-code-syntax-highlight-all.js"; import { Viewer } from "@toast-ui/react-editor"; @@ -22,7 +23,7 @@ interface IProps { content: string; } const MarkdownRender: FC = ({ content }) => { - const mdContainer = useRef(); + const mdContainer = useRef(null); const [previewImage, setPreviewImage] = useState(null); useEffect(() => { @@ -61,11 +62,7 @@ const MarkdownRender: FC = ({ content }) => { )} - + ); diff --git a/src/common/component/Message/index.tsx b/src/common/component/Message/index.tsx index 87b855d0..3b795e12 100644 --- a/src/common/component/Message/index.tsx +++ b/src/common/component/Message/index.tsx @@ -23,7 +23,7 @@ interface IProps { context?: "user" | "channel"; read?: boolean; mid: number; - updateReadIndex: (any) => void; + updateReadIndex: (param: any) => void; } const Message: FC = ({ readOnly = false, diff --git a/src/common/hook/useFavMessage.ts b/src/common/hook/useFavMessage.ts index 8faa88f7..a7d21b49 100644 --- a/src/common/hook/useFavMessage.ts +++ b/src/common/hook/useFavMessage.ts @@ -20,8 +20,8 @@ export default function useFavMessage({ const addFavorite = async (mid = []) => { const mids = Array.isArray(mid) ? mid.map((i) => +i) : [+mid]; if (mids.length == 0) return; - const { error = null } = await addFav(mids); - return !error; + const resp = await addFav(mids); + return "error" in resp; }; const removeFavorite = (id: number) => { diff --git a/src/common/hook/useFilteredChannels.ts b/src/common/hook/useFilteredChannels.ts index b14758f4..501122bc 100644 --- a/src/common/hook/useFilteredChannels.ts +++ b/src/common/hook/useFilteredChannels.ts @@ -5,7 +5,7 @@ import { Channel } from "../../types/channel"; export default function useFilteredChannels() { const [input, setInput] = useState(""); const channels = useAppSelector((store) => Object.values(store.channels.byId)); - const [filteredChannels, setFilteredChannels] = useState([]); + const [filteredChannels, setFilteredChannels] = useState<(Channel | undefined)[]>([]); useEffect(() => { if (!input) { @@ -15,6 +15,7 @@ export default function useFilteredChannels() { let reg = new RegExp(str); setFilteredChannels( channels.filter((c) => { + if (!c) return false; return reg.test(c.name.toLowerCase()); }) ); diff --git a/src/common/hook/useFilteredUsers.ts b/src/common/hook/useFilteredUsers.ts index 65042495..e82d0ef1 100644 --- a/src/common/hook/useFilteredUsers.ts +++ b/src/common/hook/useFilteredUsers.ts @@ -1,10 +1,11 @@ import { useState, useEffect } from "react"; +import { StoredUser } from "../../app/slices/users"; import { useAppSelector } from "../../app/store"; export default function useFilteredUsers() { const [input, setInput] = useState(""); const users = useAppSelector((store) => Object.values(store.users.byId)); - const [filteredUsers, setFilteredUsers] = useState([]); + const [filteredUsers, setFilteredUsers] = useState<(StoredUser | undefined)[]>([]); useEffect(() => { if (!input) { setFilteredUsers(users); @@ -13,6 +14,7 @@ export default function useFilteredUsers() { let reg = new RegExp(str); setFilteredUsers( users.filter((c) => { + if (!c) return false; return reg.test(c.name.toLowerCase()); }) ); diff --git a/src/common/hook/useForwardMessage.ts b/src/common/hook/useForwardMessage.ts index 7f5aa3d9..53ac9ca8 100644 --- a/src/common/hook/useForwardMessage.ts +++ b/src/common/hook/useForwardMessage.ts @@ -25,7 +25,11 @@ export default function useForwardMessage() { channels: number[]; }) => { setForwarding(true); - const { data: archive_id } = await createArchive(mids); + const resp = await createArchive(mids); + if ("error" in resp) { + return; + } + const archive_id = resp.data; if (users.length) { for await (const uid of users) { await sendUserMsg({ diff --git a/src/common/hook/useSendMessage.ts b/src/common/hook/useSendMessage.ts index 27c4942c..e5d17a9d 100644 --- a/src/common/hook/useSendMessage.ts +++ b/src/common/hook/useSendMessage.ts @@ -25,7 +25,7 @@ interface SendMessageDTO { } const useSendMessage = (props?: Props) => { - const { context = "user", from = 0, to = null } = props || {}; + const { context = "user", 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 }] = @@ -72,12 +72,9 @@ const useSendMessage = (props?: Props) => { if (reply_mid) { removeReplying(); await replyMessage({ - id: to, reply_mid, type, - content, - context, - from_uid: from + content }); } else { await sendFn({ diff --git a/src/common/hook/useUploadFile.ts b/src/common/hook/useUploadFile.ts index ceb78c8b..687d9e0d 100644 --- a/src/common/hook/useUploadFile.ts +++ b/src/common/hook/useUploadFile.ts @@ -1,10 +1,11 @@ -import { useState, useRef, FC } from "react"; +import { useState, useRef } from "react"; import toast from "react-hot-toast"; import { updateUploadFiles } from "../../app/slices/ui"; import BASE_URL, { FILE_SLICE_SIZE } from "../../app/config"; import { usePrepareUploadFileMutation, useUploadFileMutation } from "../../app/services/message"; import { useAppDispatch, useAppSelector } from "../../app/store"; import { Message } from "../../types/channel"; +import { UploadFileResponse } from "../../types/message"; // todo: check props type interface IProps { @@ -28,7 +29,7 @@ const useUploadFile = (props?: IProps) => { const [uploadFileFn, { isLoading: isUploading, isError: uploadFileError }] = useUploadFileMutation(); - const uploadChunk = (data: { file_id: string; chunk: File; is_last: boolean }) => { + const uploadChunk = (data: { file_id: string; chunk: Blob; is_last: boolean }) => { const { file_id, chunk, is_last } = data; const formData = new FormData(); formData.append("file_id", file_id); @@ -48,10 +49,15 @@ const useUploadFile = (props?: IProps) => { size: file_size } = file; // 拿file id - const { data: file_id } = await prepareUploadFile({ + const resp = await prepareUploadFile({ content_type: file_type, filename: name }); + if ("error" in resp) { + toast.error("Prepare Upload File Error"); + return; + } + const file_id = resp.data; console.log("file id", file_id); let uploadResult = null; @@ -92,9 +98,10 @@ const useUploadFile = (props?: IProps) => { console.log("wtfff", uploadResult); } // setUploadingFile(false); - const { - data: { path, size, hash } - } = uploadResult; + if (!uploadResult || "error" in uploadResult) { + return; + } + const { path, size, hash } = uploadResult.data as UploadFileResponse; const encodedPath = encodeURIComponent(path); const res = { name, diff --git a/src/common/utils.tsx b/src/common/utils.tsx index 5fbb0107..63e3d6f5 100644 --- a/src/common/utils.tsx +++ b/src/common/utils.tsx @@ -6,7 +6,7 @@ import IconUnknown from "../assets/icons/file.unknown.svg"; import IconDoc from "../assets/icons/file.doc.svg"; import IconCode from "../assets/icons/file.code.svg"; import IconImage from "../assets/icons/file.image.svg"; -import { Archive } from "../types/resource"; +import { Archive, ArchiveMessage } from "../types/resource"; export const isImage = (file_type = "", size = 0) => { return file_type.startsWith("image") && size <= FILE_IMAGE_SIZE; @@ -40,7 +40,7 @@ export function getDefaultSize(size = null, min = 480) { return { width: dWidth, height: dHeight }; } -export function formatBytes(bytes, decimals = 2) { +export function formatBytes(bytes: number, decimals = 2) { if (bytes === 0) return "0 Bytes"; const k = 1000; @@ -51,7 +51,7 @@ export function formatBytes(bytes, decimals = 2) { return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i]; } -export const getImageSize = (url) => { +export const getImageSize = (url: string) => { const size = { width: 0, height: 0 }; if (!url) return size; return new Promise((resolve) => { @@ -67,7 +67,7 @@ export const getImageSize = (url) => { }; }); }; -export const getInitials = (name) => { +export const getInitials = (name: string) => { const arr = name.split(" ").filter((n) => !!n); return arr .map((t) => t[0]) @@ -178,7 +178,20 @@ export const normalizeArchiveData = ( ) => { if (!data || !filePath) return []; const { messages, users } = data; - const getUrls = (uid, { content, content_type, file_id, thumbnail_id, filePath, avatar }) => { + const getUrls = ( + uid: number | undefined, + { + content, + content_type, + file_id, + thumbnail_id, + filePath, + avatar + }: Partial & { + filePath: string; + avatar?: number | string; + } + ) => { // uid存在,则favorite,否则archive const prefix = uid ? `${BASE_URL}/favorite/attachment/${uid}/${filePath}/` diff --git a/src/routes/chat/ChannelChat/index.tsx b/src/routes/chat/ChannelChat/index.tsx index fe3003c8..9c0707ff 100644 --- a/src/routes/chat/ChannelChat/index.tsx +++ b/src/routes/chat/ChannelChat/index.tsx @@ -64,7 +64,7 @@ export default function ChannelChat({ cid = 0, dropFiles = [] }: Props) { loginUser: store.authData.user, // msgIds: store.channelMessage[cid] || [], userIds: store.users.ids, - data: store.channels.byId[cid] || {}, + data: store.channels.byId[cid], messageData: store.message || {} }; }); @@ -201,7 +201,7 @@ export default function ChannelChat({ cid = 0, dropFiles = [] }: Props) { if (!curr) return null; const isFirst = idx == 0; const prev = isFirst ? null : messageData[feeds[idx - 1]]; - const read = curr?.from_uid == loginUser.uid || mid <= readIndex; + const read = curr?.from_uid == loginUser?.uid || mid <= readIndex; return renderMessageFragment({ selectMode: !!selects, updateReadIndex: updateReadDebounced, diff --git a/src/routes/chat/Layout/index.tsx b/src/routes/chat/Layout/index.tsx index 71a35312..ec6aefbe 100644 --- a/src/routes/chat/Layout/index.tsx +++ b/src/routes/chat/Layout/index.tsx @@ -13,7 +13,7 @@ interface Props { children: ReactElement; header: ReactElement; aside: ReactElement | null; - users?: ReactElement; + users?: ReactElement | null; dropFiles?: File[]; context: "channel" | "user"; to: number; diff --git a/src/routes/chat/LoadMore.tsx b/src/routes/chat/LoadMore.tsx index 2bffa0c5..15aed449 100644 --- a/src/routes/chat/LoadMore.tsx +++ b/src/routes/chat/LoadMore.tsx @@ -11,7 +11,7 @@ const Styled = styled.div` padding: 30px 0; `; type Props = { - pullUp: () => void | null; + pullUp: () => Promise | null; }; const LoadMore: FC = ({ pullUp = null }) => { const ref = useRef(); diff --git a/src/routes/chat/utils.tsx b/src/routes/chat/utils.tsx index e1bfb9bd..4b0db1e2 100644 --- a/src/routes/chat/utils.tsx +++ b/src/routes/chat/utils.tsx @@ -141,7 +141,16 @@ const MessageWrapper = ({ selectMode = false, context, id, mid, children, ...res ); }; - +type Params = { + selectMode: boolean; + isFirst: boolean; + read: boolean; + updateReadIndex: (param: any) => void; + prev: object | null; + curr: object | null; + contextId: number; + context: "user" | "channel"; +}; export const renderMessageFragment = ({ selectMode = false, isFirst = false, @@ -151,7 +160,7 @@ export const renderMessageFragment = ({ curr = null, contextId = 0, context = "user" -}) => { +}: Partial) => { if (!curr) return null; let { created_at, mid } = curr; const local_id = curr.properties?.local_id; @@ -191,14 +200,6 @@ export const renderMessageFragment = ({ ); - // React.memo( - // (prevs, nexts) => { - // // curr.properties?.local_id - // const prevObj = prevs.curr || undefined; - // const nextObj = nexts.curr || undefined; - // return prevObj?.properties?.local_id === nextObj?.properties?.local_id; - // } - // ); }; export default getUnreadCount; diff --git a/src/types/message.ts b/src/types/message.ts index a9790c3a..995a7d3d 100644 --- a/src/types/message.ts +++ b/src/types/message.ts @@ -19,7 +19,7 @@ export interface UploadFileResponse { path: string; size: number; hash: string; - image_properties: { + image_properties?: { width: number; height: number; }; diff --git a/src/types/resource.ts b/src/types/resource.ts index 63d54e5d..eee701d9 100644 --- a/src/types/resource.ts +++ b/src/types/resource.ts @@ -5,7 +5,7 @@ export interface Archive { } export interface ArchiveUser { name: string; - avatar?: number; + avatar?: number | string; } export interface ArchiveMessage { from_user: number;