diff --git a/package.json b/package.json index 54f88216..c3236beb 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "@reduxjs/toolkit": "^1.7.2", "@rtk-query/codegen-openapi": "^1.0.0-alpha.1", "@svgr/webpack": "^6.2.1", + "@tippyjs/react": "^4.2.6", "animate.css": "^4.1.1", "axios": "^0.25.0", "babel-loader": "^8.2.3", diff --git a/src/common/component/Avatar.js b/src/common/component/Avatar.js index 2ade849d..3de37c73 100644 --- a/src/common/component/Avatar.js +++ b/src/common/component/Avatar.js @@ -1,6 +1,5 @@ import { useState, useEffect } from "react"; - -export default function Avatar({ url, id = 0, ...rest }) { +export default function Avatar({ url = "", id = 0, ...rest }) { const [src, setSrc] = useState(url); const [loaded, setLoaded] = useState(false); @@ -22,7 +21,7 @@ export default function Avatar({ url, id = 0, ...rest }) { if (!loaded) { handleError(); } - }, 500); + }, 2000); return () => { clearTimeout(inter); diff --git a/src/common/component/Contact.js b/src/common/component/Contact.js index 907727de..ae74d79e 100644 --- a/src/common/component/Contact.js +++ b/src/common/component/Contact.js @@ -1,6 +1,8 @@ // import React from 'react'; import styled from "styled-components"; import Avatar from "./Avatar"; +import Tippy from "@tippyjs/react"; +import Profile from "./Profile"; import { useGetContactsQuery } from "../../app/services/contact"; const StyledWrapper = styled.div` @@ -18,6 +20,7 @@ const StyledWrapper = styled.div` } } .avatar { + cursor: pointer; width: 32px; height: 32px; position: relative; @@ -50,16 +53,30 @@ const StyledWrapper = styled.div` color: #52525b; } `; -export default function Contact({ interactive = true, status = "", uid = "" }) { +export default function Contact({ + interactive = true, + status = "", + uid = "", + popover = false, +}) { const { data: contacts } = useGetContactsQuery(); if (!contacts) return null; const currUser = contacts.find((c) => c.uid == uid); return ( -
- -
-
+ } + > +
+ +
+
+
{currUser?.name}
); diff --git a/src/common/component/Message/Commands.js b/src/common/component/Message/Commands.js new file mode 100644 index 00000000..15a82545 --- /dev/null +++ b/src/common/component/Message/Commands.js @@ -0,0 +1,53 @@ +// import React from 'react' +import styled from "styled-components"; +import toast from "react-hot-toast"; + +const StyledCmds = styled.ul` + position: absolute; + right: 10px; + top: 0; + transform: translateY(-50%); + display: flex; + align-items: center; + border: 1px solid rgba(0, 0, 0, 0.08); + border-radius: 6px; + background-color: #fff; + visibility: hidden; + .cmd { + cursor: pointer; + padding: 4px; + img { + width: 24px; + height: 24px; + } + } +`; +// https://static.nicegoodthings.com/project/rustchat/icon.forward.svg +// https://static.nicegoodthings.com/project/rustchat/icon.edit.svg +export default function Commands() { + const handleClick = () => { + toast.success("cooming soon"); + }; + return ( + +
  • + icon emoji +
  • +
  • + icon emoji +
  • +
  • + icon emoji +
  • +
    + ); +} diff --git a/src/common/component/Message.js b/src/common/component/Message/index.js similarity index 53% rename from src/common/component/Message.js rename to src/common/component/Message/index.js index 596565c8..cd9aee8a 100644 --- a/src/common/component/Message.js +++ b/src/common/component/Message/index.js @@ -1,63 +1,16 @@ import { useEffect, useRef } from "react"; -import styled from "styled-components"; import dayjs from "dayjs"; import { useDispatch } from "react-redux"; import { useInViewRef } from "rooks"; -import Avatar from "./Avatar"; -import BASE_URL from "../../app/config"; -import { useGetContactsQuery } from "../../app/services/contact"; -import { setChannelMsgRead } from "../../app/slices/message.channel"; -import { setUserMsgRead } from "../../app/slices/message.user"; -const StyledMsg = styled.div` - display: flex; - align-items: flex-start; - gap: 16px; - padding: 12px 0; - .avatar { - img { - width: 40px; - height: 40px; - border-radius: 50%; - } - } - .details { - display: flex; - flex-direction: column; - gap: 8px; - .up { - display: flex; - align-items: center; - gap: 8px; - font-weight: 500; - .name { - color: #06b6d4; - font-style: normal; - font-size: 14px; - line-height: 20px; - } - .time { - color: #bfbfbf; - font-size: 12px; - line-height: 18px; - } - } - .down { - user-select: text; - color: #374151; - font-weight: normal; - font-size: 14px; - line-height: 20px; - word-break: break-all; - white-space: break-spaces; - &.pending { - opacity: 0.5; - } - .img { - max-width: 400px; - } - } - } -`; +import Tippy from "@tippyjs/react"; +import Profile from "../Profile"; +import Avatar from "../Avatar"; +import BASE_URL from "../../../app/config"; +import { useGetContactsQuery } from "../../../app/services/contact"; +import { setChannelMsgRead } from "../../../app/slices/message.channel"; +import { setUserMsgRead } from "../../../app/slices/message.user"; +import StyledWrapper from "./styled"; +import Commands from "./Commands"; const renderContent = (type, content) => { let ctn = null; switch (type) { @@ -111,10 +64,17 @@ export default function Message({ if (!contacts) return null; const currUser = contacts.find((c) => c.uid == fromUid) || {}; return ( - -
    - -
    + + } + > +
    + +
    +
    {currUser.name} @@ -124,6 +84,7 @@ export default function Message({ {renderContent(content_type, content)}
    -
    + + ); } diff --git a/src/common/component/Message/styled.js b/src/common/component/Message/styled.js new file mode 100644 index 00000000..dc665312 --- /dev/null +++ b/src/common/component/Message/styled.js @@ -0,0 +1,63 @@ +import styled from "styled-components"; +const StyledMsg = styled.div` + position: relative; + display: flex; + align-items: flex-start; + gap: 16px; + padding: 4px; + margin: 8px 0; + border-radius: 8px; + &:hover { + background: #f5f6f7; + .cmds { + visibility: visible; + } + } + .avatar { + cursor: pointer; + img { + width: 40px; + height: 40px; + border-radius: 50%; + } + } + .details { + display: flex; + flex-direction: column; + gap: 8px; + .up { + display: flex; + align-items: center; + gap: 8px; + font-weight: 500; + .name { + color: #06b6d4; + font-style: normal; + font-size: 14px; + line-height: 20px; + } + .time { + color: #bfbfbf; + font-size: 12px; + line-height: 18px; + } + } + .down { + user-select: text; + color: #374151; + font-weight: normal; + font-size: 14px; + line-height: 20px; + word-break: break-all; + white-space: break-spaces; + &.pending { + opacity: 0.5; + } + .img { + max-width: 400px; + } + } + } +`; + +export default StyledMsg; diff --git a/src/common/component/Profile.js b/src/common/component/Profile.js new file mode 100644 index 00000000..eb31c5ef --- /dev/null +++ b/src/common/component/Profile.js @@ -0,0 +1,93 @@ +// import React from "react"; +// import toast from "react-hot-toast"; +// import { useState, useEffect } from "react"; + +import { NavLink } from "react-router-dom"; +import { BsChatText } from "react-icons/bs"; +import { RiUserAddLine } from "react-icons/ri"; +import { IoShareOutline } from "react-icons/io5"; +import styled from "styled-components"; + +import Avatar from "../../common/component/Avatar"; + +const StyledWrapper = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + margin-top: 80px; + width: 432px; + gap: 4px; + + .avatar { + width: 80px; + height: 80px; + border-radius: 50%; + } + .name { + font-weight: bold; + font-size: 18px; + line-height: 100%; + color: #1c1c1e; + } + .email { + font-weight: normal; + font-size: 14px; + line-height: 20px; + color: #78787c; + } + .icons { + display: flex; + align-items: center; + padding: 26px 0; + gap: 28px; + .icon { + } + } + .line { + width: 100%; + height: 1px; + border: none; + background-color: rgba(0, 0, 0, 0.1); + } + &.card { + padding: 16px; + width: 332px; + background: #ffffff; + box-shadow: 0px 20px 25px 20px rgba(31, 41, 55, 0.1), + 0px 10px 10px rgba(31, 41, 55, 0.04); + border-radius: 6px; + .icons { + padding-bottom: 2px; + } + } +`; +export default function Profile({ data = null, type = "embed" }) { + if (!data) return null; + const { uid, name, email, avatar } = data; + return ( + + +

    {name}

    + {email} +
      +
    • + + + +
    • +
    • + {/* */} + + {/* */} +
    • +
    • + {/* */} + + {/* */} +
    • +
    + {type == "embed" &&
    } +
    + ); +} diff --git a/src/routes/chat/ChannelChat/index.js b/src/routes/chat/ChannelChat/index.js index bf5fa367..4916f035 100644 --- a/src/routes/chat/ChannelChat/index.js +++ b/src/routes/chat/ChannelChat/index.js @@ -91,7 +91,7 @@ export default function ChannelChat({ contacts={ {filteredUsers.map(({ name, status, uid }) => { - return ; + return ; })} } diff --git a/src/routes/chat/DMChat/styled.js b/src/routes/chat/DMChat/styled.js index eb552b30..a74139b2 100644 --- a/src/routes/chat/DMChat/styled.js +++ b/src/routes/chat/DMChat/styled.js @@ -51,6 +51,7 @@ export const StyledDMChat = styled.article` display: flex; flex-direction: column; padding: 0 16px; + padding-top: 10px; height: calc(100vh - 56px - 80px); overflow-y: scroll; overflow-x: visible; diff --git a/src/routes/chat/styled.js b/src/routes/chat/styled.js index 56cdfb99..448af505 100644 --- a/src/routes/chat/styled.js +++ b/src/routes/chat/styled.js @@ -104,13 +104,18 @@ const StyledWrapper = styled.div` .up { display: flex; justify-content: space-between; + align-items: center; .name { font-weight: 600; font-size: 14px; line-height: 20px; color: #52525b; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } time { + white-space: nowrap; font-weight: 500; font-size: 12px; line-height: 18px; diff --git a/src/routes/contacts/index.js b/src/routes/contacts/index.js index 7dade830..4b03240b 100644 --- a/src/routes/contacts/index.js +++ b/src/routes/contacts/index.js @@ -4,7 +4,7 @@ import { useGetContactsQuery } from "../../app/services/contact"; import Search from "../../common/component/Search"; import Contact from "../../common/component/Contact"; import CurrentUser from "../../common/component/CurrentUser"; -import Profile from "./Profile"; +import Profile from "../../common/component/Profile"; import StyledWrapper from "./styled"; @@ -33,7 +33,7 @@ export default function ContactsPage() { {user_id && (
    - + c.uid == user_id)} />
    )} diff --git a/yarn.lock b/yarn.lock index 7106953a..385c779a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1444,6 +1444,11 @@ schema-utils "^3.0.0" source-map "^0.7.3" +"@popperjs/core@^2.9.0": + version "2.11.2" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.2.tgz#830beaec4b4091a9e9398ac50f865ddea52186b9" + integrity sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA== + "@react-dnd/asap@4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@react-dnd/asap/-/asap-4.0.0.tgz#b300eeed83e9801f51bd66b0337c9a6f04548651" @@ -1633,6 +1638,13 @@ "@svgr/plugin-jsx" "^6.2.1" "@svgr/plugin-svgo" "^6.2.0" +"@tippyjs/react@^4.2.6": + version "4.2.6" + resolved "https://registry.yarnpkg.com/@tippyjs/react/-/react-4.2.6.tgz#971677a599bf663f20bb1c60a62b9555b749cc71" + integrity sha512-91RicDR+H7oDSyPycI13q3b7o4O60wa2oRbjlz2fyRLmHImc4vyDwuUP8NtZaN0VARJY5hybvDYrFzhY9+Lbyw== + dependencies: + tippy.js "^6.3.1" + "@trysound/sax@0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" @@ -7387,6 +7399,13 @@ timsort@^0.3.0: resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= +tippy.js@^6.3.1: + version "6.3.7" + resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.3.7.tgz#8ccfb651d642010ed9a32ff29b0e9e19c5b8c61c" + integrity sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ== + dependencies: + "@popperjs/core" "^2.9.0" + to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"