refactor: add typescript support to project
This commit is contained in:
+4
-4
@@ -3,11 +3,10 @@ import { useDispatch } from "react-redux";
|
||||
import styled from "styled-components";
|
||||
import Tippy from "@tippyjs/react";
|
||||
import { hideAll } from "tippy.js";
|
||||
import toast from "react-hot-toast";
|
||||
import { updateSelectMessages } from "../../../app/slices/ui";
|
||||
// import StyledMenu from "../styled/Menu";
|
||||
import ContextMenu from "../ContextMenu";
|
||||
import Tooltip from "../../component/Tooltip";
|
||||
|
||||
import Tooltip from "../Tooltip";
|
||||
import useFavMessage from "../../hook/useFavMessage";
|
||||
import useSendMessage from "../../hook/useSendMessage";
|
||||
import ReactionPicker from "./ReactionPicker";
|
||||
@@ -20,8 +19,8 @@ import IconForward from "../../../assets/icons/forward.svg";
|
||||
import IconSelect from "../../../assets/icons/select.svg";
|
||||
import IconDelete from "../../../assets/icons/delete.svg";
|
||||
import moreIcon from "../../../assets/icons/more.svg?url";
|
||||
import toast from "react-hot-toast";
|
||||
import useMessageOperation from "./useMessageOperation";
|
||||
|
||||
const StyledCmds = styled.ul`
|
||||
z-index: 999;
|
||||
position: absolute;
|
||||
@@ -62,6 +61,7 @@ const StyledCmds = styled.ul`
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
`;
|
||||
|
||||
export default function Commands({ context = "user", contextId = 0, mid = 0, toggleEditMessage }) {
|
||||
const {
|
||||
canDelete,
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// import { useState } from "react";
|
||||
import Tippy from "@tippyjs/react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import ContextMenu from "../ContextMenu";
|
||||
+5
-2
@@ -2,9 +2,10 @@ import { useState, useRef, useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import TextareaAutosize from "react-textarea-autosize";
|
||||
import { useKey } from "rooks";
|
||||
import { useEditMessageMutation } from "../../../app/services/message";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useEditMessageMutation } from "../../../app/services/message";
|
||||
import { ContentTypes } from "../../../app/config";
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
width: 100%;
|
||||
.input {
|
||||
@@ -45,8 +46,9 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default function EditMessage({ mid, cancelEdit }) {
|
||||
const inputRef = useRef();
|
||||
const inputRef = useRef<HTMLTextAreaElement>(null);
|
||||
const msg = useSelector((store) => store.message[mid] || {});
|
||||
const [shift, setShift] = useState(false);
|
||||
const [enter, setEnter] = useState(false);
|
||||
@@ -95,6 +97,7 @@ export default function EditMessage({ mid, cancelEdit }) {
|
||||
});
|
||||
};
|
||||
if (!msg) return null;
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<div className="input">
|
||||
+2
-2
@@ -1,17 +1,17 @@
|
||||
import { useEffect, useState } from "react";
|
||||
// import dayjs from "dayjs";
|
||||
|
||||
import styled from "styled-components";
|
||||
import StyledMsg from "./styled";
|
||||
import renderContent from "./renderContent";
|
||||
import Avatar from "../Avatar";
|
||||
import useFavMessage from "../../hook/useFavMessage";
|
||||
|
||||
const StyledFav = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: var(--br);
|
||||
background-color: #f4f4f5;
|
||||
`;
|
||||
|
||||
const FavoritedMessage = ({ id }) => {
|
||||
const { favorites } = useFavMessage({});
|
||||
const [msgs, setMsgs] = useState(null);
|
||||
+1
@@ -5,6 +5,7 @@ import renderContent from "./renderContent";
|
||||
import Avatar from "../Avatar";
|
||||
import IconForward from "../../../assets/icons/forward.svg";
|
||||
import useNormalizeMessage from "../../hook/useNormalizeMessage";
|
||||
|
||||
const StyledForward = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -1,8 +1,9 @@
|
||||
// import React from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { FC } from "react";
|
||||
import Tippy from "@tippyjs/react";
|
||||
import Profile from "../Profile";
|
||||
import styled from "styled-components";
|
||||
import Profile from "../Profile";
|
||||
import { useAppSelector } from "../../../app/store";
|
||||
|
||||
const Styled = styled.span`
|
||||
padding: 0 2px;
|
||||
color: #1fe1f9;
|
||||
@@ -10,8 +11,16 @@ const Styled = styled.span`
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
export default function Mention({ uid, popover = true, cid, textOnly = false }) {
|
||||
const contactsData = useSelector((store) => store.contacts.byId);
|
||||
|
||||
interface Props {
|
||||
uid: number;
|
||||
popover?: boolean;
|
||||
cid: number;
|
||||
textOnly?: boolean;
|
||||
}
|
||||
|
||||
const Mention: FC<Props> = ({ uid, popover = true, cid, textOnly = false }) => {
|
||||
const contactsData = useAppSelector((store) => store.contacts.byId);
|
||||
const user = contactsData[uid];
|
||||
if (!user) return null;
|
||||
if (textOnly) return `@${user.name}`;
|
||||
@@ -26,4 +35,6 @@ export default function Mention({ uid, popover = true, cid, textOnly = false })
|
||||
<Styled className={popover ? "clickable" : ""}>{`@${user.name}`}</Styled>
|
||||
</Tippy>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Mention;
|
||||
+20
-10
@@ -1,6 +1,13 @@
|
||||
// import React from "react";
|
||||
import { FC } from "react";
|
||||
import { useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import toast from "react-hot-toast";
|
||||
import usePinMessage from "../../hook/usePinMessage";
|
||||
import StyledModal from "../styled/Modal";
|
||||
import Button from "../styled/Button";
|
||||
import Modal from "../Modal";
|
||||
import PreviewMessage from "./PreviewMessage";
|
||||
|
||||
const StyledPinModal = styled(StyledModal)`
|
||||
min-width: 406px;
|
||||
.title,
|
||||
@@ -15,24 +22,25 @@ const StyledPinModal = styled(StyledModal)`
|
||||
overflow-x: hidden;
|
||||
}
|
||||
`;
|
||||
import usePinMessage from "../../hook/usePinMessage";
|
||||
import StyledModal from "../styled/Modal";
|
||||
import Button from "../styled/Button";
|
||||
import Modal from "../Modal";
|
||||
import PreviewMessage from "./PreviewMessage";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
export default function PinMessageModal({ closeModal, mid = 0, gid = 0 }) {
|
||||
// const dispatch = useDispatch();
|
||||
interface Props {
|
||||
closeModal: () => void;
|
||||
mid: number;
|
||||
gid: number;
|
||||
}
|
||||
|
||||
const PinMessageModal: FC<Props> = ({ closeModal, mid = 0, gid = 0 }) => {
|
||||
const { channel, pinMessage, isPining, isSuccess } = usePinMessage(gid);
|
||||
const handlePin = () => {
|
||||
pinMessage(mid);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isSuccess) {
|
||||
closeModal();
|
||||
toast.success("Pin Message Successfully");
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isSuccess]);
|
||||
|
||||
if (!mid) return null;
|
||||
@@ -57,4 +65,6 @@ export default function PinMessageModal({ closeModal, mid = 0, gid = 0 }) {
|
||||
</StyledPinModal>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default PinMessageModal;
|
||||
+3
-3
@@ -1,11 +1,11 @@
|
||||
// import { useEffect, useRef, useState } from "react";
|
||||
import dayjs from "dayjs";
|
||||
import renderContent from "./renderContent";
|
||||
import Avatar from "../Avatar";
|
||||
import StyledWrapper from "./styled";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useAppSelector } from "../../../app/store";
|
||||
|
||||
export default function PreviewMessage({ mid = 0 }) {
|
||||
const { msg, contactsData } = useSelector((store) => {
|
||||
const { msg, contactsData } = useAppSelector((store) => {
|
||||
return { msg: store.message[mid], contactsData: store.contacts.byId };
|
||||
});
|
||||
if (!msg) return null;
|
||||
+1
-1
@@ -1,4 +1,3 @@
|
||||
// import { useState } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import styled from "styled-components";
|
||||
import { getEmojiDataFromNative } from "emoji-mart";
|
||||
@@ -10,6 +9,7 @@ import ReactionPicker from "./ReactionPicker";
|
||||
import Tooltip from "../Tooltip";
|
||||
import { useReactMessageMutation } from "../../../app/services/message";
|
||||
import addEmojiIcon from "../../../assets/icons/add.emoji.svg?url";
|
||||
|
||||
const StyledWrapper = styled.span`
|
||||
position: relative;
|
||||
margin-top: 8px;
|
||||
+3
-3
@@ -1,9 +1,9 @@
|
||||
import styled from "styled-components";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
import { useReactMessageMutation } from "../../../app/services/message";
|
||||
import { Emojis } from "../../../app/config";
|
||||
import Emoji from "../ReactionItem";
|
||||
import { useAppSelector } from "../../../app/store";
|
||||
|
||||
const StyledPicker = styled.div`
|
||||
background: none;
|
||||
z-index: 999;
|
||||
@@ -38,7 +38,7 @@ const StyledPicker = styled.div`
|
||||
export default function ReactionPicker({ mid, hidePicker }) {
|
||||
// const wrapperRef = useRef(null);
|
||||
const [reactMessage, { isLoading }] = useReactMessageMutation();
|
||||
const { reactionData, currUid } = useSelector((store) => {
|
||||
const { reactionData, currUid } = useAppSelector((store) => {
|
||||
return {
|
||||
reactionData: store.reactionMessage[mid] || {},
|
||||
currUid: store.authData.uid
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from "react";
|
||||
import React, { MouseEvent, FC } from "react";
|
||||
import styled from "styled-components";
|
||||
import { useSelector } from "react-redux";
|
||||
import reactStringReplace from "react-string-replace";
|
||||
import MrakdownRender from "../MrakdownRender";
|
||||
import Mention from "./Mention";
|
||||
@@ -8,6 +7,7 @@ import { ContentTypes } from "../../../app/config";
|
||||
import { getFileIcon, isImage } from "../../utils";
|
||||
|
||||
import Avatar from "../Avatar";
|
||||
import { useAppSelector } from "../../../app/store";
|
||||
const Styled = styled.div`
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
@@ -16,19 +16,23 @@ const Styled = styled.div`
|
||||
border-radius: var(--br);
|
||||
gap: 8px;
|
||||
margin-bottom: 4px;
|
||||
|
||||
&.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
white-space: nowrap;
|
||||
|
||||
.avatar {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
@@ -37,6 +41,7 @@ const Styled = styled.div`
|
||||
color: #06b6d4;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
overflow: hidden;
|
||||
font-weight: 500;
|
||||
@@ -45,20 +50,22 @@ const Styled = styled.div`
|
||||
color: #616161;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.txt {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 1;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
-webkit-box-orient: vertical;
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.md {
|
||||
position: relative;
|
||||
max-height: 152px;
|
||||
overflow: hidden;
|
||||
|
||||
&:after {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
@@ -69,24 +76,29 @@ const Styled = styled.div`
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0) 63.54%, #e5e7eb 93.09%);
|
||||
}
|
||||
}
|
||||
|
||||
.pic {
|
||||
display: inherit;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 15px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.file_name {
|
||||
margin-left: 5px;
|
||||
font-size: 10px;
|
||||
color: #555;
|
||||
}
|
||||
}
|
||||
|
||||
/* padding-left: 10px; */
|
||||
`;
|
||||
|
||||
const renderContent = (data) => {
|
||||
const { content_type, content, thumbnail, properties } = data;
|
||||
let res = null;
|
||||
@@ -136,13 +148,19 @@ const renderContent = (data) => {
|
||||
}
|
||||
return res;
|
||||
};
|
||||
const Reply = ({ mid, interactive = true }) => {
|
||||
const { data, users } = useSelector((store) => {
|
||||
|
||||
interface ReplyProps {
|
||||
mid: number;
|
||||
interactive?: boolean;
|
||||
}
|
||||
|
||||
const Reply: FC<ReplyProps> = ({ mid, interactive = true }) => {
|
||||
const { data, users } = useAppSelector((store) => {
|
||||
return { data: store.message[mid], users: store.contacts.byId };
|
||||
});
|
||||
const handleClick = (evt) => {
|
||||
const handleClick = (evt: MouseEvent<HTMLDivElement>) => {
|
||||
const { mid } = evt.currentTarget.dataset;
|
||||
const msgEle = document.querySelector(`[data-msg-mid='${mid}']`);
|
||||
const msgEle = document.querySelector<HTMLDivElement>(`[data-msg-mid='${mid}']`);
|
||||
if (msgEle) {
|
||||
msgEle.dataset.highlight = true;
|
||||
msgEle.scrollIntoView({ behavior: "smooth", block: "center" });
|
||||
+3
-2
@@ -1,7 +1,7 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import { useLazyGetOGInfoQuery } from "../../../app/services/message";
|
||||
// import favUrl from "../../../assets/icons/favicon.default.svg?url";
|
||||
|
||||
const StyledCompact = styled.a`
|
||||
background: #f3f4f6;
|
||||
border: 1px solid #d4d4d4;
|
||||
@@ -92,12 +92,13 @@ const StyledDetails = styled.a`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default function URLPreview({ url = "" }) {
|
||||
const [favicon, setFavicon] = useState("");
|
||||
const [getInfo] = useLazyGetOGInfoQuery();
|
||||
const [data, setData] = useState(null);
|
||||
useEffect(() => {
|
||||
const getMetaData = async (url) => {
|
||||
const getMetaData = async (url: string) => {
|
||||
// todo
|
||||
const { data } = await getInfo(url);
|
||||
const title = data.title || data.site_name;
|
||||
@@ -15,11 +15,13 @@ import EditMessage from "./EditMessage";
|
||||
import renderContent from "./renderContent";
|
||||
import Tooltip from "../Tooltip";
|
||||
import ContextMenu from "./ContextMenu";
|
||||
|
||||
import useContextMenu from "../../hook/useContextMenu";
|
||||
import usePinMessage from "../../hook/usePinMessage";
|
||||
|
||||
// todo: move to root file
|
||||
dayjs.extend(isToday);
|
||||
dayjs.extend(isYesterday);
|
||||
|
||||
function Message({
|
||||
readOnly = false,
|
||||
contextId = 0,
|
||||
+1
-2
@@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import Linkit from "react-linkify";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
import reactStringReplace from "react-string-replace";
|
||||
import { ContentTypes } from "../../../app/config";
|
||||
import Mention from "./Mention";
|
||||
import ForwardedMessage from "./ForwardedMessage";
|
||||
@@ -9,7 +9,6 @@ import MrakdownRender from "../MrakdownRender";
|
||||
import FileMessage from "../FileMessage";
|
||||
import URLPreview from "./URLPreview";
|
||||
|
||||
import reactStringReplace from "react-string-replace";
|
||||
const renderContent = ({
|
||||
context = null,
|
||||
to = null,
|
||||
@@ -1,4 +1,5 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
const StyledMsg = styled.div`
|
||||
position: relative;
|
||||
display: flex;
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
import { useRef, useEffect } from "react";
|
||||
|
||||
export default function useInView() {
|
||||
const ref = useRef(undefined);
|
||||
export default function useInView<T extends HTMLElement>() {
|
||||
const ref = useRef<T>(null);
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
@@ -17,7 +17,7 @@ export default function useInView() {
|
||||
{ threshold: 0 }
|
||||
);
|
||||
useEffect(() => {
|
||||
const currEle = ref?.current;
|
||||
const currEle = ref.current;
|
||||
if (currEle) {
|
||||
observer.observe(ref.current);
|
||||
}
|
||||
+9
-3
@@ -4,13 +4,19 @@ import DeleteMessageConfirm from "../DeleteMessageConfirm";
|
||||
import ForwardModal from "../ForwardModal";
|
||||
import PinMessageModal from "./PinMessageModal";
|
||||
import { ContentTypes } from "../../../app/config";
|
||||
import { useSelector } from "react-redux";
|
||||
import useCopy from "../../hook/useCopy";
|
||||
import usePinMessage from "../../hook/usePinMessage";
|
||||
import { useAppSelector } from "../../../app/store";
|
||||
|
||||
export default function useMessageOperation({ mid, context, contextId }) {
|
||||
interface Params {
|
||||
mid: number;
|
||||
context: string;
|
||||
contextId: number;
|
||||
}
|
||||
|
||||
export default function useMessageOperation({ mid, context, contextId }: Params) {
|
||||
const { copy } = useCopy();
|
||||
const { content_type, properties, currUid, from_uid, content } = useSelector((store) => {
|
||||
const { content_type, properties, currUid, from_uid, content } = useAppSelector((store) => {
|
||||
return {
|
||||
content: store.message[mid]?.content,
|
||||
from_uid: store.message[mid]?.from_uid,
|
||||
Reference in New Issue
Block a user