refactor: more TS code
This commit is contained in:
@@ -82,7 +82,7 @@ export const authApi = createApi({
|
||||
}
|
||||
}),
|
||||
// 更新 device token
|
||||
updateDeviceToken: builder.mutation<void, { device_token: string }>({
|
||||
updateDeviceToken: builder.mutation<void, string>({
|
||||
query: (device_token) => ({
|
||||
url: "/token/device_token",
|
||||
method: "PUT",
|
||||
|
||||
@@ -19,7 +19,7 @@ const favoritesSlice = createSlice({
|
||||
addFavorite(state, action: PayloadAction<Favorite>) {
|
||||
state.push(action.payload);
|
||||
},
|
||||
deleteFavorite(state, action: PayloadAction<number>) {
|
||||
deleteFavorite(state, action: PayloadAction<string>) {
|
||||
const id = action.payload;
|
||||
const idx = state.findIndex((f) => f.id == id);
|
||||
if (idx > -1) {
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
import BASE_URL, { ContentTypes } from "../config";
|
||||
import { isImage } from "../../common/utils";
|
||||
|
||||
export interface State {
|
||||
[key: number]: object;
|
||||
replying: {
|
||||
[key: string | number]: number;
|
||||
};
|
||||
}
|
||||
export interface MessagePayload {
|
||||
mid: number;
|
||||
sending: boolean;
|
||||
@@ -21,6 +14,13 @@ export interface MessagePayload {
|
||||
download?: string;
|
||||
thumbnail?: string;
|
||||
}
|
||||
export interface State {
|
||||
[key: number]: MessagePayload;
|
||||
replying: {
|
||||
[key: string | number]: number;
|
||||
};
|
||||
}
|
||||
|
||||
const initialState: State = {
|
||||
replying: {}
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { FC, ReactElement, ReactNode } from "react";
|
||||
import Tippy from "@tippyjs/react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import ContextMenu from "../ContextMenu";
|
||||
@@ -11,8 +12,16 @@ import IconSelect from "../../../assets/icons/select.svg";
|
||||
import { updateSelectMessages } from "../../../app/slices/ui";
|
||||
import useSendMessage from "../../hook/useSendMessage";
|
||||
import useMessageOperation from "./useMessageOperation";
|
||||
|
||||
export default function MessageContextMenu({
|
||||
type Props = {
|
||||
context: "user" | "channel";
|
||||
contextId: number;
|
||||
mid: number;
|
||||
visible: boolean;
|
||||
hide: () => void;
|
||||
editMessage: () => void;
|
||||
children: ReactElement;
|
||||
};
|
||||
const MessageContextMenu: FC<Props> = ({
|
||||
context,
|
||||
contextId,
|
||||
mid,
|
||||
@@ -20,10 +29,9 @@ export default function MessageContextMenu({
|
||||
hide,
|
||||
editMessage,
|
||||
children
|
||||
}) {
|
||||
}) => {
|
||||
const {
|
||||
copyContent,
|
||||
// isMarkdown,
|
||||
canEdit,
|
||||
canPin,
|
||||
canDelete,
|
||||
@@ -109,4 +117,5 @@ export default function MessageContextMenu({
|
||||
</Tippy>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
export default MessageContextMenu;
|
||||
|
||||
@@ -52,10 +52,10 @@ type Props = {
|
||||
};
|
||||
const EditMessage: FC<Props> = ({ mid, cancelEdit }) => {
|
||||
const inputRef = useRef<HTMLTextAreaElement>(null);
|
||||
const msg = useAppSelector((store) => store.message[mid] || {});
|
||||
const msg = useAppSelector((store) => store.message[mid]);
|
||||
const [shift, setShift] = useState(false);
|
||||
const [enter, setEnter] = useState(false);
|
||||
const [currMsg, setCurrMsg] = useState(msg.content);
|
||||
const [currMsg, setCurrMsg] = useState(msg?.content);
|
||||
const [edit, { isLoading: isEditing, isSuccess }] = useEditMessageMutation();
|
||||
useEffect(() => {
|
||||
if (isSuccess) {
|
||||
|
||||
@@ -12,9 +12,9 @@ const StyledFav = styled.div`
|
||||
background-color: #f4f4f5;
|
||||
`;
|
||||
type Props = {
|
||||
id?: number;
|
||||
id?: string;
|
||||
};
|
||||
const FavoredMessage: FC<Props> = ({ id }) => {
|
||||
const FavoredMessage: FC<Props> = ({ id = "" }) => {
|
||||
const { favorites } = useFavMessage({});
|
||||
const [msgs, setMsgs] = useState<ReactElement | null>(null);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useState, FC, ReactElement } from "react";
|
||||
import styled from "styled-components";
|
||||
import StyledMsg from "./styled";
|
||||
import renderContent from "./renderContent";
|
||||
@@ -29,9 +29,15 @@ const StyledForward = styled.div`
|
||||
color: #98a2b3;
|
||||
}
|
||||
`;
|
||||
const ForwardedMessage = ({ context, to, from_uid, id }) => {
|
||||
type Props = {
|
||||
context: "user" | "channel";
|
||||
to: number;
|
||||
from_uid: number;
|
||||
id: string;
|
||||
};
|
||||
const ForwardedMessage: FC<Props> = ({ context, to, from_uid, id }) => {
|
||||
const { normalizeMessage, messages } = useNormalizeMessage();
|
||||
const [forwards, setForwards] = useState(null);
|
||||
const [forwards, setForwards] = useState<ReactElement | null>(null);
|
||||
useEffect(() => {
|
||||
if (id) {
|
||||
normalizeMessage(id);
|
||||
@@ -50,15 +56,7 @@ const ForwardedMessage = ({ context, to, from_uid, id }) => {
|
||||
</h4>
|
||||
<div className="list">
|
||||
{messages.map((msg, idx) => {
|
||||
const {
|
||||
user = {},
|
||||
// created_at,
|
||||
download,
|
||||
content,
|
||||
content_type,
|
||||
properties,
|
||||
thumbnail
|
||||
} = msg;
|
||||
const { user = {}, download, content, content_type, properties, thumbnail } = msg;
|
||||
return (
|
||||
<StyledMsg className="archive" key={idx}>
|
||||
{user && (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FC, ReactNode } from "react";
|
||||
// import { FC, ReactNode } from "react";
|
||||
import Tippy from "@tippyjs/react";
|
||||
import styled from "styled-components";
|
||||
import Profile from "../Profile";
|
||||
@@ -19,7 +19,7 @@ interface Props {
|
||||
textOnly?: boolean;
|
||||
}
|
||||
|
||||
const Mention: FC<Props> = ({ uid, popover = true, cid, textOnly = false }) => {
|
||||
const Mention = ({ uid, popover = true, cid, textOnly = false }: Props) => {
|
||||
const usersData = useAppSelector((store) => store.users.byId);
|
||||
const user = usersData[uid];
|
||||
if (!user) return null;
|
||||
|
||||
@@ -14,7 +14,7 @@ const PreviewMessage: FC<Props> = ({ mid = 0 }) => {
|
||||
return { msg: store.message[mid], usersData: store.users.byId };
|
||||
});
|
||||
if (!msg) return null;
|
||||
const { from_uid, created_at, content_type, content, thumbnail, properties } = msg;
|
||||
const { from_uid, created_at, content_type, content, thumbnail = "", properties } = msg;
|
||||
const { name, avatar } = usersData[from_uid] || {};
|
||||
return (
|
||||
<StyledWrapper className={`preview`}>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { FC } from "react";
|
||||
import styled from "styled-components";
|
||||
import { getEmojiDataFromNative } from "emoji-mart";
|
||||
import AppleEmojiData from "emoji-mart/data/apple.json";
|
||||
import Tippy from "@tippyjs/react";
|
||||
import { hideAll } from "tippy.js";
|
||||
import ReactionItem from "../ReactionItem";
|
||||
import ReactionItem, { Emojis } from "../ReactionItem";
|
||||
import ReactionPicker from "./ReactionPicker";
|
||||
import Tooltip from "../Tooltip";
|
||||
import { useReactMessageMutation } from "../../../app/services/message";
|
||||
@@ -104,12 +105,20 @@ const StyledDetails = styled.div`
|
||||
color: #1d2939;
|
||||
}
|
||||
`;
|
||||
const ReactionDetails = ({ uids = [], emoji, index }) => {
|
||||
const ReactionDetails = ({
|
||||
uids = [],
|
||||
emoji,
|
||||
index
|
||||
}: {
|
||||
uids: number[];
|
||||
emoji?: keyof Emojis;
|
||||
index: number;
|
||||
}) => {
|
||||
const usersData = useAppSelector((store) => store.users.byId);
|
||||
const names = uids.map((id) => {
|
||||
return usersData[id]?.name;
|
||||
});
|
||||
const emojiData = getEmojiDataFromNative(emoji, "apple", AppleEmojiData);
|
||||
const emojiData = getEmojiDataFromNative(emoji || "", "apple", AppleEmojiData);
|
||||
const prefixDesc =
|
||||
names.length > 3
|
||||
? `${names.join(", ")} and ${names.length - 3} others reacted with`
|
||||
@@ -127,14 +136,20 @@ const ReactionDetails = ({ uids = [], emoji, index }) => {
|
||||
</StyledDetails>
|
||||
);
|
||||
};
|
||||
export default function Reaction({ mid, reactions = null }) {
|
||||
type Props = {
|
||||
mid: number;
|
||||
reactions?: {
|
||||
[key in keyof Emojis]: number[];
|
||||
};
|
||||
};
|
||||
const Reaction: FC<Props> = ({ mid, reactions = null }) => {
|
||||
const [reactWithEmoji] = useReactMessageMutation();
|
||||
const { currUid } = useAppSelector((store) => {
|
||||
return {
|
||||
currUid: store.authData.user?.uid
|
||||
};
|
||||
});
|
||||
const handleReact = (emoji) => {
|
||||
const handleReact = (emoji: string) => {
|
||||
reactWithEmoji({ mid, action: emoji });
|
||||
};
|
||||
console.log("curr reactions", reactions);
|
||||
@@ -178,4 +193,5 @@ export default function Reaction({ mid, reactions = null }) {
|
||||
</Tooltip>
|
||||
</StyledWrapper>
|
||||
);
|
||||
}
|
||||
};
|
||||
export default Reaction;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { FC } from "react";
|
||||
import styled from "styled-components";
|
||||
import { useReactMessageMutation } from "../../../app/services/message";
|
||||
import { Emojis } from "../../../app/config";
|
||||
@@ -34,8 +35,11 @@ const StyledPicker = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default function ReactionPicker({ mid, hidePicker }) {
|
||||
type Props = {
|
||||
mid: number;
|
||||
hidePicker: () => void;
|
||||
};
|
||||
const ReactionPicker: FC<Props> = ({ mid, hidePicker }) => {
|
||||
const [reactMessage, { isLoading }] = useReactMessageMutation();
|
||||
const { reactionData, currUid } = useAppSelector((store) => {
|
||||
return {
|
||||
@@ -68,4 +72,5 @@ export default function ReactionPicker({ mid, hidePicker }) {
|
||||
</ul>
|
||||
</StyledPicker>
|
||||
);
|
||||
}
|
||||
};
|
||||
export default ReactionPicker;
|
||||
|
||||
@@ -95,8 +95,6 @@ const Styled = styled.div`
|
||||
color: #555;
|
||||
}
|
||||
}
|
||||
|
||||
/* padding-left: 10px; */
|
||||
`;
|
||||
|
||||
const renderContent = (data) => {
|
||||
@@ -162,11 +160,11 @@ const Reply: FC<ReplyProps> = ({ mid, interactive = true }) => {
|
||||
const { mid } = evt.currentTarget.dataset;
|
||||
const msgEle = document.querySelector<HTMLDivElement>(`[data-msg-mid='${mid}']`);
|
||||
if (msgEle) {
|
||||
msgEle.dataset.highlight = true;
|
||||
msgEle.dataset.highlight = "true";
|
||||
msgEle.scrollIntoView({ behavior: "smooth", block: "center" });
|
||||
setTimeout(() => {
|
||||
console.log("scroll view", msgEle);
|
||||
msgEle.dataset.highlight = false;
|
||||
msgEle.dataset.highlight = "false";
|
||||
}, 3000);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -8,11 +8,22 @@ import ForwardedMessage from "./ForwardedMessage";
|
||||
import MarkdownRender from "../MarkdownRender";
|
||||
import FileMessage from "../FileMessage";
|
||||
import URLPreview from "./URLPreview";
|
||||
|
||||
type Props = {
|
||||
context: "user" | "channel";
|
||||
to?: number;
|
||||
from_uid?: number;
|
||||
created_at: number;
|
||||
properties?: object;
|
||||
content_type;
|
||||
content: string;
|
||||
download: string;
|
||||
thumbnail: string;
|
||||
edited?: boolean | number;
|
||||
};
|
||||
const renderContent = ({
|
||||
context = null,
|
||||
to = null,
|
||||
from_uid,
|
||||
context,
|
||||
to = 0,
|
||||
from_uid = 0,
|
||||
created_at,
|
||||
properties,
|
||||
content_type,
|
||||
@@ -20,7 +31,7 @@ const renderContent = ({
|
||||
download,
|
||||
thumbnail,
|
||||
edited = false
|
||||
}) => {
|
||||
}: Props) => {
|
||||
let ctn = null;
|
||||
switch (content_type) {
|
||||
case ContentTypes.text:
|
||||
@@ -39,13 +50,13 @@ const renderContent = ({
|
||||
{reactStringReplace(content, /(\s{1}@[0-9]+\s{1})/g, (match, idx) => {
|
||||
console.log("match", match);
|
||||
const uid = match.trim().slice(1);
|
||||
return <Mention key={idx} uid={uid} cid={to} />;
|
||||
return <Mention key={idx} uid={+uid} cid={to} />;
|
||||
})}
|
||||
{/* {content.replace(/\s{1}\@[1-9]+\s{1}/g,)} */}
|
||||
{/* {new RegExp(/\s{1}\@[1-9]+\s{1}/g).exec(content)} */}
|
||||
</Linkit>
|
||||
{edited && (
|
||||
<span className="edited" title={dayjs(edited).format("YYYY-MM-DD h:mm:ss A")}>
|
||||
<span className="edited" title={dayjs(+edited).format("YYYY-MM-DD h:mm:ss A")}>
|
||||
(edited)
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -26,9 +26,9 @@ export default function useMessageOperation({ mid, context, contextId }: Params)
|
||||
};
|
||||
});
|
||||
const { canPin, pins, unpinMessage, isUnpinSuccess } = usePinMessage(
|
||||
context == "channel" ? contextId : undefined
|
||||
context == "channel" ? contextId : 0
|
||||
);
|
||||
const [mids, setMids] = useState([]);
|
||||
const [mids, setMids] = useState<number[]>([]);
|
||||
const [pinModalVisible, setPinModalVisible] = useState(false);
|
||||
const [deleteModalVisible, setDeleteModalVisible] = useState(false);
|
||||
const [forwardModalVisible, setForwardModalVisible] = useState(false);
|
||||
@@ -53,10 +53,9 @@ export default function useMessageOperation({ mid, context, contextId }: Params)
|
||||
// forward message
|
||||
const forwardEle = document.querySelector(
|
||||
`[data-msg-mid='${mid}'] .down [data-forwarded-mids]`
|
||||
);
|
||||
) as HTMLDivElement;
|
||||
if (forwardEle) {
|
||||
console.log("ddddd", mid, forwardEle.dataset);
|
||||
const mids = forwardEle.dataset.forwardedMids.split(",");
|
||||
const mids = forwardEle.dataset.forwardedMids?.split(",").map((m) => +m) || [];
|
||||
setMids(mids);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
const Styled = styled.div`
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
export default function StyledCombobox({ store }) {
|
||||
console.log("combox wtf", store.get.state());
|
||||
return <Styled>StyledCombobox</Styled>;
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import EmojiHeart from "../../assets/icons/emoji.heart.svg";
|
||||
import EmojiRocket from "../../assets/icons/emoji.rocket.svg";
|
||||
import EmojiLook from "../../assets/icons/emoji.look.svg";
|
||||
|
||||
interface Emojis {
|
||||
export interface Emojis {
|
||||
"👍": ReactElement;
|
||||
"👎": ReactElement;
|
||||
"😄": ReactElement;
|
||||
@@ -31,11 +31,11 @@ const emojis: Emojis = {
|
||||
};
|
||||
|
||||
interface Props {
|
||||
native?: keyof Emojis;
|
||||
native: keyof Emojis;
|
||||
}
|
||||
|
||||
const ReactionItem: FC<Props> = ({ native }) => {
|
||||
if (!native) return null;
|
||||
// if (!native) return null;
|
||||
return emojis[native] ?? null;
|
||||
};
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import pictureIcon from "../../../assets/icons/picture.svg?url";
|
||||
import { getFileIcon, isImage } from "../../utils";
|
||||
import useSendMessage from "../../hook/useSendMessage";
|
||||
import { useAppSelector } from "../../../app/store";
|
||||
import { MessagePayload } from "../../../app/slices/message";
|
||||
|
||||
const Styled = styled.div`
|
||||
background-color: #f3f4f6;
|
||||
@@ -74,10 +75,9 @@ const Styled = styled.div`
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
/* transform: translateY(-50%); */
|
||||
}
|
||||
`;
|
||||
const renderContent = (data) => {
|
||||
const renderContent = (data: MessagePayload) => {
|
||||
const { content_type, content, thumbnail = "", properties } = data;
|
||||
let res = null;
|
||||
switch (content_type) {
|
||||
@@ -120,7 +120,15 @@ const renderContent = (data) => {
|
||||
return res;
|
||||
};
|
||||
|
||||
export default function Replying({ context, id, mid }) {
|
||||
export default function Replying({
|
||||
context,
|
||||
id,
|
||||
mid
|
||||
}: {
|
||||
context: "user" | "channel";
|
||||
id: number;
|
||||
mid: number;
|
||||
}) {
|
||||
const { removeReplying } = useSendMessage({ to: id, context });
|
||||
const { msg, usersData } = useAppSelector((store) => {
|
||||
return { usersData: store.users.byId, msg: store.message[mid] };
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ChangeEvent, useRef } from "react";
|
||||
import { ChangeEvent, useRef, FC } from "react";
|
||||
import styled from "styled-components";
|
||||
import Tooltip from "../Tooltip";
|
||||
import AddIcon from "../../../assets/icons/add.solid.svg";
|
||||
@@ -42,15 +42,22 @@ const Styled = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default function Toolbar({
|
||||
type Props = {
|
||||
toggleMarkdownFullscreen: () => void;
|
||||
fullscreen: boolean;
|
||||
toggleMode: () => void;
|
||||
mode: "markdown" | "text";
|
||||
to: number;
|
||||
context: "user" | "channel";
|
||||
};
|
||||
const Toolbar: FC<Props> = ({
|
||||
toggleMarkdownFullscreen,
|
||||
fullscreen,
|
||||
toggleMode,
|
||||
mode,
|
||||
to,
|
||||
context
|
||||
}) {
|
||||
}) => {
|
||||
const { addStageFile } = useUploadFile({ context, id: to });
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const handleUpload = (evt: ChangeEvent<HTMLInputElement>) => {
|
||||
@@ -105,4 +112,5 @@ export default function Toolbar({
|
||||
</Tooltip>
|
||||
</Styled>
|
||||
);
|
||||
}
|
||||
};
|
||||
export default Toolbar;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState } from "react";
|
||||
import { ChangeEvent, useState } from "react";
|
||||
import styled from "styled-components";
|
||||
import Modal from "../../Modal";
|
||||
import Button from "../../styled/Button";
|
||||
@@ -48,9 +48,17 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
export default function EditFileDetails({ name, closeModal, updateName }) {
|
||||
export default function EditFileDetails({
|
||||
name,
|
||||
closeModal,
|
||||
updateName
|
||||
}: {
|
||||
name: string;
|
||||
closeModal: () => void;
|
||||
updateName: (name: string) => void;
|
||||
}) {
|
||||
const [fileName, setFileName] = useState(name);
|
||||
const handleNameChange = (evt) => {
|
||||
const handleNameChange = (evt: ChangeEvent<HTMLInputElement>) => {
|
||||
setFileName(evt.target.value);
|
||||
};
|
||||
const handleUpdate = () => {
|
||||
|
||||
@@ -7,24 +7,34 @@ import useUploadFile from "../../../hook/useUploadFile";
|
||||
import EditIcon from "../../../../assets/icons/edit.svg";
|
||||
import DeleteIcon from "../../../../assets/icons/delete.svg";
|
||||
|
||||
export default function UploadFileList({ context = "", id = null }) {
|
||||
type EditProps = {
|
||||
index: number;
|
||||
name: string;
|
||||
};
|
||||
export default function UploadFileList({
|
||||
context,
|
||||
id
|
||||
}: {
|
||||
context: "user" | "channel";
|
||||
id: number;
|
||||
}) {
|
||||
const eidtor = useMixedEditor(`${context}_${id}`);
|
||||
const [editInfo, setEditInfo] = useState(null);
|
||||
const [editInfo, setEditInfo] = useState<EditProps | null>(null);
|
||||
const { stageFiles, updateStageFile, removeStageFile } = useUploadFile({
|
||||
context,
|
||||
id
|
||||
});
|
||||
const toggleModalVisible = (info) => {
|
||||
const toggleModalVisible = (info: EditProps) => {
|
||||
setEditInfo((prev) => (prev ? null : info));
|
||||
};
|
||||
const handleOpenEditModal = (idx) => {
|
||||
const handleOpenEditModal = (idx: number) => {
|
||||
const info = stageFiles[idx];
|
||||
if (!info) return;
|
||||
|
||||
toggleModalVisible({ ...info, index: idx });
|
||||
};
|
||||
const updateFileName = (name) => {
|
||||
if (!name) return;
|
||||
const updateFileName = (name: string) => {
|
||||
if (!name || !editInfo) return;
|
||||
const { index } = editInfo;
|
||||
updateStageFile(index, { name });
|
||||
};
|
||||
|
||||
@@ -17,7 +17,7 @@ export default function useFavMessage({
|
||||
return { favs: store.favorites };
|
||||
});
|
||||
|
||||
const addFavorite = async (mid = []) => {
|
||||
const addFavorite = async (mid: number | number[]) => {
|
||||
const mids = Array.isArray(mid) ? mid.map((i) => +i) : [+mid];
|
||||
if (mids.length == 0) return;
|
||||
const resp = await addFav(mids);
|
||||
@@ -29,7 +29,7 @@ export default function useFavMessage({
|
||||
removeFav(id);
|
||||
};
|
||||
|
||||
const isFavorited = (mid = null) => {
|
||||
const isFavorited = (mid = 0) => {
|
||||
if (!mid) return false;
|
||||
let mids: number[] = [];
|
||||
favorites.forEach((f: Favorite) => {
|
||||
|
||||
Reference in New Issue
Block a user