refactor: new send input

This commit is contained in:
zerosoul
2022-03-23 16:31:54 +08:00
parent 4e56f15e24
commit b133780061
25 changed files with 727 additions and 859 deletions
+1 -1
View File
@@ -161,7 +161,7 @@ export default function ChannelModal({ personal = false, closeModal }) {
<Button
disabled={isLoading}
onClick={handleCreate}
className="normal main"
className="normal"
>
Create
</Button>
@@ -1,29 +0,0 @@
import * as React from "react";
import ReactMde from "react-mde";
import * as Showdown from "showdown";
import "react-mde/lib/styles/css/react-mde-all.css";
const converter = new Showdown.Converter({
tables: true,
simplifiedAutoLink: true,
strikethrough: true,
tasklists: true,
});
export default function MarkdownEditer() {
const [value, setValue] = React.useState("**Hello world!!!**");
const [selectedTab, setSelectedTab] = React.useState("write");
return (
<div className="container">
<ReactMde
value={value}
onChange={setValue}
selectedTab={selectedTab}
onTabChange={setSelectedTab}
generateMarkdownPreview={(markdown) =>
Promise.resolve(converter.makeHtml(markdown))
}
/>
</div>
);
}
+13 -1
View File
@@ -65,11 +65,23 @@ export default function Reply({ mid }) {
const { data, users } = useSelector((store) => {
return { data: store.message[mid], users: store.contacts.byId };
});
const handleClick = (evt) => {
const { mid } = evt.currentTarget.dataset;
const msgEle = document.querySelector(`[data-msg-mid='${mid}']`);
if (msgEle) {
msgEle.dataset.highlight = true;
msgEle.scrollIntoView({ behavior: "smooth", block: "center" });
setTimeout(() => {
console.log("scroll view", msgEle);
msgEle.dataset.highlight = false;
}, 3000);
}
};
if (!data) return null;
const currUser = users[data.from_uid];
if (!currUser) return null;
return (
<Styled data-mid={mid} className="reply">
<Styled data-mid={mid} className="reply" onClick={handleClick}>
<div className="user">
<Avatar className="avatar" url={currUser.avatar} name={currUser.name} />
<span className="name">{currUser.name}</span>
+1 -1
View File
@@ -70,7 +70,7 @@ function Message({ contextId = 0, mid = "", context = "user" }) {
// if (!message) return null;
return (
<StyledWrapper
data-mid={mid}
data-msg-mid={mid}
ref={myRef}
className={`message ${inView ? "in_view" : ""}`}
>
+3
View File
@@ -12,6 +12,9 @@ const StyledMsg = styled.div`
&.in_view {
content-visibility: visible;
}
&[data-highlight="true"] {
background: #f5f6f7;
}
&:hover,
&.preview {
background: #f5f6f7;
+1 -2
View File
@@ -1,6 +1,5 @@
// import React from 'react'
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import styled from "styled-components";
const Styled = styled.div`
font-family: "Helvetica Neue", sans;
@@ -148,7 +147,7 @@ const Styled = styled.div`
export default function MrakdownRender({ content }) {
return (
<Styled>
<ReactMarkdown remarkPlugins={[remarkGfm]}>{content}</ReactMarkdown>
<ReactMarkdown>{content}</ReactMarkdown>
</Styled>
);
}
+24 -16
View File
@@ -1,25 +1,33 @@
import { useState } from "react";
// import { useState } from "react";
import Tippy from "@tippyjs/react";
import styled from "styled-components";
const StyledBtn = styled.button`
background: none;
display: flex;
align-items: center;
svg {
width: 22px;
height: 22px;
}
`;
import Picker from "../EmojiPicker";
import SmileIcon from "../../../assets/icons/emoji.smile.svg";
export default function EmojiPicker({ selectEmoji }) {
const [emojiPickerVisible, setEmojiPickerVisible] = useState(false);
const toggleEmojiPicker = () => {
setEmojiPickerVisible((prev) => !prev);
};
const handleSelect = (emoji) => {
selectEmoji(emoji.native);
setEmojiPickerVisible(false);
};
return (
<>
<button className="toggle" onClick={toggleEmojiPicker}>
😄
</button>
{emojiPickerVisible && (
<div className="picker">
<Picker onSelect={handleSelect} />
</div>
)}
</>
<Tippy
offset={[-18, 25]}
interactive
placement="top-start"
trigger="click"
content={<Picker onSelect={handleSelect} />}
>
<StyledBtn>
<SmileIcon />
</StyledBtn>
</Tippy>
);
}
-1
View File
@@ -7,7 +7,6 @@ import { removeReplyingMessage } from "../../../app/slices/message";
import styled from "styled-components";
const Styled = styled.div`
z-index: 999;
position: relative;
display: flex;
justify-content: space-between;
align-items: center;
+31 -85
View File
@@ -1,74 +1,37 @@
// import React from 'react'
import { RiMarkdownLine, RiMarkdownFill } from "react-icons/ri";
import styled from "styled-components";
import Button from "../styled/Button";
import EmojiPicker from "./EmojiPicker";
import addIcon from "../../../assets/icons/add.svg?url";
import AddIcon from "../../../assets/icons/add.solid.svg";
import MarkdownIcon from "../../../assets/icons/markdown.svg";
const Styled = styled.div`
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding-bottom: 5px;
.left {
justify-content: flex-end;
gap: 10px;
.md {
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
.md {
cursor: pointer;
}
.add {
cursor: pointer;
position: relative;
width: 16px;
height: 16px;
input {
opacity: 0;
cursor: pointer;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
}
.emoji {
position: relative;
.toggle {
font-size: 22px;
border: none;
background: none;
}
.picker {
position: absolute;
top: -20px;
right: -15px;
transform: translateY(-100%);
}
}
}
.right {
display: flex;
align-items: center;
gap: 8px;
.send {
/* font-size: 16px;
font-weight: bold;
background: none; */
.add {
cursor: pointer;
position: relative;
width: 24px;
height: 24px;
input {
opacity: 0;
cursor: pointer;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
}
.divider {
width: 1px;
height: 16px;
background-color: #ccc;
margin: 0 4px;
}
`;
export default function Toolbar({
contentType = "text",
updateContentType,
handleUpload,
selectEmoji,
handleSend,
}) {
const toggleMarkdown = () => {
@@ -76,35 +39,18 @@ export default function Toolbar({
};
return (
<Styled>
<div className="left">
<div className="add">
<img src={addIcon} />
<input
multiple={true}
onChange={handleUpload}
type="file"
name="file"
id="file"
/>
</div>
<div className="divider"></div>
<div className="emoji">
<EmojiPicker selectEmoji={selectEmoji} />
</div>
<div className="md" onClick={toggleMarkdown}>
{contentType == "markdown" ? (
<RiMarkdownFill size={24} />
) : (
<RiMarkdownLine size={24} />
)}
</div>
<div className="md" onClick={toggleMarkdown}>
{contentType == "markdown" ? <MarkdownIcon /> : <MarkdownIcon />}
</div>
<div className="right">
{contentType == "markdown" && (
<Button className="send main" onClick={handleSend}>
Send
</Button>
)}
<div className="add">
<AddIcon />
<input
multiple={true}
onChange={handleUpload}
type="file"
name="file"
id="file"
/>
</div>
</Styled>
);
@@ -71,7 +71,7 @@ export default function UploadModal({
Cancel
</Button>
<Button
className="upload main"
className="upload"
disabled={channelSending || userSending}
onClick={handleUpload}
>
+24 -69
View File
@@ -12,7 +12,8 @@ import useFiles from "./useFiles";
import UploadModal from "./UploadModal";
import Replying from "./Replying";
import Toolbar from "./Toolbar";
import MarkdownEditor from "../MarkdownEditer";
import EmojiPicker from "./EmojiPicker";
import RichInput from "../RichInput";
const Types = {
channel: "#",
user: "@",
@@ -27,9 +28,9 @@ export default function Send({
const [contentType, setContentType] = useState("text");
const [replyMessage] = useReplyMessageMutation();
const { files, setFiles, resetFiles } = useFiles([]);
const inputRef = useRef();
const [shift, setShift] = useState(false);
const [enter, setEnter] = useState(false);
// const [shift, setShift] = useState(false);
// const [enter, setEnter] = useState(false);
const [editor, setEditor] = useState(null);
const [markdown, setMarkdown] = useState("");
const [msg, setMsg] = useState("");
const dispatch = useDispatch();
@@ -53,44 +54,26 @@ export default function Send({
] = useSendChannelMsgMutation();
const sendMessage = type == "channel" ? sendChannelMsg : sendMsg;
const sendingMessage = userSending || channelSending;
useKey(
"Shift",
(e) => {
console.log("shift", e.type);
setShift(e.type == "keydown");
},
{ eventTypes: ["keydown", "keyup"], target: inputRef }
);
const handleMsgChange = (evt) => {
if (enter && !shift) {
handleSendMessage();
} else {
setMsg(evt.target.value);
}
const insertEmoji = (emoji) => {
console.log("insert emoji", emoji, editor);
editor.insertText(emoji);
};
const handleInputKeydown = (e) => {
console.log("keydown event", e);
setEnter(e.key === "Enter");
};
const selectEmoji = (emoji) => {
setMsg((prev) => `${prev}${emoji}`);
};
useEffect(() => {
if (inputRef) {
inputRef.current.focus();
}
}, [msg, replying_mid]);
const handleUpload = (evt) => {
setFiles([...evt.target.files]);
};
const handleSendMessage = () => {
if (!msg || !id || sendingMessage) return;
console.log("send message", msg);
const content_type = msg ? "text" : "markdown";
const content = msg ? msg : markdown;
console.log("current message", markdown, msg);
if (replying_mid) {
console.log("replying", replying_mid);
replyMessage({
id,
reply_mid: replying_mid,
content: msg,
type: content_type,
content,
context: type,
from_uid,
});
@@ -98,58 +81,30 @@ export default function Send({
} else {
sendMessage({
id,
content: msg,
type: content_type,
content,
from_uid,
properties: { local_id: new Date().getTime() },
});
}
setMsg("");
};
const sendMarkdown = () => {
console.log("markdown", markdown, markdown.endsWith("\\"));
sendMessage({
id,
content: markdown,
from_uid,
properties: { local_id: new Date().getTime() },
type: "markdown",
});
setMarkdown("");
};
return (
<>
<StyledSend className={`send ${replying_mid ? "reply" : ""} ${type}`}>
{replying_mid && <Replying mid={replying_mid} id={id} />}
<div className="input">
{contentType == "markdown" ? (
<MarkdownEditor value={markdown} updateValue={setMarkdown} />
) : (
<TextareaAutosize
autoFocus
onFocus={(e) =>
e.currentTarget.setSelectionRange(
e.currentTarget.value.length,
e.currentTarget.value.length
)
}
ref={inputRef}
className="content"
maxRows={8}
minRows={1}
onKeyDown={handleInputKeydown}
onChange={handleMsgChange}
value={msg}
placeholder={`Send to ${Types[type]}${name}`}
/>
)}
</div>
<EmojiPicker selectEmoji={insertEmoji} />
<RichInput
setEditor={setEditor}
placeholder={`Send to ${Types[type]}${name} `}
sendMessage={handleSendMessage}
updateMarkdown={setMarkdown}
updatePureText={setMsg}
/>
<Toolbar
handleSend={sendMarkdown}
contentType={contentType}
updateContentType={setContentType}
selectEmoji={selectEmoji}
handleUpload={handleUpload}
/>
</StyledSend>
+8 -40
View File
@@ -1,56 +1,24 @@
import styled from "styled-components";
const StyledSend = styled.div`
position: absolute;
bottom: -70px;
left: 50%;
transform: translateX(-50%);
position: relative;
background: #e5e7eb;
border-radius: 8px;
width: calc(100% - 32px);
min-height: 54px;
border-radius: var(--br);
width: 100%;
/* min-height: 54px; */
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 5px;
padding: 4px 18px;
justify-content: space-between;
gap: 15px;
padding: 14px 18px;
/* margin: 0 16px; */
&.user {
bottom: 10px;
}
&.reply {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.input {
width: 100%;
position: relative;
.content {
resize: unset;
outline: none;
padding: 4px;
font-weight: 500;
font-size: 14px;
line-height: 20px;
color: #616161;
width: 100%;
border: none;
background: none;
}
.btn {
cursor: pointer;
border: none;
border-radius: 4px;
padding: 2px 6px;
background: green;
color: #fff;
font-size: 16px;
font-weight: bold;
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
}
/* padding: 4px 0; */
}
`;
@@ -56,7 +56,7 @@ export default function ProfileBasicEditModal({
buttons={
<>
<Button onClick={closeModal}>Cancel</Button>
<Button onClick={handleUpdate} className="main">
<Button onClick={handleUpdate}>
{isLoading ? "Updating" : `Done`}
</Button>
</>
@@ -63,7 +63,7 @@ export default function ConfigFirebase() {
<div className="input">
<Label htmlFor="desc">Private Key</Label>
<Textarea
rows={15}
rows={10}
disabled={!enabled}
data-type="private_key"
onChange={handleChange}
@@ -61,6 +61,9 @@ const StyledWrapper = styled.div`
> .right {
background-color: #fff;
width: 100%;
max-height: 100%;
/* max-height: -webkit-fill-available; */
overflow: auto;
padding: 32px;
> .title {
font-weight: bold;
+23 -10
View File
@@ -1,21 +1,34 @@
import styled from "styled-components";
const StyledButton = styled.button`
cursor: pointer;
padding: 8px 16px;
background: none;
border: 1px solid #e5e7eb;
box-shadow: 0px 1px 2px rgba(31, 41, 55, 0.08);
padding: 10px 18px;
border: none;
box-sizing: border-box;
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
border-radius: var(--br, 4px);
font-weight: 500;
color: #374151;
&.main {
border: none;
background: #1fe1f9;
color: #fff;
font-size: 16px;
line-height: 24px;
color: #fff;
background-color: #22ccee;
&:hover,
&:active {
background-color: #06aed4;
}
&:focus {
background-color: #22ccee;
}
&:disabled {
background-color: #a5f0fc;
}
&.small {
padding: 8px 14px;
font-size: 14px;
line-height: 20px;
}
&.danger {
border: none;
background: #ef4444;
background-color: #ef4444;
color: #fff;
}
&.ghost {