refactor: replace image message with file message

This commit is contained in:
zerosoul
2022-04-12 09:37:46 +08:00
parent 1dcf757920
commit 5884c73bad
18 changed files with 285 additions and 262 deletions
+4 -4
View File
@@ -6,22 +6,22 @@ import "@toast-ui/editor-plugin-code-syntax-highlight/dist/toastui-editor-plugin
import codeSyntaxHighlight from "@toast-ui/editor-plugin-code-syntax-highlight/dist/toastui-editor-plugin-code-syntax-highlight-all.js";
import StyledWrapper from "./styled";
import { useUploadImageMutation } from "../../../app/services/message";
import useUploadFile from "../../hook/useUploadFile";
import Button from "../../component/styled/Button";
function MarkdownEditor({ placeholder, sendMarkdown, setEditorInstance }) {
const editorRef = useRef(undefined);
const { uploadFile } = useUploadFile();
// const [pHolder, setPHolder] = useState(placeholder);
const [uploadImage] = useUploadImageMutation();
useEffect(() => {
const editor = editorRef?.current;
if (editor) {
const editorInstance = editor.getInstance();
editorInstance.removeHook("addImageBlobHook");
editorInstance.addHook("addImageBlobHook", async (blob, callback) => {
const { data: url } = await uploadImage(blob);
callback(url);
const { thumbnail = "" } = await uploadFile(blob);
callback(thumbnail);
});
setEditorInstance(editorInstance);
}
+12 -12
View File
@@ -4,7 +4,7 @@ import { useSelector } from "react-redux";
import MrakdownRender from "../MrakdownRender";
import { ContentTypes } from "../../../app/config";
import { getFileIcon } from "../../utils";
import { getFileIcon, isImage } from "../../utils";
import Avatar from "../Avatar";
const Styled = styled.div`
cursor: pointer;
@@ -98,20 +98,20 @@ const renderContent = (data) => {
</div>
);
break;
case ContentTypes.image:
case ContentTypes.imageJPG:
res = <img className="pic" src={thumbnail} />;
break;
case ContentTypes.file:
{
const { file_type, name } = properties;
const { file_type, name, size } = properties;
const icon = getFileIcon(file_type, name);
res = (
<>
{icon}
<span className="file_name">{name}</span>
</>
);
if (isImage(file_type, size)) {
res = <img className="pic" src={thumbnail} />;
} else {
res = (
<>
{icon}
<span className="file_name">{name}</span>
</>
);
}
}
break;
+30 -27
View File
@@ -6,7 +6,7 @@ import Linkit from "react-linkify";
import dayjs from "dayjs";
import Mention from "./Mention";
import MrakdownRender from "../MrakdownRender";
import { getDefaultSize } from "../../utils";
import { getDefaultSize, isImage } from "../../utils";
import FileBox from "../FileBox";
import URLPreview from "./URLPreview";
import reactStringReplace from "react-string-replace";
@@ -68,35 +68,38 @@ const renderContent = ({
ctn = <MrakdownRender content={content} />;
}
break;
case "image/png":
case "image/jpeg":
{
const { name, size, type } = properties;
const { width, height } = getDefaultSize(properties);
ctn = (
<img
className="img preview"
style={{ width: `${width}px`, height: `${height}px` }}
data-meta={JSON.stringify({ width, height, name, type, size })}
data-origin={content}
src={thumbnail || content}
/>
);
}
break;
case "rustchat/file":
{
const { size, name, file_type } = properties;
ctn = (
<FileBox
from_uid={from_uid}
created_at={created_at}
content={content}
size={size}
name={name}
file_type={file_type}
/>
);
if (isImage(file_type, size)) {
const { width, height } = getDefaultSize(properties);
ctn = (
<img
className="img preview"
style={{ width: `${width}px`, height: `${height}px` }}
data-meta={JSON.stringify({
width,
height,
name,
file_type,
size,
})}
data-origin={content}
src={thumbnail || content}
/>
);
} else {
ctn = (
<FileBox
from_uid={from_uid}
created_at={created_at}
content={content}
size={size}
name={name}
file_type={file_type}
/>
);
}
}
break;
+89 -61
View File
@@ -27,6 +27,7 @@ import Styled from "./styled";
import ImageElement from "./ImageElement";
import { CONFIG } from "./config";
import Contact from "../Contact";
import useUploadFile from "../../hook/useUploadFile";
// import Mention from "./Mention";
export const TEXT_EDITOR_PREFIX = "rustchat_text_editor";
export const setEditorFocus = (edtr) => {
@@ -55,6 +56,9 @@ const Plugins = ({
sendMessages,
members = [],
}) => {
const enableMentions = members.length > 0;
const { uploadFile } = useUploadFile();
const filesRef = useRef([]);
// const plateEditor = usePlateEditorRef(`${TEXT_EDITOR_PREFIX}_${id}`);
const contactData = useSelector((store) => store.contacts.byId);
const [msgs, setMsgs] = useState([]);
@@ -66,6 +70,7 @@ const Plugins = ({
className: "box",
placeholder,
};
useKey(
"Enter",
(evt) => {
@@ -96,36 +101,44 @@ const Plugins = ({
target: editableRef,
}
);
const plugins = createPlugins(
[
createParagraphPlugin(),
createImagePlugin(),
createNodeIdPlugin(),
createSoftBreakPlugin(CONFIG.softBreak),
createTrailingBlockPlugin(CONFIG.trailingBlock),
createExitBreakPlugin(CONFIG.exitBreak),
createComboboxPlugin(),
createMentionPlugin({
// component: Mention,
// handlers: {
// onKeyDown: ({ query }) => {
// console.log("mention", query);
// return true;
// },
// },
options: {
createMentionNode: (item) => {
console.log("mention", item);
const {
text,
data: { uid },
} = item;
return { value: `@${text}`, uid };
},
insertSpaceAfterMention: true,
const pluginArr = [
createParagraphPlugin(),
createImagePlugin({
options: {
uploadImage: async (dataUrl) => {
const resp = await fetch(dataUrl);
const blob = await resp.blob();
const { thumbnail, ...rest } = await uploadFile(blob);
const { name, file_type, size, path, hash } = rest;
filesRef.current.push({ name, file_type, size, path, hash });
return thumbnail;
},
}),
],
},
}),
createNodeIdPlugin(),
createSoftBreakPlugin(CONFIG.softBreak),
createTrailingBlockPlugin(CONFIG.trailingBlock),
createExitBreakPlugin(CONFIG.exitBreak),
];
const plugins = createPlugins(
enableMentions
? pluginArr.concat([
createComboboxPlugin(),
createMentionPlugin({
options: {
createMentionNode: (item) => {
console.log("mention", item);
const {
text,
data: { uid },
} = item;
return { value: `@${text}`, uid };
},
insertSpaceAfterMention: true,
},
}),
])
: pluginArr,
{
components,
}
@@ -146,27 +159,40 @@ const Plugins = ({
});
return { value: arr.join(""), mentions };
};
for await (const v of val) {
for (const v of val) {
if (v.type == "img") {
// img
const resp = await fetch(v.url);
const value = await resp.blob();
tmps.push({ type: "image", value });
const url = v.url;
const file_path = decodeURIComponent(
new URL(url).searchParams.get("file_path")
);
console.log("files", filesRef.current, file_path);
const json = filesRef.current.find((f) => f.path == file_path) || {};
const { name, size, hash, path, ...rest } = json;
tmps.push({
type: "file",
content: { name, size, hash, path },
properties: rest,
});
} else {
// p
const { value, mentions } = getMixedText(v.children);
const prev = tmps[tmps.length - 1];
if (!prev) {
tmps.push([{ type: "text", value, mentions }]);
tmps.push([
{ type: "text", content: value, properties: { mentions } },
]);
} else {
if (Array.isArray(prev)) {
tmps[tmps.length - 1].push({
type: "text",
value,
mentions,
content: value,
properties: { mentions },
});
} else {
tmps.push([{ type: "text", value, mentions }]);
tmps.push([
{ type: "text", content: value, properties: { mentions } },
]);
}
}
}
@@ -175,14 +201,14 @@ const Plugins = ({
return Array.isArray(tmp)
? {
type: "text",
value: tmp.map((t) => t.value).join("\n"),
mentions: tmp.map((t) => t.mentions).flat(),
content: tmp.map((t) => t.content).join("\n"),
properties: { mentions: tmp.map((t) => t.mentions).flat() },
}
: tmp;
});
const msgs = arr.filter(({ value }) => !!value);
const msgs = arr.filter(({ content }) => !!content);
setMsgs(msgs);
console.log("tmps", val, tmps, msgs);
console.log("tmps", tmps, arr, msgs);
},
[msgs]
);
@@ -197,26 +223,28 @@ const Plugins = ({
initialValue={initialValue}
plugins={plugins}
>
<MentionCombobox
// component={StyledCombobox}
onRenderItem={({ item }) => {
console.log("wtf", item);
return <Contact uid={item.data.uid} interactive={false} />;
}}
items={members.map((id) => {
const data = contactData[id];
if (!data) return null;
const { uid, name, ...rest } = data;
return {
key: uid,
text: name,
data: {
uid,
...rest,
},
};
})}
/>
{enableMentions ? (
<MentionCombobox
// component={StyledCombobox}
onRenderItem={({ item }) => {
console.log("wtf", item);
return <Contact uid={item.data.uid} interactive={false} />;
}}
items={members.map((id) => {
const data = contactData[id];
if (!data) return null;
const { uid, name, ...rest } = data;
return {
key: uid,
text: name,
data: {
uid,
...rest,
},
};
})}
/>
) : null}
</Plate>
</Styled>
);
+6 -1
View File
@@ -34,10 +34,15 @@ export default function MrakdownRender({ content }) {
"click",
(evt) => {
console.log(evt);
evt.stopPropagation();
const { target } = evt;
// 图片
if (target.nodeName == "IMG") {
const data = { originUrl: target.dataset.origin || target.src };
const urlObj = new URL(target.src);
const originUrl = `${urlObj.origin}${
urlObj.pathname
}?file_path=${urlObj.searchParams.get("file_path")}`;
const data = { originUrl };
setPreviewImage(data);
}
},
+13 -13
View File
@@ -4,7 +4,7 @@ import { ContentTypes } from "../../../app/config";
import MrakdownRender from "../MrakdownRender";
import closeIcon from "../../../assets/icons/close.circle.svg?url";
import pictureIcon from "../../../assets/icons/picture.svg?url";
import { getFileIcon } from "../../utils";
import { getFileIcon, isImage } from "../../utils";
import { removeReplyingMessage } from "../../../app/slices/message";
import styled from "styled-components";
const Styled = styled.div`
@@ -97,20 +97,20 @@ const renderContent = (data) => {
</div>
);
break;
case ContentTypes.image:
case ContentTypes.imageJPG:
res = <img className="pic" src={pictureIcon} />;
break;
case ContentTypes.file:
{
const { file_type, name } = properties;
const icon = getFileIcon(file_type, name);
res = (
<>
{icon}
<span className="name">{name}</span>
</>
);
const { file_type, name, size } = properties;
if (isImage(file_type, size)) {
res = <img className="pic" src={pictureIcon} />;
} else {
const icon = getFileIcon(file_type, name);
res = (
<>
{icon}
<span className="name">{name}</span>
</>
);
}
}
break;
default:
+3 -2
View File
@@ -75,7 +75,8 @@ function Send({
const handleSendMessage = async (msgs = []) => {
if (!msgs || msgs.length == 0 || !id) return;
for await (const msg of msgs) {
const { type: content_type, value: content, mentions = [] } = msg;
console.log("send msg", msg);
const { type: content_type, content, properties = {} } = msg;
if (replying_mid) {
console.log("replying", replying_mid);
await replyMessage({
@@ -93,7 +94,7 @@ function Send({
type: content_type,
content,
from_uid,
properties: { local_id: new Date().getTime(), mentions },
properties,
});
}
}
+29 -28
View File
@@ -1,12 +1,12 @@
import { useEffect } from "react";
import { useSelector } from "react-redux";
import FileItem from "./FileItem";
import useSendImageMessage from "../../hook/useSendImageMessage";
import useSendFileMessage from "../../hook/useSendFileMessage";
import useUploadFile from "../../hook/useUploadFile";
import Modal from "../Modal";
import Button from "../styled/Button";
import { isTreatAsImage } from "../../utils";
// import { isTreatAsImage } from "../../utils";
import StyledWrapper from "./styled";
import useSendMessage from "../../hook/useSendMessage";
export default function UploadModal({
context = "user",
@@ -16,42 +16,45 @@ export default function UploadModal({
}) {
const from_uid = useSelector((store) => store.authData.uid);
const {
sendImageMessage,
isSending: isSendingImage,
isSuccess: sendImageSuccess,
} = useSendImageMessage({
sendMessage,
isSuccess: sendMessageSuccess,
isSending,
} = useSendMessage({
context,
from: from_uid,
to: sendTo,
});
const {
sendFileMessage,
data,
uploadFile,
progress,
isSending: isSendingFile,
isSuccess: sendFileSuccess,
} = useSendFileMessage({
context,
from: from_uid,
to: sendTo,
});
isUploading,
isSuccess: uploadSuccess,
} = useUploadFile();
const handleUpload = () => {
const file = files[0];
// const { type } = file;
if (isTreatAsImage(file)) {
sendImageMessage(file);
} else {
sendFileMessage(file);
}
uploadFile(file);
};
useEffect(() => {
if (sendFileSuccess || sendImageSuccess) {
if (uploadSuccess) {
// 把已经上传的东西当做消息发出去
const { size, path, name, hash, ...rest } = data;
sendMessage({
type: "file",
content: { size, name, path, hash },
properties: rest,
});
}
}, [uploadSuccess, data]);
useEffect(() => {
if (sendMessageSuccess) {
closeModal();
}
}, [sendImageSuccess, sendFileSuccess]);
}, [sendMessageSuccess]);
if (!sendTo) return null;
console.log("upload file modal", files, sendTo);
const isSending = isSendingFile || isSendingImage;
const sending = isUploading || isSending;
return (
<Modal>
<StyledWrapper
@@ -64,12 +67,10 @@ export default function UploadModal({
</Button>
<Button
className="upload"
disabled={isSending}
disabled={sending}
onClick={handleUpload}
>
{isSending
? `Uploading (${Math.floor(progress * 100)}%)`
: `Upload`}
{sending ? `Uploading (${progress}%)` : `Upload`}
</Button>
</>
}