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