refactor: more TS code
This commit is contained in:
@@ -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<Props> = ({ download = true, data, closeModal }) =>
|
||||
download={name}
|
||||
type={type}
|
||||
href={downloadLink || originUrl}
|
||||
// target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Download original
|
||||
|
||||
@@ -166,7 +166,7 @@ const ManageMembers: FC<Props> = ({ 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 =
|
||||
|
||||
@@ -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<IProps> = ({ content }) => {
|
||||
const mdContainer = useRef<HTMLDivElement>();
|
||||
const mdContainer = useRef<HTMLDivElement | null>(null);
|
||||
const [previewImage, setPreviewImage] = useState<PreviewImageData | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -61,11 +62,7 @@ const MarkdownRender: FC<IProps> = ({ content }) => {
|
||||
<ImagePreviewModal download={false} data={previewImage} closeModal={closePreviewModal} />
|
||||
)}
|
||||
<Styled ref={mdContainer}>
|
||||
<Viewer
|
||||
initialValue={content}
|
||||
// eslint-disable-next-line no-undef
|
||||
plugins={[codeSyntaxHighlight]}
|
||||
></Viewer>
|
||||
<Viewer initialValue={content} plugins={[codeSyntaxHighlight]}></Viewer>
|
||||
</Styled>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -23,7 +23,7 @@ interface IProps {
|
||||
context?: "user" | "channel";
|
||||
read?: boolean;
|
||||
mid: number;
|
||||
updateReadIndex: (any) => void;
|
||||
updateReadIndex: (param: any) => void;
|
||||
}
|
||||
const Message: FC<IProps> = ({
|
||||
readOnly = false,
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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<Channel[]>([]);
|
||||
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());
|
||||
})
|
||||
);
|
||||
|
||||
@@ -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());
|
||||
})
|
||||
);
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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,
|
||||
|
||||
+18
-5
@@ -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<ArchiveMessage> & {
|
||||
filePath: string;
|
||||
avatar?: number | string;
|
||||
}
|
||||
) => {
|
||||
// uid存在,则favorite,否则archive
|
||||
const prefix = uid
|
||||
? `${BASE_URL}/favorite/attachment/${uid}/${filePath}/`
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -11,7 +11,7 @@ const Styled = styled.div`
|
||||
padding: 30px 0;
|
||||
`;
|
||||
type Props = {
|
||||
pullUp: () => void | null;
|
||||
pullUp: () => Promise<void> | null;
|
||||
};
|
||||
const LoadMore: FC<Props> = ({ pullUp = null }) => {
|
||||
const ref = useRef<HTMLDivElement>();
|
||||
|
||||
+11
-10
@@ -141,7 +141,16 @@ const MessageWrapper = ({ selectMode = false, context, id, mid, children, ...res
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
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<Params>) => {
|
||||
if (!curr) return null;
|
||||
let { created_at, mid } = curr;
|
||||
const local_id = curr.properties?.local_id;
|
||||
@@ -191,14 +200,6 @@ export const renderMessageFragment = ({
|
||||
</MessageWrapper>
|
||||
</React.Fragment>
|
||||
);
|
||||
// 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;
|
||||
|
||||
@@ -19,7 +19,7 @@ export interface UploadFileResponse {
|
||||
path: string;
|
||||
size: number;
|
||||
hash: string;
|
||||
image_properties: {
|
||||
image_properties?: {
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ export interface Archive {
|
||||
}
|
||||
export interface ArchiveUser {
|
||||
name: string;
|
||||
avatar?: number;
|
||||
avatar?: number | string;
|
||||
}
|
||||
export interface ArchiveMessage {
|
||||
from_user: number;
|
||||
|
||||
Reference in New Issue
Block a user