From 506fc8e59ae0a00521b4d9c3e1d2f17427697aaa Mon Sep 17 00:00:00 2001 From: zerosoul Date: Wed, 13 Apr 2022 21:48:37 +0800 Subject: [PATCH] feat: forward message UX --- src/common/component/Contact.js | 6 +- src/common/component/ForwardModal/index.js | 112 ++++++++++++++++++++ src/common/component/ForwardModal/styled.js | 106 ++++++++++++++++++ src/common/component/Message/Commands.js | 19 +++- src/common/component/Message/Mention.js | 9 +- src/common/component/Message/Reply.js | 30 +++++- src/common/component/styled/Button.js | 5 + src/common/component/styled/Input.js | 6 +- 8 files changed, 281 insertions(+), 12 deletions(-) create mode 100644 src/common/component/ForwardModal/index.js create mode 100644 src/common/component/ForwardModal/styled.js diff --git a/src/common/component/Contact.js b/src/common/component/Contact.js index 79b11413..5c245014 100644 --- a/src/common/component/Contact.js +++ b/src/common/component/Contact.js @@ -25,8 +25,8 @@ const StyledWrapper = styled.div` } .avatar { cursor: pointer; - width: 32px; - height: 32px; + width: ${({ size }) => `${size}px`}; + height: ${({ size }) => `${size}px`}; position: relative; img { border-radius: 50%; @@ -66,6 +66,7 @@ export default function Contact({ uid = "", popover = false, compact = false, + avatarSize = 32, }) { const navigate = useNavigate(); const curr = useSelector((store) => store.contacts.byId[uid]); @@ -75,6 +76,7 @@ export default function Contact({ if (!curr) return null; return ( { + // return { conactsData: store.contacts.byId, loginUid: store.authData.uid }; + // }); + const handleInputChange = (evt) => { + updateInput(evt.target.value); + }; + const toggleCheckMember = ({ currentTarget }) => { + const { uid } = currentTarget.dataset; + let tmp = members.includes(+uid) + ? members.filter((m) => m != uid) + : [...members, +uid]; + console.log(uid, currentTarget); + setMembers(tmp); + }; + const removeSelected = (uid) => { + setMembers(members.filter((m) => m != uid)); + }; + const selectedCount = members.length ? `(${members.length})` : null; + return ( + + +
+
+ +
+ {contacts && ( +
    + {contacts.map((u) => { + const { uid } = u; + const checked = members.includes(uid); + console.log({ checked }); + return ( +
  • + + +
  • + ); + })} +
+ )} +
+
+

Send To {selectedCount}

+
    + {members.map((uid) => { + return ( +
  • + + +
  • + ); + })} +
+
+ +
+ +
+ + +
+
+
+
+ ); +} diff --git a/src/common/component/ForwardModal/styled.js b/src/common/component/ForwardModal/styled.js new file mode 100644 index 00000000..2e48e32a --- /dev/null +++ b/src/common/component/ForwardModal/styled.js @@ -0,0 +1,106 @@ +import styled from "styled-components"; + +const StyledWrapper = styled.div` + display: flex; + /* max-width: 604px; */ + max-height: 514px; + min-height: 400px; + background: #fff; + box-shadow: 0px 25px 50px rgba(31, 41, 55, 0.25); + border-radius: var(--br); + transition: all 0.5s ease; + overflow: hidden; + .left { + width: 260px; + /* height: 100%; */ + box-shadow: inset -1px 0px 0px rgba(0, 0, 0, 0.1); + overflow-y: scroll; + .search { + position: sticky; + top: 0; + z-index: 99; + background: #fff; + box-shadow: 0px 1px 0px rgba(0, 0, 0, 0.1); + padding: 16px; + width: calc(100% - 1px); + input { + outline: none; + width: -webkit-fill-available; + padding: 10px 8px; + font-size: 14px; + line-height: 20px; + background: rgba(0, 0, 0, 0.08); + border-radius: var(--br); + } + } + .users { + display: flex; + flex-direction: column; + /* height: 260px; */ + padding-bottom: 20px; + /* overflow-y: scroll; */ + .user { + cursor: pointer; + display: flex; + align-items: center; + padding: 0 16px; + /* margin: 0 4px; */ + width: -webkit-fill-available; + border-radius: 4px; + &:hover { + background: rgba(116, 127, 141, 0.1); + } + > div { + width: 100%; + } + } + } + } + .right { + display: flex; + flex-direction: column; + align-items: flex-start; + /* height: 100%; */ + /* justify-content: space-between; */ + padding: 16px 32px 32px 32px; + box-sizing: border-box; + .title { + font-weight: 600; + font-size: 14px; + line-height: 20px; + color: #344054; + margin-bottom: 16px; + } + .selected { + width: 100%; + height: 260px; + padding: 10px 0; + overflow: scroll; + .item { + position: relative; + .remove { + cursor: pointer; + position: absolute; + right: 5px; + top: 50%; + transform: translateY(-50%); + } + } + } + .reply { + width: 280px; + margin-bottom: 4px; + } + .input { + margin-bottom: 32px; + } + .btns { + width: 100%; + display: flex; + align-items: center; + justify-content: flex-end; + gap: 16px; + } + } +`; +export default StyledWrapper; diff --git a/src/common/component/Message/Commands.js b/src/common/component/Message/Commands.js index 5ba3132f..eccddd99 100644 --- a/src/common/component/Message/Commands.js +++ b/src/common/component/Message/Commands.js @@ -12,7 +12,9 @@ import EmojiPicker from "./EmojiPicker"; import replyIcon from "../../../assets/icons/reply.svg?url"; import reactIcon from "../../../assets/icons/reaction.svg?url"; import editIcon from "../../../assets/icons/edit.svg?url"; +import bookmarkIcon from "../../../assets/icons/bookmark.svg?url"; import moreIcon from "../../../assets/icons/more.svg?url"; +import ForwardModal from "../ForwardModal"; const StyledCmds = styled.ul` z-index: 9999; position: absolute; @@ -60,6 +62,7 @@ export default function Commands({ }) { const dispatch = useDispatch(); const [deleteModalVisible, setDeleteModalVisible] = useState(false); + const [forwardModalVisible, setForwardModalVisible] = useState(false); const [tippyVisible, setTippyVisible] = useState(false); const currUid = useSelector((store) => store.authData.uid); const cmdsRef = useRef(null); @@ -71,7 +74,10 @@ export default function Commands({ hideAll(); } }; - + const toggleForwardModal = () => { + hideAll(); + setForwardModalVisible((prev) => !prev); + }; const toggleDeleteModal = () => { hideAll(); setDeleteModalVisible((prev) => !prev); @@ -111,6 +117,11 @@ export default function Commands({ )} +
  • + + icon bookmark + +
  • {/*
  • Edit Message
  • */}
  • Pin Message
  • +
  • + Forward +
  • Reply
  • @@ -142,6 +156,9 @@ export default function Commands({ {deleteModalVisible && ( )} + {forwardModalVisible && ( + + )} ); } diff --git a/src/common/component/Message/Mention.js b/src/common/component/Message/Mention.js index d1ecf9f9..56788d7e 100644 --- a/src/common/component/Message/Mention.js +++ b/src/common/component/Message/Mention.js @@ -4,23 +4,26 @@ import Tippy from "@tippyjs/react"; import Profile from "../Profile"; import styled from "styled-components"; const Styled = styled.span` - cursor: pointer; padding: 0 2px; color: #1fe1f9; + &.clickable { + cursor: pointer; + } `; -export default function Mention({ uid }) { +export default function Mention({ uid, popover = true }) { const contactsData = useSelector((store) => store.contacts.byId); const user = contactsData[uid]; if (!user) return null; return ( } > - {`@${user.name}`} + {`@${user.name}`} ); } diff --git a/src/common/component/Message/Reply.js b/src/common/component/Message/Reply.js index ecea38f4..ac49453f 100644 --- a/src/common/component/Message/Reply.js +++ b/src/common/component/Message/Reply.js @@ -1,13 +1,14 @@ // import React 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"; import { ContentTypes } from "../../../app/config"; import { getFileIcon, isImage } from "../../utils"; + import Avatar from "../Avatar"; const Styled = styled.div` - cursor: pointer; display: flex; align-items: flex-start; padding: 8px; @@ -15,6 +16,9 @@ const Styled = styled.div` border-radius: var(--br); gap: 8px; margin-bottom: 4px; + &.clickable { + cursor: pointer; + } .user { display: flex; align-items: center; @@ -89,7 +93,19 @@ const renderContent = (data) => { let res = null; switch (content_type) { case ContentTypes.text: - res = {content}; + res = ( + + {reactStringReplace( + content, + /(\s{1}\@[0-9]+\s{1})/g, + (match, idx) => { + console.log("match", match); + const uid = match.trim().slice(1); + return ; + } + )} + + ); break; case ContentTypes.markdown: res = ( @@ -120,7 +136,7 @@ const renderContent = (data) => { } return res; }; -export default function Reply({ mid }) { +export default function Reply({ mid, interactive = true }) { const { data, users } = useSelector((store) => { return { data: store.message[mid], users: store.contacts.byId }; }); @@ -140,7 +156,11 @@ export default function Reply({ mid }) { const currUser = users[data.from_uid]; if (!currUser) return null; return ( - +
    {currUser.name} diff --git a/src/common/component/styled/Button.js b/src/common/component/styled/Button.js index 744b3813..be956ca8 100644 --- a/src/common/component/styled/Button.js +++ b/src/common/component/styled/Button.js @@ -36,6 +36,11 @@ const StyledButton = styled.button` background: none; color: #1fe1f9; } + &.cancel { + border: 1px solid #e5e7eb; + background: none; + color: #374151; + } `; export default StyledButton; diff --git a/src/common/component/styled/Input.js b/src/common/component/styled/Input.js index f79c1d96..10596031 100644 --- a/src/common/component/styled/Input.js +++ b/src/common/component/styled/Input.js @@ -17,12 +17,16 @@ const StyledInput = styled.input` background: #ffffff; border: 1px solid #e5e7eb; box-shadow: 0px 1px 2px rgba(31, 41, 55, 0.08); - border-radius: 4px; + border-radius: var(--br); font-weight: normal; font-size: 14px; line-height: 20px; color: #333; padding: 8px; + outline: none; + &.higher { + padding: 12px 8px; + } &.large { font-weight: 400; font-size: 16px;